Amiga C For Advanced Programmers
Amiga C For Advanced Programmers
Amiga C For Advanced Programmers
PO
(jfT
P()
N 0
66 11V
CK PEN
61 MME
Amiga C
for
Advanced
Programmers
Bleek
Jennrich
Schulz
EIDWINDOUJ e SI,"1MEZERflZERO BLOCI(PEN RRSPOR
8T TOPED6E., FOLLOUlMOUSE. 6RD61MMEDIRTE 1'1
DSETI(E'YMRP() L
DRRUlMDDE
IE OFFGRDGT
11'16 COSET
EUlWINDOUJ 61 .
CI(PEN RRSPOR
8T. TOPED6
IMMEDIRTE N.
DSETI(EVMRP(
EN DRRUJMDDE 4
'E OFF680
'1RL
DRR661N6 CDSETIC
EUlUlINDOUJ
OZERO BLOCI(PEN RRSPOR
~T. TOPED6E
USE
61MMEDIRTE 1'1
DSETKE'IMRP()
1'1 DRRUJMODE 4
lE OFF6RDGET
R661N6 CDSETIC
fUJUJINDOUJ GI
OCI(PEN RRSPOR
FIT TOPED6E
D61MMEDIRTE 1'1,
DSETI(EVMRP() L
TPEN DRRUJMODE
IE OFF6RDGET DISPLR'IRL RT. DRRG6'
Abacus
5370 52nd Street, SE
Grand Rapids, MI 49512
Data Becker GmbH
Merowingerstrasse 30
4000 Duesseldorf, West Germany
This book is copyrighted. No part of this book may be reproduced, stored in a retrieval
system, or transmitted in any form or by any means, electronic, mechanical, photocopying,
recording or otherwise. without the prior written permission of Abacus or Data Becker
GmbH.
Every effort has been made to ensure complete and accurate information concerning the
material presented in this book. However, Abacus can neither guarantee nor be held legally
responsible for any mistakes in printing or faulty instructions contained in this book. The
authors always appreciate receiving notice of any errors or misprints.
Amiga 500, Amiga 1000, Amiga 2000 and Amiga are trademarks or registered trademarks
of Commodore-Amiga. Inc. AC/BASIC Compiler and AC/BASIC are trademarks or
registered trademarks of Absoft Corporation. Cray is a trademark or registered trademark of
Cray Incorporated. AmigaBASIC is a trademark or registered trademark of Microsoft
Corporation.
:r::SBN
ii
1-55755-046-8
Table of Contents
Preface
1.
1.1
1.1.1
1.1.2
1.1.3
1.1.4
1.2
1.2.1
1.2.2
1.2.3
2.
2.1
2.1.1
2.1.2
2.1.3
2.1.4
2.2
2.3
2.3.1
2.3.2
2.4
2.5
2.5.1
2.5.2
2.5.3
3.
3.1
3.1.1
3.1.2
3.1.2.1
3.1.2.2
3.1.2.3
3.1.2.4
3.1.3
3.1.3.1
3.1.3.2
3.1.3.3
3.2
3.2.1
3.2.1.1
3.2.1.2
3.2.1.3
The C language
4
4
8
9
13
14
14
20
25
C Compiler Operations
35
Intuition and C
Windows
Window parameters and selecting them
How Intuition manages windows
Accessing the Intuition library
The NewWindow structure
The Window structure
A summary of window functions
Example window application programs
All-purpose windows
Program routines for text editing
Window for a new CLI
Screen Fundamentals
Creating custom screens
The NewScreen structure
The first screen listing
The Screen structure
39
40
49
54
59
63
67
70
70
74
77
77
78
79
83
84
84
89
89
90
101
106
110
111
117
120
123
123
124
127
130
iii
3.2.1.4
3.2.2
3.2.2.1
3.2.2.2
3.3
3.3.1
3.3.1.1
3.3.1.2
3.3.1.3
3.3.1.4
3.3.2
3.3.2.1
3.3.2.2
3.3.2.3
3.3.2.4
3.3.3
3.3.3.1
3.3.3.2
3.3.3.3
3.3.3.4
3.3.4
3.3.4.1
3.3.4.2
3.3.4.3
3.4
3.4.1
3.4.1.1
3.4.1.2
3.4.1.3
3.4.2
3.4.2.1
3.4.2.2
3.5
3.5.1
3.5.2
3.5.3
3.5.3.1
3.5.3.2
3.5.3.3
3.5.4
3.6
3.6.1
3.6.2
3.6.3
3.6.4
3.7
3.7.1
3.7.1.1
3.7.1.2
3.7.1.3
3.7.2
iv
134
138
138
141
143
143
144
144
145
147
152
152
152
153
154
158
158
159
160
160
165
166
170
175
177
177
178
189
195
203
204
227
229
229
233
234
235
236
239
242
251
252
252
255
258
262
263
267
268
269
271
3.7.2.1
3.7.2.2
3.7.2.3
3.7.2.4
3.7.2.5
3.7.2.6
3.7.2.7
3.8
3.8.1
3.8.2
3.8.2.1
3.8.2.2
3.8.2.3
3.8.2.4
3.8.2.5
3.8.2.6
3.8.3
3.8.4
3.8.4.4
3.9
3.9.1
3.9.2
3.9.3
3.9.4
3.9.5
3.9.6
3.10
3.10.1
3.10.2
3.10.3
3.11
3.11.1
3.11.2
3.11.3
3.11.4
4.
4.1
4.2
4.3
4.3.1
4.3.2
4.3.3
4.3.4
4.3.5
4.3.6
4.3.7
4.4.8
4.3.9
4.4
4.4.1
Gadgets
Window messages
Requesters and execution
Menu flag analysis
Mouse reading
Recognizing keyboard input
The disk drive
Menus
General construction of a menu
Making a menu
Filling the menu strip
The Menultem structure
Adding extras to menu design
Submenus
Style menu tutorial
Graphic menus
Reading the menu strip
Working with source code utilities
Fine-tuning the source text
The Console Device
Designing Communication Direction
Receiving the First Data
Displaying the Characters
Console Device Control Sequences
Reading Messages from the Console
Finishing the CLI Editor
User-Defined Keyboard Tables
Keyboard Table Design
Working with the Keyboard Tables
Creating Your Own Keyboard Table
Memory Management
Memory Organization of the Amiga
First Steps with AlIocMemO
Improvement using AllocEntryO
The Final Solution
271
381
272
273
274
276
278
281
283
283
284
287
290
298
302
305
305
321
325
335
337
337
339
340
341
343
349
361
361
363
367
369
369
370
372
375
381
389
398
398
406
408
419
422
427
445
474
512
558
558
v
4.4.2
Index
558
561
561
562
564
568
570
571
574
586
606
624
636
644
645
Preface
Now that the initial excitement over the Amiga has died down, people
are discovering that Amiga can also be used for "real" applications as
well as fantastic graphics and sound. Users want more and more
literature about programming the Amiga for these serious applications.
This book shows you how to make use of the many Amiga operating
system functions in your own programs.
This book is not a guide on learning C: It's a book on advanced C programming. If you need a guide for the beginner, please read Abacus'
Amiga C for Beginners before continuing with this book.
Since this book shows you how to program using the C language,
we'll begin with author B. Jennrich demonstrating the actual operation
of a C compiler. Then we'll get to the actual "instructional" part of the
book. You'll learn what makes type casting and other aspects of the
language necessary. We hope this saves you time and aggravation in
developing larger projects. Maybe you tried C before and spent more
time creating errors and system crashes than working programs. This
book will hopefully give you a better understanding of the language and
what you can accomplish in this language.
The next segment of the book describes the subject of Intuition in
detail. W. Bleek, a specialist in this area, leads you into the depths of
the Intuition user interface. Mter reading this section you'll be able to
easily add screens, windows, gadgets, menus and more to your C
programs.
The third section shows you how to develop an Intuition-based text
editor. Peter Schulz, known for his AssemPro assembler software and
the book Amiga 3D Graphic Programming applies your newlyacquired knowledge of Intuition to a practical project You'll learn how
to wode with devices and other libraries.
All in all, this book is one which the serious C programmer will
always want to have within reach of his Amiga.
Bleek, Jennrich, Schulz
February 1988
vii
1.
The C
language
1.
ABACUS
1.
THE
LANGUAGE
The C language
In 1972 Dennis Ritche began work on improving the B language, an
improved version of the typeless BCPL language (Basic Combined
Programming Language.) The result was the C language.
The C language was developed to ease the implementation of the UNIX
operating system on PDP series computers. Dennis Ritche probably
never dreamed that his language would become so popular among personal computer users.
C's popularity stems from its nature-an extremely efficient high-level
language. This efficiency is especially important in home computing.
Home computers contain limited amounts of memory, may require
large amounts of time for program execution and have low clock
frequencies, therefore a compiler should make a program as small and as
fast as possible.
Code created by a C compiler executes quickly and complies into a
compact form. These two factors are very important to the programmer
who wants to write fast-running programs without the hassle of
learning machine language.
In the early days any C source code could be run on any C compiler.
This was another factor favoring C-portability. As time went on,
however, different operating systems required different compilers (and
different operating system interfaces). This made complete compatibility almost impossible.
The 68000-based Amiga, Atart ST and MAC contain many crucial
system differences. For example, any Atari ST system routines (GEM,
VDI and AES) must be replaced with the equivalent Amiga routines if
you want an ST source code to run on the Amiga. This requires a
detailed knowledge of both operating systems.
This incompatibility problem becomes worse when you transfer a C
source code from an IBM PC or compatible to the Amiga. IBM
systems use segmented addressing, requiring the use of FAR declarations of variables and functions. In addition, MS-DOS access occurs
almost exclusively through interrupts. This means that the C
programmer needs detailed knowledge of both operating systems when
converting MS-DOS programs to the Amiga.
1.
THE
C LANGUAGE
1.1
The compilers The earlier versions of the Lattice compiler were considerably better
than the early versions of Aztec C. The Lattice compiler could handle
arrays which occupied more than 65,535 bytes, while Aztec could noL
In addition, problems arose when trying to compile programs written
for Lattice on the Aztec compiler. On the other hand, Aztec programs
always compiled fine with Lattice. Thus programmers had to stick with
the Lattice compiler if they wanted to be able to use their old programs.
The new Aztec version (Version 3.4a) has no problems with Lattice
compatibility, at least that we could find. We tested a number of programs developed with Lattice and they all compiled successfully with
Aztec (with the help of a compiler option).
Thus we decided to use the new Aztec compiler exclusively in this
book. The programs will require only minor, if any, modifications to
compile with the Lattice C compiler.
1.1.1
ABACUS
bini
include/
lib/
The SYS1:bin! directory contains all the programs necessary for using
the compiler. These include the compiler itself (cc) which creates
assembler source. The assembler (as) which assembles this source and
the linker (In) which links the object code of the assembled source
with the libraries found in the SYS1:lib directory.
The SYS 1 :include directory contains the individual header or include
files which must be included in each C program. These include and
header files access necessary parts of the operating system.
The second disk, "SYS2:" contains assembler include files and
additional libraries which you can link with your programs, as
necessary (see Section 2.3 for more information). This disk also
contains some example programs.
You can start programming in C with only these two disks-the
standard version of the Aztec compiler. But the developer's version of
the compiler, which adds a few utility programs, makes compilation
much easier.
The third disk "SYS3:" contains various utility programs (e.g., make,
and more program examples).
Disk "Library Source:", contains the source code of the assembler
and C calls for the individual operating system routines in compressed
(ARCed) format. These assembler and C files are assembled, compiled
and combined into c.lib and m.lib. which can and/or must be linked
to your programs.
First we'll look at the standard (and most cost-effective) version of the
Aztec compiler. This contains just the disks "SYS1:" and "SYS2:".
1.
THE
C LANGUAGE
AMIGA
Dlsk IsysJ
pi sk
.1
asml
binI
as
cc
In
mclk
set
setdat
(Assembler)
(Compiler)
(Linker)
(Manx Clock)
(Tool for system
variables)
(Date and time)
(CLI ColT'lllands)
(Devices)
(Fonts)
(C Include-Files)
(Hardware-Handler)
cf
devsl
fontsl
includel
11
sys2-
binI
Ib
examples
libl
cl.lib
rnal.lib
c32.lib
bits)
ma32.lib
cl32.lib
ma132.lib
IcrtO.o
(Library hangler)
(examples)
ml.lib (Large data)
sl.lib
m32.lib (int=32
s32.lib
m132.lib(Large data)
s132.libint=32 bits)
libl
c.lib
rna. lib
m.lib
libsl
(Arniga Librarys)
(Ed's Backup)
tl
sl
Figure 1.1
Startup sequence
.dbint
(for Debugger db)
crt scrl
crtO.a68
clipaesr.c
vars.c
wbparse.c
exist.c
rnain.c
Iff
(IFF file-demos
lintl
rnanx.c (function definitions)
systeml
bin/set
INCLUDE=dfO:include
bin/set
CLIB=ram:!dfO:lib
bin/set
CCTEMP=ram:
stack overflow This startup-sequence first increases the stack to 10240 bytes. This is a
preventative measure which should protect you from a "stack overflow"
when developing programs (you will learn another way of preventing
stack overflow later).
linker library
ABACUS
bin/set INCLUDE=df1:include
bin/set CLIB=ram:!df1:lib
bin/set CCTEMP=ram:
bin/setdat
run bin/mclk
1.
THE
LANGUAGE
AMIGA
Now you have enough room on the disk in drive dfO:, disk I, to write
large programs (approx. 30004000 lines).
Another possibility would be to leave the startup sequence intact and
store the programs on the disk in dfl:. This depends on the programmer's preferences. Amiga owners using a hard disk will have to alter
their startup sequence accordingly.
1.1.2
The source file is fIrst passed to the compiler, which automatically calls
the assembler. The assembler then assembles the assembly language
source created by the compiler. The object file created by the assembler
is then linked with c.lib to form an executable program.
This script file assumes that the compiler (co), the assembler (as) and
the linker (In) are contained in the CLI command directory C:
(commands), and can be treated like normal CLI commands. To do this,
copy these fIles from the SYS1:bin directory to SYS1:c. The path
command may also be used to set the search path, path SYS1 :BIN.
The program to be compiled must be in the current directory.
Assuming the name of the script fIle is comp, then a C program can be
compiled with execute oomp cJ>rog. You can interrupt the
compiler and linker by pressing <Ctrl><C>.1f you call the assembler
separately, you can also interrupt script file processing when assembling.
This is because the FAILAT level defaults to 10 after loading the
Workbench. The compiler and linker interrupt the compilation or link
process after <Ctrl><C> with exi t (10). This stops the script file
processing because it determined through the exit () command that a
serious "error" occurred. But since the compiler normally calls the
assembler, the command exi t(l 0) goes to the compiler when <Ctrl>
<C> is pressed, so the script file cannot be interrupted when
assembling.
1.1
ABACUS
THE AZTEC
COMPILER
1.1.3
libl
m8.lib
m832.lib
mx32.lib
m81.lib
mx1.lib
m8132.1ib mx132.1ib
binI
(tools)
mx.lib
make
examples/
db
(C examples)
Figure 1.2
A makefile lets you easily compile large programs consisting of
multiple modules. You do not have to manually compile a modified
module and then link it with the old modules. make handles all this.
You should copy the make utility into the C: subdirectory of the
"SYS1:" disk. Be sure you are working with copies and not the original
disks!
Lees look at the following four modules and create a makefile to
compile them. The makefile must be in the same directory as the
modules to be compiled and it must be named makef ile.:
1.
THE
AMIGA
LANGUAGE
/***********************************/
/* modl.c
*/
/***********************************/
funcl()
{
1***********************************/
1* mod2.c
*/
1***********************************/
func2 ()
{
1***********************************/
1* mod3.c
*/
/***********************************/
func3()
/***********************************/
1* main.c
*/
1***********************************/
main 0
{
The makefile shows how the individual files depend on eaCh other.
For example, the frrst object file depends on the C source file mod1.c.
This is expressed in the make file in the line mod1.o: mod1.c, which
says that the commands in the line after mod1.o: mod1.c are executed
if modl.c is older than mod1.o or if mod1.o does not exist.
In order to determine which file is older than another, the time and date
must be set correctly. The startup sequence calls setdat. Since the
date of the last modification of a file is always stored in the file info
block, make can always determine how old a flle is.
Back to the makefile. The first line of your makefile looks like
this:
10
ABACUS
mod1. 0: modl. c
The next line shows what should be done if modl.o is older than
mod1.e:
cc modI
Now to create the object file for main.e, you only need to call make
main.o. But you can't use the individual object files if they are not
linked. Therefore you must add the following lines to the make file:
"func: modLo mod2.0 mod3.o main.o
In -0 func modl.o mod2.0 mod3.o main.o -IRAM:c n
The func file These two lines indicate that the fune file, which represents the executable program relies on the four object files. If one of these files is
more recent than func, the func file must be relinked. The make
program checks each object file, and if it determines that a modification
has been made to one of them, it creates the new object file from the
modified C source. This keeps the fune program up to date after a
make.
Here is the complete makefile which creates the executable function
from the modules listed before with make fune:
modI. 0 : modl. c
cc modI
mod2.0: mod2.c
cc mod2
mod3.0: mod3.c
cc mod3
main.o: main.c
cc main
func: modl.o mod2.o mod3.0 main.o
In -0 func mod1.o mod2.0 mod3.0 main.o -IRAM:c
11
1.
THE
LANGUAGE
AMIGA
perfonn the make with just make instead of make func. When the
call is simply make, the fIrst command of the makefile executes.
If you want to execute multiple commands after a flle-to-flle relationship, you must ensure that each command is on a separate line and is
indented by at least one character. In addition, you must pay attention to
the correct calling sequence of commands, whereby you can use any
CLI command (e.g., cd, delete, etc.):
func: modl.o mod2.o mod3.o main.o
ln -0 func modl.o mod2.o mod3.o main.o -lRAM:c
delete modl.o
delete mod2.o
You don't have to enter the same string repeatedly. If the string doesn't
fIt on one line, you can "extend" the line with the backslash (\):
OBJECT = modl.o mod2.0 mod3.o mod4.0 modS.o mod6.0\
mod7.o modS.o mod9.0 modlO.o main.o
Now you can start writing your own C programs using the make
utility.
12
ABACUS
1.1.4
There are also routines which perform more complex computations and
manipulations with arguments.
A little tip for those of you who are thinking of purchasing the Aztec
compiler: Consider exactly the extent to which you want to program
the operating system, and whether it is necessary to buy the commercial
version for the function sources. Normally the developer's version will
do everything you want, and the large price differential is something to
seriously consider.
13
1.
THE
1.2
LANGUAGE
1.2.1
Comparing files
As an introduction to Aztec programming, we will use a program
which compares two files with each other and displays the differences as
well as the positions at which they occur. To do this, the program must
be told which fIles to compare. This is done through the command line
of the CLI. The arguments of the command line pass to the program as
the arguments of the main () function:
main (arge, argv)
int
iirge;
ehar
**argv;
{
...
You can access the arguments (strings) with the pointer argv. Let's
take a look at this pointer:
char **argv;
14
ABACUS
i f (argc != 3)
{
Now let's try to open the two files. But since you can't be sure that the
user hasn't made a mistake in specifying the files, or that the files even
exist, you should test to see if the files actually exist:
i f (File1 == OL)
{
You may wonder why the letter L follows the 0 in the condition
Filel == OL. This tells the compiler that it should compare the
pointer Filel with a long value. You could also replace this OL with
(long) O.
There is another way to ensure that constants are treated as "long
variables": the +L compiler option. Here you should note that both
constants and int variables convert to long. When using the +L
option you should link the c32.lib to the program, which ensures
that the argument counter (argc) of main(argc, argv) is actually a
long variable.
15
1.
THE
LANGUAGE
AMIGA
If you access this counter using c.lib and the +L option (e.g., if
(argc == 3)), then four bytes specify the value of this variable instead
of just two. However, this completely invalidates the result because the
two bytes of the previous int variable now become the two high bytes
of the current long variable (+ L option). In addition, the pointers to the
strings shift by two bytes.
But back to the real problem: If the file descriptor returned by f openO
equals zero, then an error occurred in opening the file. The CloseItO
routine closes the open files, sends a message to the user and exits the
program. A tip: Programs which allocate memory or perform other
operations which must be undone should have a routine which automatically releases everything previously allocated
If the files were opened properly, you can now compare them. Now
comes the main loop of the program, which compares the two files
byte for byte for similarities or differences.
The next byte is read from each of the files, and these two bytes are
then compared. If the bytes are identical, then the program reads the
next two bytes. If the bytes are different, the two bytes and the position
in the file at which they occurred are displayed.
It is best to go through the two files inside a while loop, which
terminates when one of the two files has been completely read. The
feofO function determines the end of the file, and the next byte of the
file can be read with fgetc.
filepos = 0;
while (!feof(File1) && !feof(File2
{
By tel = fgetc(File1);
Byte2 = fgetc(File2);
if (By tel != Byte2)
printf ("$%08x %02x <> %02x\n", filepos,Bytel,Byte2);
filepos++;
In this loop, the two file bytes (Bytel and Byte2) are compared with
each other and if they differ, a message like the following appears on
the screen:
$00000154
In addition to the two different bytes, their location in the file also
appears. This necessitates a counter variable (filepos) which contains the number of bytes read, or the read position within the file.
After the comparison, this counter variable increments by one. If both
files still contain data, the comparison continues. But if one of the files
is empty, the loop ends.
16
ABACUS
The file attribute which indicates how many bytes the individual files
contain comes from the following program segments:
if (feof(File1) && !feof(File2
1* file1 is empty *1
1* but File2 is not. *1
fgetc (File2);
filepos++;
else
if (!feof(File1) && feof(File2
1* File2 is empty *1
1* but File1 is not. *1
1);
while (!feof(File1
{
fgetc (File1);
filepos++;
The two if instructions test which file is empty. The name of the
empty file and its length are then displayed. The other file is read to the
end and for each byte read the filepos counter increments. After the
end of the file has been reached, this filename and the length of the file
are displayed:
Comparison terminated I!!
File1 out of data at $00000170
File2 out of data at $00000200
A few words about the filepos counter. In the ftrst while loop, in
which the comparison between the two file bytes is performed,
filepos always increments by one when one of the two mes still has
bytes available. But if it detects the end of a me with feofO, then the
fgetc inside the loop always reads one more byte than is actually
present. This is why when comparing files of different lengths, one
extra byte is "compared" and printed. You must ensure that this "extra
byte" is subtracted from the size of filepos printed.
If both files are the same length, this is determined by the following if
statement.
17
1.
THE C LANGUAGE
else
if (feof(File1) && feof(File2
printf ("\"%s\" and \"%s\" have the size: %08lx\n",
argv[1],argv[2],filepos-1);
Now you have to close the open files and exit the program. The
CloseItO routine performs this task:
CloseIt (Error Code)
int
Error-Code;
{
if (File1 != 0) fclose (File1);
if (File2 != 0) fclose (File2);
exi t (Error_Code);
This routine closes the open files (file descriptor != 0) then terminates
the program with exi
The Error Code which exi passes to
the CLI, and can be used to terminate sCript files.
to.
to
Here is the complete program, which also defines all of the variables
used:
1*******************************************************1
1* 1.2.1.A. compare
*1
1*
COMPARE
*I
1*
(c) Bruno Jennrich
*1
1*
*1
1*
*1
1* This program compares two files with each other.
*1
1*******************************************************1
iinclude "stdio.h"
1* File descriptors *1
FILE *File1,
*File2;
long filepos;
unsigned char Byte1,
Byte2;
1* byte read *1
i f (argc != 3)
{
18
OL)
1* open files *1
1* file open () error *1
1.2
ABACUS
OL)
/* filel is empty */
/* but File2 is not. */
fgetc (File2);
filepos++;
else
if (!feof(Filel) && feof(File2
/* File2 is empty */
fgetc (Filel);
filepos++;
else
if (feof(Filel) && feof(File2
printf ("\"%s\" and \"%s\" have the size: %OBlx\n",
argv[1],argv[2),filepos-l);
CloseIt (0);
Closelt (Error Code)
int
Error=Code;
{
19
1. THE C LANGUAGE
AMIGA
Since this program consists of just one module, it doesn't make much
sense to create a makefile for it as well. A script file works just as
well:
.key file
cc <file>
In +Cb <file> -Lram:c
This script file can (and should) be used for all of the programs in
Chapters I and 2, if the libraries are in the RAM disk.
The programs created by this script file are usually loaded into the
computer's chip memory (+Cb). Even if you have more than SI2K of
memory, the program usually goes into the lower SI2K if that memory
is available.
If you write a program which doesn't fit in the remaining chip memory
(about 320K on the Amiga 1000), you can link your program without
the +Cb option. However, you must ensure that the structures which
lie below the SI2K boundary are stored there (such as memory
assignments for structures with *pointer_to_struct =
AllocMem(sizeof (struct ),MEMF CHIP.
1.2.2
abcdefghl
j
i f (argc != 2)
{
20
t.Z
ABACUS
This should be familiar from the last program. What is new is the addition of a conversion table:
for (i=0;i<32 ;i++)
;i<128;i++)
for (
for (
;i<160;i++)
for (
;i<256;i++)
Conversion_Table[i]
conversion_Table!i]
Conversion_Table[i]
conversion_Table!i]
. ,.
(char)i;
(char)i;
This conversion table displays the bytes read as characters. You can't
display all of the ASCII codes. For example, ASCII code "12" clears
the screen. To prevent this sort of thing, all of the printable characters
are stored in a table. Unprintable characters (ASCII codes 0-31 and 128159) are represented by periods "." (see your AmigaBASIC handbook,
Appendix A for a list of ASCII characters).
We used four for loops to construct this table. Note that the
initialization of the loop variable i is missing from the last three for
loops. This is unnecessary. After each for loop the loop variable i
has the same value as the variable used in starting the next for loop.
Now the individual bytes can be read, after setting the file position to 0:
filepos = 0;
while ok = BRead(
!= 0)
printf (1I$%08lx
filepos += ok;
.. , filepos);
");
;~<WIDTH;i++)
printf ("
");
for (i=O;l<ok;i++)
printf ("%C" ,Conversion_Table [Byte til ]);
printf ("\n");
21
1.
THE
AMIGA
LANGUAGE
The condition in the while loop ensures that bytes are read from the
file. To do so, the routine BRead is called, which causes either WIDTH
= 12 bytes or the number of bytes remaining in the me to be read (Plus
one, because feofO doesn't become true until one character has been
read past the end).
The variable z contains the number of variables read:
int BRead()
{
int i;
int z;
for (i=O;i<WIDTH;i++)
if (lfeof(_File
{
Byte[ij
z++;
fgetc(_File);
return (z) ;
fies the number of bytes to be displayed per line. The sample output at
the start of this section was created using a WIDTH of 9. When printing
the hex dump to an 80-column CLI window, WIDTH can be set to 12.
This utilizes the entire width of the window.
When the hex dump is displayed, the file position is printed first
(printf ($%08lx ,filepos);). Next the file position increments
by the number of bytes read (filepos += ok;). The result of
BReadO is the number of bytes actually read (while ok = BReadO)
!= 0).
Mter the file position (and some spaces) has been printed, each byte
must be printed in hexadecimal notation. This is done in a f or loop
(for (i=O; i<ok; i++)printf("$%02x ",Byte[iJ);). Here the
hexadecimal numbers occupy two characters. A leading zero is placed in
front of the numbers "0" through "f'.
If the number of bytes actually read is less than WIDTH, spaces are
printed for the missing bytes (if (ok < WIDTH) for (i=ok;
i <WIDTH; i ++) prin t f(" ");). It can only occur at the end of the
file that fewer than WIDTH characters could be read, but it still looks
unprofessional if the hexadecimal and ASCII characters are not kept in
their respective columns, such as:
$00000000 $12 $23 $10 $07 $06 $07 $c2 $82 $85
22
ABACUS
Now you just have to print the bytes as ASCn characters (for (i=O;
i<ok; i++) printf("%c",Conversion Table [Byte[i]]);).
Here is the entire program, again with the definitions of all variables
and arrays:
/*******************************************************/
/*
*/
/* 1.2.2.A. dump.c
*/
/*
DUMP
*/
/*
(c) Bruno Jennrich
*/
/*
*/
/*
*/
/* This program creates a hex dump of a file.
*/
/*******************************************************/
#include "stdio.h"
#define WIDTH 12L
FILE *_File;
/* file descriptor
long filepos;
/* current file position
unsigned char Byte [WIDTH);
/* buffer for bytes read
unsigned char Conversion_Table[256);
/* ASCII - printable conversion
int ok;
1* number of bytes read
*1
*/
*1
*1
*1
int BRead()
{
int i;
int z = 0;
for (i=O;i<WIDTH;i++)
if (!feof(_File
{
Byte[i)
z++;
= fgetc(_File);
return (z);
23
1.
THE
AMIGA
LANGUAGE
/* loop variable *1
int i;
i f (argc != 2)
{
/* open file *1
/* file open error *1
0)
(i=0;i<32 ;i++)
(
;i<128;i++)
(
;i<160;i++)
(
;i<256;i++)
filepos=O;
while ok
Conversion_Table[i]
'.';
Conversion_Table[i]
(char)i;
Conversion_Table[i]
'.';
Conversion_Table[i]
(char)i;
1* Conversion_Table aufbauen *1
!=
0)
printf ("$%08lx
filepos+=ok;
",filepos);
1* print current file position *1
1* increment current file position *1
24
ABACUS
CloseIt (0);
/* bye */
CloseIt (Error_Code)
int
Error_Code;
if (_File != 0) fclose (_File);
/* if file open */
/* close it */
exit (Error_Code);
1.2.3
A monitor
With a few changes, the hexadecimal dump program can also be used as
a machine language monitor. A monitor lets you view the contents of
the Amiga's memory. The monitor displays the various memory
contents.
To do this you must tell the program the starting and ending addresses
of the area of memory you want to see. This is done again with command arguments. Two arguments are passed:
main (argc, argyl
int
argc;
char
**argv;
{
i f (argc != 3)
{
printf ("USAGE
exit (10);
After you have determined that the correct number of arguments have
been passed, you can start converting them. The starting and ending
addresses are specified as ASCII strings, and must be converted to
numbers. We decided to convert the strings to hexadecimal numbers.
Almost every monitor on the market works with hexadecimal numbers.
so your monitor will too.
The Aztec compiler offers a routine which converts an ASCII string
into a hex number. The following is our conversion routine:
unsigned char *atoh (String)
char
*String;
{
int i;
unsigned char *hex;
25
1.
THE
LANGUAGE
This routine ftrst sets the return value hex to 0 and factor to 1. The
following steps only occur when the string length doesn't exceed eight
bytes. This is because the address range of four gigabytes, which the
Amiga can theoretically address, is represented by the addresses from 0
to FFFFFFFF. The highest address is therefore FFFFFFFF. As a
string, this number is eight characters long.
If the string passed to the routine has eight or fewer characters, the
main loop of this function executes. In this loop the string is exam-
2.
When calculating the result you have to test to see if the current
character is a digit (0-9) or a hex character (O-F).
In the ftrst case the ASCII value of the digit 0 is subtracted from the
ASCII value of the digit O. This is done to obtain the numerical value
26
ABACUS
which the digit represents. "1 - 0" yields the number 1. "2 - 0" yields
the number 2, etc. These relationships can be easily seen from an
ASCII table. The ASCII code for the digit 0 is 48, the digit 1 is 49, and
so on, up to the digit 9, which has the ASCII code 57.
This number is multiplied by factor and added to the previous result
factor is then multiplied by the value "Oxl0 = 16". This causes a
place shift
In the decimal system the number 123 means:
3 ones + 2 tens + 1 hundred
Or written differently:
3
Oxl + 2
OxlO + 1
OxlOO
factor runs through Oxl, OxlO, OxlOO. etc. Now it may be clear
why the string processing goes from back to front. This guarantees that
the last character of the string represents the ones place of the number.
The second-to-Iast character is in the sixteens place, the third at the
256's place, etc.
Now we come to the second case. Here you have a hex digit A-F. Since
you do not assume that you use only upper or lowercase, you fIrst have
to convert the character to uppercase. This is done by masking out bit
4. (String[i] & MASK" = "String[i] & (255L-32L (see ASCII
table). The ASCII value of the letter "A" is subtracted from the hex
digit in order to get the numerical values 0. 1,2 etc. Since the digit A
corresponds to the value Oxa or 10, you have to add Oxa to this result
You can continue the calculation as above.
The starting and ending addresses which determine the memory area can
now be calculated with this function:
lowadress = atoh (argv[l]);
highadress = atoh (argv[2]);
You should note that characters which aren't valid hex digits are
ignored. The string 12$$5 returns the same value as 125.
Now test to see if the starting address is lower than the ending address
of the memory range. The memory is processed in ascending order:
27
1.
THE
LANGUAGE
If the starting address is larger than the ending address, the program
exits immediately. If the starting address is less than the ending address,
then you can use the conversion table from the previous program
example:
, ,.,
for (i=0;i<32 ;i++) Conversion_Table[ij
for (
for (
for (
;i<128;i++) Conversion_Table[ij
;i<160;i++) Conversion Table[ij
;i<256;i++) Conversion:Table[il
.
, .,.,
(char)i;
(char) i;
= lowadress;
Now you can start displaying the memory contents. This should be
done in a separate routine because you have to test when the output
should stop.
In the hex dump of a file it wasn't so hard because the ReadO function
would tell you if you could continue to read from the file or DOL
ReadO also told you how many bytes had to be printed.
As long as the value of pos has not reached the address of the highest
memory location to display, bytes are displayed. You also need to check
to see if you have enough bytes to fit in a line (Show(WIDTH or
fewer.
If the number of bytes to print is less than the number of bytes which
can be displayed on a line (WIDTH), then only the remaining bytes are
displayed (Show (highadress-pos.
To test to see if fewer than WIDTH bytes are remaining to be printed,
you must compare the number of bytes to be printed (highadresspo s) with the number of bytes which can be displayed on a line.
Unfortunately, the Aztec compiler does not understand long constants
(like 12L) in a comparison of long constants (an exception is "=" in
28
ABACUS
which long constants are not processed). Long constants are correctly
handled in assignments and as parameters to procedures.
We used a little trick: We assigned the value of the long constant to a
long variable and used this variable in the comparison. The compiler
understands this (you don't have to use this trick with the +L option
and the c32.1ib library).
Now on to the routine responsible for printing the bytes on the screen.
It should look familiar to you-we made a few small changes:
Show (ok)
long ok;
{
int i;
printf ("$%08lx
",pos);
for (i=O;i<ok;i++)
printf("$%02x ",*(pos+i;
i f (ok < WIDTH) for (
");
This routine first outputs the address of the first byte to be displayed on
the line. Then each byte is printed in hexadecimal notation. The method
used to make sure that the ASCII versions of the bytes are always
aligned when there are fewer then WIDTH characters to be printed is also
familiar.
The access to pos is something new. This variable (a pointer) is
accessed with *(pos+i). This means: Add the value i to the current
address stored in pos, and return the contents of the memory location
to which this result points.
The same goes for *(pos++). This means: Increment the value ofpos
by one and return the contents of the memory location to which this
value points.
The parentheses are necessary in both cases. * has a higher precedence
than ++. If the parentheses were omitted, *pos++ would mean: Get
the contents of the memory location to which pes points and then
increment the value of pes.
Here again is the complete program:
29
1.
THE
C LANGUAGE
AMIGA
/**************************************************************/
/*
1.2.3.A.mon.c
*/
/*
*/
/*
MON
*/
/*
(c) Bruno Jennrich
*/
/*
*/
/*
*/
/* This program outputs a memory range
*/
1* as a hex dump.
*1
/**************************************************************/
idefine WIDTH llL
#define MASK (255L-32L)
1* conversion table */
unsigned char *lowadress,
/* lowest address to be displayed *1
*highadress; /* highest address to be displayed */
unsigned char *pos;
*/
int i;
unsigned char *hex;
unsigned long factor;
hex = (unsigned char *) OL;
factor = 1L;
/* result *1
/* place factor */
1* result = 0 *1
1* factor equals 1 (one place) *1
return Chex);
main Cargc, argv)
int argc;
char
**argv:
{
int i;
i f (arge 1= 3)
{
30
ABACUS
1* error in input *1
(i%0;i<32 ;i++)
( ;i<128;i++)
(
;i<160;i++)
(
:i<256;i++)
Conversion Tab1e[i)
'.';
Conversion-Table [i) = (char)!;
Conversion=Table[i]
'.';
Conversion Table[i]
(char)i:
- 1* build conversion table *1
pos = lowadress;
int i;
printf (1I$%OBlx
H,
1* output position *1
pos) ;
for (i=O;i<ok;i++)
printf ("$%02x ",*(pos+i;
if (ok < WIDTH) for
printf ("
");
printf ("
; i<WIDTH;i++)
It):
for (i=O;i<ok;i++)
printf (II%cll,Conversion Table[*(pos++)]);
printf("\n") :
1* bytes as hex *1
1* carriage return *1
Note that the contents of the specified ending address are not printed.
mon 1 00 200 displays the contents of memory locations hex 100 to
up to and including hex 1ff.
31
2.
C Compiler
Operations
2.
ABACUS
2.
C COMPILER OPERATIONS
C Compiler Operations
The compiler, assembler and linker are all common to the two popular
C compiler implementations. We will now look at these three and their
options and take a closer look at the compiler during compilation.
First let's look at how the compiler is called. It follows this form:
cc [>output_file] [options] prog[.c]
You can also send the messages to a disk file named errors.e and
then read this file with ED (ee >errors.e program).
Redirection
,.
35
2. C COMPILER OPERATIONS
The caret (1\) shows where the compiler believes the error occurred. The
compiler doesn't notice the missing semicolon error, number 69, until
one line later. Since it is an unwritten law that only one function is
written per line (excluding if statements where commands only appear
in the "then" segment of the function), this error always occurs one line
later.
Now we come to the compiler options:
Aztec
Compiler
Options
-A
-Dsymbol[=value]
36
-Idirectory
-0 filename
-s
-T
-B
This option suppresses the halting of message output after every five messages. When directing error
messages to the printer, for example, it doesn't
make sense for the compiler to stop after every five
messages to ask you if you want to continue.
-Enumber
- Lnumber
ABACUS
2. C
COMPILER OPEk"TIONS
-Znumber
+B
+c
+D
+Hfilename
+Ifilenarne
+L
Lattice.
+Q
+ff
+fi
+f8
37
2.
C COMPILER OPERATIONS
+m
This option checks the stack for overflow. If overflow occurs, a runtime error is produced.
-n
+P
+r
It's more than just passing additional arguments to ee. You also have
to call the assembler "by hand," which creates the object file
program.o from program. asm. If you specify only the -0 option
(without -A) when compiling, the object file created by the assembler
(now called by the compiler) has the specified name.
Normally you don't have to specify the name for the object file because
it automatically has the same root name as the C source file, but with
an extension of .0. If your program is large enough that you have to
conserve every byte of space on the disk, you should remove the object
file after each link with delete. This can be done with the professional version in the script file or with the developer's version in the
makefile. This is useful only when you have multiple programs on
the disk, each of which consists of only one module.
38
2.1 How A C
ABACUS
2.1
COMPILER
WORKS
39
2. C COMPILER OPERATIONS
2.1.1
It looks similar for structures. Here you have the name of the structure,
followed by a code (a single byte) for the start of the structure. Then the
variables contained in the structure are listed:
struct nonsense
{
int hello;
char na;
unsigned char good_day;
};
Symbol table:
"nonsense"/"code_structure_type/"hello"/code_int/
"na"/code_char/"good_day"/code_unsigned_char/end_code
40
2.1 How
ABACUS
COMPILER
WORKS
int hello;
char na;
unsigned char good_day;
nonsense_structure;
Symbol table:
"nonsense structure"/"code structure begin/
"hello"/code_int/"na"/code=char/
"good_day"/code_unsigned_char/end_code
ARRAY;
int digits [6] ;
Symbol table:
"digits"/code_array/code_int/6/end_code
41
2. C COMPILER OPERATIONS
ENUMERATED:
enum color
Symbol table:
"color"/code_enum/"red"/O/"green"/1/"blue"/2/end_code
%d",sum,sum*lO);
The printf function fIrst prints the sum of the two variables a+b.1f
the second output is supposed to be the sum multiplied by ten, you
will probably be disappointed here. The compiler sees sum*10 and
generates a +b * 1 O. It adds the value of a to ten times the value of b.
You should put the equation in parentheses to avoid this:
#define sum (a+b)
42
2.1 How
ABACUS
#include "exec/types.h"
#endif
C COMPILER
WORKS
#ifndef GFX_H
/* if GFX_H is not defined, */
#include "graphics/gfx.h" /* then include graphics/gfx.h */
#endif
The compiler can use the symbol table to check to see if a symbol is
defined (#ifdef) or not (#ifnde). The compiler can then determine
the fate of the statements enclosed in the #if... - #endif block. Each
include file defines a symbol at the start with the name
"IncludeFile H".
Bit fields
Now let's look at bit fields. These are a new feature added to Aztec
compiler version 3.4a. As the name implies. bit fields provide a way of
organizing individual bits of information. They are declared similarly to
structures:
struct bitfield
{
unsigned
unsigned
unsigned
unsigned
unsigned
2:;
1: a;
1: b;
1: c;
3:;
/* bits 0 and 1 */
/* bit 2 */
/* bit 3 */
/* bit 4 */
/* bits 5-7 *1
11
struct bit field bf;
This declaration creates an eight-bit-wide bit field. You can now access
bits 2. 3 and 4 with bf.a. bf.b and bf.c. You can only set these
variables to 0 or 1. This is logical. since a single bit can have only one
of two values.
You cannot access bits O. 1.5.6 and 7 because they were not assigned
names. These bits were "skipped" as told by the unsigned 2:; and
unsigned 3:; directives.
The symbol table for bit field declaration and definition above looks
like this:
Symbol table:
"bit field" / code _type_begin/"" /code_bitO/'''' /code_bit1/
"a"/code_bit2/"b"/code_bit3/"c"/code_bit4/""/code_bit5 I
""/code_bit6/""/code_bit7/end_code
Definition:
"bf"/code_structure_start/code_type/"bitfield"/end_code
union
Now let's look at the last of the non-scalar data types-the union
union designates a variable which can consist of more than one data
type:
43
2. C COMPILER OPERATIONS
union number
{
int i;
float f;
union number n;
Symbol table:
"number"/code_union_type/"i"/code_int/"f"/code_float/
end_code
Definition:
The variables i and f occupy the same storage space. Therefore, this
variable can be interpreted as either type.
There are also pointers to the individual variables, structures and arrays,
so we must introduce the code for pointers:
int *pointer;
struct nonsense *sensible;
Symbol table:
"pointer"/codeyointer/"sensible"/code_int/end_code/
"sensible"/codeyointer/"nonsense"/code_structure_type/
end_code
The symbol table constructed during the compilation is needed whenever a variable is accessed. IT you want to increment the variable i
(i ++), for example, the compiler must know the variable type of i.
Assuming that i is an int object, the compiler then knows that this
variable is 16 bits wide and that it must generate the statement
add.w #l,address of i and not add.b #l,address of i or
add.l #l,address_of_1 for the assembly language source. It is similar when accessing the values of structure elements, e.g.,
sensible->hello = O. The compiler must first find out if
sensible is really a pointer. From the symbol table it learns:
sensible/code-pointer and knows that sensible is a pointer.
Now it must determine what kind of object sensible points to. If
sensible points to an int variable, than the line sensible>hello doesn't make sense, because int variables don't contain
additional elements, as do structures.
From the symbol table the compiler discovers that sensible points
to a structure. The "->" is therefore allowed. sensible->hello can
also be replaced with (* sensible).hello, whereby it should be
44
2.1 How
ABACUS
C COMPILER
WORKS
noted that the period (.) has higher priority than the star (*) in front of
sensible, which is why the parentheses are necessary.
If you wrote sensible.hello, the compiler would respond with an
(char *) input;
4S
2. C COMPILER OPERATIONS
The program segment above illustrates two features beside the cast:
First, a variable is initialized in its definition, and second, strings are
used.
1**************************1
1* Assembly language
*1
1**************************1
dseg
ds 0
public _String
_String:
dc.l .1+0
cseg
;data segment
;reserve 0 bytes
;global variable
;allocate memory
;with address of string
;code segment
.1
dc.b 84,104,101,32,65,109,105,103,97,32,105,115,32,84
dc.b 111, 112,115, 33,33,0
;ASCII Codes
ds 0
;null byte
public _main
_main:
;here we start
char *string
1**************************1
1* Assembly language
*1
1**************************1
public yroc
yroc:
link a5,#.2
movem.l .8,-(sp)
46
;procedure is 'global'
;allocate stack storage
;save registers
2.1 How
ABACUS
lea .1,aO
move.l aO,-4(a5)
C COMPILER
WORKS
movem.l (sp)+,.8
unlk a5
rts
.2 equ -4
.3 reg
.1
dc.b 84,104,101,32,65,109,105,103,97,32,105,115,32,84
dc.b 111, 112,115, 33,33,0
ds 0
;null byte
As you see, the strings are in an unusual form. The ASCII codes of the
characters, not the characters themselves, go into the assembler file (see
Section 2.2 for information about the common assembler directives
global, public, dseg and cseg).
Here we should probably explain the width of the individual variables:
Char objects are always one byte wide and therefore have a value range
from -128 to 127 or 0-255, if you use "unsigned char". Int
variables occupy two bytes with a value range of -32768 to 32767 or 065535. Long objects are four bytes wide with a value range of 2147483648 to 2147483647 or 0-4294967296. Float variables
contain four bytes and double variables 8 bytes.
All this time you have assumed that your program only declares and
defines variables outside functions. But you can also define variables
within a function which are available only for that function. Take a
look at the following program:
int i;
main () { ... }
prroc ()
{
int i;
i
= . ;
47
2. C COMPILER OPERATIONS
The variable i appears twice. If you access i in any line of the prroc
function, the compiler knows that you mean the i defined within this
routine. The compiler needs a second symbol table to recognize this. If
the compiler wrote the symbol code for the variable i in the global
symbol table, then it is unable to distinguish between the global i
dermed before mainO and the i in procO.
The local symbol table is used whenever variables are declared and used
within a function. When the function is compiled, the next function can
access the entire memory range of the local symbol table. When a variable is accessed in the routine, the compiler looks for the variable in the
local symbol table first. If the variable could not be found there, then
the compiler searches the global symbol table. When using libraries,
defining the library base pointer inside or outside the function is important. For example, if you define the pointer to the Intuition library
(struct IntuitionBase *IntuitionBase) within a function
(e.g., mainO), then you cannot use the Intuition functions.
They require the variable _IntuitionBase, which can only be made
available for other modules and linker libraries with the assembler
directive global Intui tionBase,4. If IntuitionBase is
dermed within a function, then only stack space is reserved for this
pointer, which cannot be accessed with the label_Intui tionBase.
But back to the local symbol table. Naturally it also needs space in
memory. The compiler automatically allocates 1040 bytes for it. If the
compiler gives you the error message Local table full (Use -L),
you should follow its advice and increase the memory for the local
symbol table with the -L option. You should note that an entry is 26
bytes long. -L40 reserves 1040 bytes.
The global symbol table can also be saved-the local symbol table
cannot. This is especially useful when you write programs which use
many include files.
You can separately compile the include file block (which may contain
only include files) and then store the symbol table by using the +H
option (cc +Hcompiler includes include block.h)
whereby the include block looks something like:
finclude "exec/types.h"
finclude "graphics/gfx.h"
#include "graphics/gels.h"
Mterward you can read this symbol table back in with the +I option
(cc +Icompiled_includes program.c). This saves the time of
reading the individual include files and constructing the symbol table.
48
ABACUS
2.1.2
2.1 How
COMPILER
WORKS
49
2. C COMPILER OPERATIONS
1*********************1
1* C language
*1
1*********************1
lnt c;
main ()
(
lnt j;
j = 0;
I
proc()
{
lnt i;
c = 0;
. ..,.
For the sake of simplicity, we are using only int variables here. But
the principle is the same for allocating or reserving memory for structures,arrays, etc.
Let's look at the assembly source created by the compiler:
;:ts = 8
global _0,2
; senseless! ? !
;tells the assembler to reserve
;two bytes for c. Same as
;" c: ds.b 2" ; main is known to all modules
;"as is _cIt
main:
link
movem.l
as,#.2
.3,-(sp)
clr.w -2(aS)
.4
rnovem.l (sp)+,.3
unlnk
rts
as
.2 equ -2
.3 reg
public ...Froc
link as,#.9
rnovem.l .IO,-(sp)
clr.w c
.11
rnovem.l (sp)+,.lO
unlnk as
rts
.9 equ -2
.10 reg
public .begin
dseg
end
so
;2 bytes for i
;no registers to be saved
;start of the data segment
ABACUS
WORKS
The variable storage for variables in functions comes from the stack.
while the storage for global variables, which are defined outside the
functions, occupies "normal" memory. These declarations look similar
for structures and arrays, except more bytes are reserved. For unions,
sufficient storage is reserved for the largest variant in the union. This
memory space is then accessed when a union element is accessed:
/*********************/
/* C language
*/
/*********************/
union num
{
int i;
float f;
union num z;
main 0
{
z.f
= 1.1;
z.i
= 10;
;z.i
movem.l -(sp),.3
unlk as
rts
.2 equ 0
.3 reg
public .begin
dseg
end
=0
51
2. C COMPILER OPERATIONS
/*********************/
/* C language
*/
/*********************/
main ()
{
struct Structure
{
int Elementl;
long Element2;
float Element3;
struct Structure s;
struct Structure t;
s.Elementl
0;
s.Element2
s.Element3;
s=t;
;s.Elementl
=0
move.l -4(aS),dO
jsr Ffid
move.l do,-8(aS)
;s.Element3 -> dO
;convert float to long
;dO -> s.Element2
lea -lO(aS),aO
lea -20(aS),al
move.l (al) +, (aO) +
move.l (al) +, (aO) +
move.w (al) +, (aO) +
;&s to aO
;&t to al
;convert bytes
.4
movem.l (sp) +, .3
unlk as
rts
.2 equ -20
.3 reg
public begin
dseg
end
As you can see, this program uses variables of type float. It must
therefore be compiled with the +fi options. When using double
variables, the +ff option must be used. When using the 68881 math
coprocessor you must compile your programs with the +f8 option. In
all three cases math routines are needed which are not in the c.lib
52
2.1 How
ABACUS
COMPILER
WORKS
global variable, you should always specify the storage class extern in
the declaration. This way you always know which variables in the function are being used; which are local and which are global:
int i;
proc ()
{
extern int i;
int j;
The static storage class gives the compiler information about the
type of storage which should be used for the variable. Normally all of
the variables in a function are auto. This means that memory space is
allocated on the stack when the function is entered, and then released
when control leaves it. If you declare a variable static, then the
memory space is taken from the normal program storage. When the
function is left, the value is retained so that you can use the old value
when the function is re-entered. The bss directive of the assembler is
used to assign static storage space. This is similar to the global
directive, except that the storage space for the routine in which the
sta tic variable is defined is valid. This results because the variable is
not accessed by its name, as in global_c,2, but with a normal label
(like .4).
S3
2.
C COMPILER OPERATIONS
2.1.3
add.l #2,-8(a5)
;
;
i=O (intialization of
i')
j += 2 (loop body)
(increment)
add.l #1,-4(a5)
i++
cmp #5,-4(a5)
; i<5
(termination condition)
blt .1
; yes, then back to the beginning
2.1 How
ABACUS
C COMPILER
WORKS
1*******************1
1* c language
*1
1*******************1
1* Have to do the *1
1* initialization ourselves *1
1* while loop
*1
i = 0;
while (i<S)
{
1* loop body
j += 2;
*1
i ++;
.1 :
elr.l -4(aS)
i = 0;
*
*
*
*
*
i<5
no
{ j += 2;
i ++; }
back to the start of the loop
.2 :
In this type of while loop the body executes at least once. The loop
condition is checked after each execution.
Another control structure is the switch statement. It provides a way
of choosing from several alternatives. A typical switch statement
looks like this:
switch (i)
{
case 3:
i++;
break;
case 4:
i--;
break;
ss
2. C COMPILER OPERATIONS
default:
i=O;
break;
.6:
add.l #1,-4(aS)
bra .S
* case 3: i++;
* break;
.7:
sub.l #1,-4(aS)
bra .S
*
*
.8:
clr.l -4(aS)
bra .S
* default: i=O;
.4:
contents of "in to dO
case 4: i--;
break;
sUb.l #3,dO
beq .6
sub.l #l,dO
beq .7
bra .8
.S:
First the compiler moves the value of the variable i to data register dO.
Each case portion is then translated, whereby a label is first placed
before each portion and then the individual statements are translated.
The use of the break statements at the close of each case portion
causes a branch to the instruction after the switch.
How is the s wit c h statement processed by the compiler? It first
causes the variable i to be loaded into data register dO and then the
branch to .4.
When the compiler reads case 3,the value 3 is subtracted from dO
and the result tested for O. A branch is made if true. In the second case
the value is tested against 4. But instead of loading the value of the
variable i back into dO and then subtracting 4, the compiler calculates
the difference between the first and second case (3-4). If this difference
is negative, then the difference is subtracted from dO (sub.l U,dO). If
the difference is positive, it is added (add.l fl,dO). Then you can
again test the data register against O.
If all of the case comparisons fail, the default statements are automatically executed (the default is optional, in which case the swi tch
statement is simply exited).
But naturally a swi tch statement is not always as ordered as was
shown above. It may contain more than two cases, and they do not
have to be ordered.
56
2.1 How
ABACUS
COMPILER
WORKS
switch (i)
(
case 1:
i++;
break;
case 0:
i--;
break;
case 6:
i=O;
break;
case 2:
i=O;
break;
* contents of 'i' to dO
.6:
add.l #l,-4(aS)
bra .S
* case 1: i++;
* break;
.7:
sub.l #l,-4(aS)
bra .5
*
*
.8:
sub.l #l,-4(aS)
bra .S
* case 6:
* break;
.9:
sUb.l #l,-4(aS)
bra .S
*
*
case 2: i--;
break;
*
*
*
*
case 0:
case 1:
case 2:
default:
default:
default:
case 6:
*
*
case 0: i--;
break;
i--;
(0)
(2)
(4)
(12)
* default
* dO * 2
.5:
This s wit c h statement is translated similar to the first. Only the
individual case selection is handled somewhat differently.
57
2. C COMPILER OPERATIONS
First the case blocks are translated in the order in which they appear
in the C program. They are then placed in order in the jump table which
contains the addresses (relative to the PC) of all of the case blocks.
The offsets are then sorted according to the order of the cases to which
they point and are stored.
Under normal circumstances you can test a maximum of 100 case
conditions. If you want to test more cases-which seems mther unlikely.
the -Y option allocates more space for additional case conditions.
- Y 1 0 0 reserves 100 entries for the cas e conditions. Each entry
consists of four bytes, whereby two bytes are used for the offset and the
other two bytes for the integer number for which the case is tested.
When selecting a case condition the value of the variable i (contained
in dO) is doubled (left-shifted) and used an index into the jump table.
Since this table consists only of offsets which are specified in 16-bit
words, i only has to be doubled instead of quadrupled, as would be
necessary if the table consisted of the absolute addresses of the case
blocks.
Since some cases are not tested (case 3:, case 4:, case 5:), the
jump table is filled at these locations with the address of the command
after the switch statement (label.S).
If the cases to be tested are far apart from each other, then they are not
selected with a jump table. This would not be an efficient use of
memory because the default label would appear many times in the jump
table. Therefore the fIrst method is used here-the variable to be tested
is placed in dO and the various case values are subtracted or added and
dO is tested against O.
= 0;
else
i++;
.4:
.S:
cmp.l #1,-4(aS)
bne .4
clr.l -4 (as)
bra .5
add.l t1,-4(a5)
*
*
else i++;
==
no
* yes
* continue
The label poses the only problem when translating the if statement.
The compiler notes which label number it must use after the branch
command. for example. and which label must be placed before a command.
58
2.1 How
ABACUS
C COMPILER
WORKS
2.1.4
Function calls
In addition to using control statements and variable assignments, you
can also write your own functions or use existing ones. There are two
types of functions: those which return a value, and those which do not
Both types let you pass arguments to them. Arguments are passed
through the stack.
func(i,j,k)
int i;
long
j;
struct nonsense *k;
int 1;
1=0;
func (i,k,k)
like this:
_func:
link as.#.2
movem.l .3- (sp)
move.114(a5),-(sp)
move.llO(a5),-(sp)
move. 1 8(a5),-(sp)
S9
2. C COMPILER OPERATIONS
jsr _func
lea lO(sp),sp
movem.l (sp)+,.3
unlnk as
rts
;call routine
;restore stack
;load registers again
;release local storage
;two bytes for int object
;register list
.2 equ -2
.3 reg
The following figure illustrates how the local variables are placed on
the stack:
Pointer
long
lO(aS)_ >1-----_
8 (a5) -
> ~'"""'"=="'-'''"II
lnt
link a5,i.2
as->-(sp);
a5 = sp;
sp->sp+#.2
(.2 equ -2)
~2(a5) ->~~~~
___I
sp _ >1-----_1
movem.l .6,-(sp)
Figure 2.1
You can see that the input arguments are accessed with positive
indexing on as. (e.g., 8(aS, while negative indexing accesses local
variables (-2(aS.
When the input arguments are accessed, the old value of register as.
placed on the stack by the link command, and the return address, placed
on the stack by any JSR, must be skipped. Beyond this, the input
parameters are simply accessed in reverse order on the stack.
60
ABACUS
WORKS
return address because when the arguments were passed, only six bytes
were allocated on the stack instead of 12.
Correct Porameter Hondling
Pointer
14(aS)
long
lO(aS)
int
link as,t.2
link as,t.2
Local
Variable
Local
Variable
Figure 2.2
For functions which return a value, you must specify the type of the
return value. This is given immediately before the function name. There
are also functions of type void, which means that they do not return a
value. If you do not explicitly set the return type of a function, the
compiler assumes that it is into
The type of the function's return value is stored in the symbol table
just like a variable type:
void Function ( , , )
Symbol table:
"Function"/code_function/code_void
61
2. C COMPILER OPERATIONS
You can now be sure that the compiler will not "complain" when you
assign the return value of FindPortO, which is a pointer to a
MsgPort structure, to a pointer of type *MsgPort. Basically, the
objects of the pointers don't matter when doing pointer assignments.
Pointer variables are always four bytes long, so that no conversions
have to be made. There is a second type of pointer, however, the BCPL
or CPTR pointers. These are leftovers from the BCPL language. Their
contents are shifted right by two bits so that they can access only long
words. BCPL pointers occur only as input parameters, and then only
rarely I such as in certain I/O routines.
You can also use extern to assign different return values to functions
in other modules. Let's say that the routine atolO is defined in
module 2 and that it actually returns a long value. If you now define
in module 1 that this routine returns a value of int type (extern
int atolO), the compiler won't be any the wiser because the symbol
table for the second module is not available. Note that this declaration
must take place before the function is used. Even when you write your
own functions of type other than void, you must ensure that they are
defined (or declared before they are used) so that they are entered in the
symbol table.
For functions, the input argument types are irrelevant. There are compilers, however, which check the input parameters for "type purity" so
that errors like those described above don't occur. The Aztec compiler is
not one of these.
62
ABACUS
2.2
The Assembler
After the compiler has created the assembly language source, the
assembler can begin its work. It must create the object file from the
assembly source. Its call is as follows:
as [>output_file) [options) program.asm
;data segment
;reserve 0 bytes
;global variable
memory with address of the
; code segment
;data segment
; "Hello"
;code segment
public -main
main:
- link
a5,#.3
movem.l .4,-(sp)
add.l 1, str
jsr _exit
;start here
;str++;
.5
movem.l (sp)+,.4
unlk as
rts
.3 equ 0
.4 reg
public exit
public -:-begin
dseg
end
63
2.
C COMPILER OPERATIONS
the same time it checks to see if a label in the symbol table corresponds
to something in the source file. This is the case with rna in, for
example. First this label is placed in the symbol table (public
_main) and then defined L main: ). global_i,2 places the label_i
in the symbol table. The memory storage for these variables is not
made available until the program is linked. The bss directive also
reserves storage for a variable. The label associated with the storage is
not placed in the symbol table, however. Therefore bss is well suited
for declaring static variables within functions.
When an instruction now accesses a memory location specified by a
label, the symbol table is searched to see if this label is defmed in the
same module. If this is the case, the offset from the current position to
the label can be calculated for the instruction.
When calculating the offset, it is important to know whether the label
describes a memory location in the code segment or the data segment.
From the first pass through the program the assembler knows how
many bytes the program contains. If an instruction accesses a label in
the data segment, you must take into account the fact that the data
segment is appended to the code segment.
The code and data segments are two distinct areas. If the directive dseg
stands before an instruction in the assemble language source, then all
subsequent data is written into the data segment until a cseg occurs,
marking the start of the code segment.
In the following listing you can clearly see that the code and data
segments are two separate areas. Whenever the directive dseg or cseg
appears, the address in the second column changes (the code segment!
data segment program counter).
Aztec 68000 Assembler 3.4a 1-25-87
1 0000:
dseg
2 0000:
ds a
3 0000:
public str
4 0000:
str:5 0000: xxxx xxxx
dc.l .1+0
6 0004:
cseg
7 0000:
dseg
8 0004:
.1
9 0004: 4861 6c6c 6fOO
dc.b 72,97,108,108, 111,0
10 OOOa:
cseg
11 0000:
global i,2
public -main
12 0000:
main:
13 0000:
- link a5,i.3
14 0000: 4e55 0000
15 0004:
movem.l .4,-(sp)
64
ABACUS
16
17
18
19
20
21
22
23
24
25
26
27
0004:
0008:
OOOc:
OOOe:
OOOc:
OOOe:
0010:
0010:
0010:
OOOc:
OOOc:
OOOa:
52ad xxxx
4eba xxxx
.5
movem.l (sp)+,.4
unlk as
rts
.3 equ 0
.4 reg
public exit
public :begin
4e5d
4e75
0000 0000
0000
dseg
end
a- .g:ra8a
$
$.1
$
$.'
$.8
link
add.!
jar
$.c
$.c
unlk
rt.
$04
..
.',
11,
-exit
.s
,. $0 (as)
Clta-aa"lIIa:Dt
,
fl.
fl.
$0.
$
$Ie
$Oe
dc.1
"Sello"
n.
...
lJab.~-tlbl.
Cod..-for-Coda-Se9llent
Acid.r.all 1117
"_i"
RESOLVED
Code-for-Cocle-SelJllleDt.
Ackireaa 0
I+-
"-mun"
RESOLVED
Code-for-Extern
Address 1111
"-exit"
UllRBSOLVED
code- for-Extern
Addrel!llil 1177
Figure 2.3
".bellin"
UNRESOLVED
6S
2.
C COMPILER OPERATIONS
Let's look at the listing and the assembly language source. When you
compare the two, it appears that the assembler is not assembling the
instructions movem.l .4-(sp) (line 15) and movem.l -(sp),.4 (line
19).
-0 filename
-Idirectory
-L
-N
-Snurnber
-v
-ZAP
-c
-D
-Ename[=value]
66
2.3
ABACUS
2.3
THE LINKER
The linker
The linker resolves unresolved references in the individual modules. To
do this, the object files (modules) are usually linked to the linker
libraries. The call is as follows:
In [>output_filel object_file.o [options]
Let's look at the last example program, which called the _ exi t
routine. This routine wasn't defined in the module in which it was
called, however. The _ exi t definition takes place in the linker library
c.lib. This must be linked to the object file which contains the
exit call.
The linker frrst reads the object file. It looks at the symbol table. From
the symbol table the compiler learns that _ exi t is an unresolved
external reference. The linker notes this in a table which lists all of the
unre-solved references. If one of these references is later resolved in a
module, then this is noted in the table.
Now the linker reads each module in order. Up to now, the commands
which called external routines, for example, were not complete. The hex
code for the PC-relative j s r instruction exists, but the offset of the
routine to jump to is unknown. These offsets can be calculated in a
second pass and then entered at the appropriate location.
Let's look at the following assembler source:
dseg
ds a
public _Str
Str:
- dc.l .1+0
;allocate
cseg
dseg
.1
dc.b 72,97,108108, Ill, a
cseg
global i,2
public :=main
main:
- link
as,#.3
movem.l .4,-(sp)
add.l I, str
jsr _exit
.5
movem.l (sp)+,.4
unlk as
rts
.3 equ
.4 reg
;data segment
;reserve a bytes
;global variable
memory with address of the string
; code segment
;data segment
; "Hello"
;code segment
;start here
;str++;
a
67
2.
C COMPILER OPERATIONS
public exit
public :-begin
dseg
end
occupies four bytes. But the assembler uses only the first two bytesnamely those used for the code for the PC-relative j s r instruction
itself ($4eba). The remaining two bytes, which specify the offset for
this instruction, remain initialized. This initialization is handled by the
linker, which can calculate all of the necessary offsets in a second pass
and enter them in the appropriate locations.
Now we come to a question you are probably asking yourself: When
you link an object file with a linker library, are all of the functions of
the library linked in or just the functions actually used by the object
file? Here the Aztec linker is very economical. It appends to your program only the library routines actually used, when linking two object
files, it combines all of the functions.
But here again we have a question: Since you can link c.lib to a
module with In Ofile.o -lram:c as well as In Ofile.o
ram:c.lib, and since c.lib is just an object file which contains
many functions, how are only the needed routines from c.lib added to
the Ofile and not the entire c.lib?
The answer is that the linker libraries are not object files in the sense of
the files which are created by the assembler. The linker libraries consist
of a combination of several normal object files. These libraries are organized in a somewhat different manner. While the symbol table is placed
at the end of the file in normal object files, the table moves to the
beginning of a linker library. Then comes the executable machine code.
If the linker is to link a library to an object file, which is almost
always the case, it recognizes the library by the special code at the
beginning of the file. The linker then reads the library symbol table,
which it uses to immediately resolve external references in the object
file. It also notes which library routines are used.
When the linker has read the symbol table, it then comes to the
executable code of the library. It removes the functions that it noted and
appends them to the normal object flIes. In the second pass it calculates
all of the necessary offsets and sets them.
The linker is not only responsible for resolving unresolved external
references. It also links startup code to the object file. This is accomplished by the assembler directive public .begin..begin is stored
in the list of unresolved references in the first pass and then appended to
the program later, when linking with c.lib.
68
ABACUS
In addition, the machine language instruction jmp .begin (in PCrelative form) is created for each module which contains the directive
public .begin. This is necessary only for the first object file to be
linked. This command must be the first one in the program. In the
other modules this command just takes up space (four bytes). Therefore
when you use multiple modules you should make sure that the label
.begin appears only in the first module. You should compile all other
modules with the +b option.
Now we come to the startup code. First it opens the DOS and
MathFFP libraries. Then the startup code determines whether the program was started from the Workbench or the OJ.
When starting from the Workbench, it must first wait for the startup
message. If this message passes arguments, they are interpreted as a
Lock structure and designate the Locked directory as the current
directory. The Tooltype window opens, if necessary, and defaults to
standard input/output.
When starting from the CLI, the startup code ensures that the program
gets the number of arguments and the address of the argument array.
These are made available by the OOS through the CIS (Command Input
String). The CLI's startup code prepares the parameters so that they can
be passed through main(argc, argyl.
After processing the parameters string, the main routine of the C
69
2. C COMPILER OPERATIONS
The linker libraries with a 1 in their names must be linked to the object
fIles when you want to use the "large data" and "large code" memory
model.
2.3.1
2.3.2
70
2.3
ABACUS
THE LINKER
If you want to use either "large code" or "large data," you must compile
and assemble the program with the +C or +D option. In both cases you
must link in a library with an 1 in the name (such as cl.lib or
c132.lib).
Now you may be wondering what the "32" means in names like
c32.lib and cl32.lib. This 32 stands for 32-bit integers. The int
variables used in these libraries are 32 bits in length instead of 16.
This is especially important for compatibility between Lattice and
Aztec programs. Lattice programs don't use 16-bit int variables. The
compiler extends variables and constants to 32 bits. This extension is
not performed by the Aztec compiler when using an int variable as a
function parameter, 16 bits are passed instead of 32. The Amiga library
routines require 32 bits, however, and the arguments may be interpreted
incorrectly, or the system may even crash.
The Aztec compiler can emulate the Lattice compiler when you extend
all int variables and constants to 32 bits. This is accomplished with
the +L compiler option.
The C support functions in c32.lib, cl32.lib, etc., were compiled
with this +L option and are useful when compiling Lattice source codes
under Aztec.
~O filename
-Lfilename
-F filename
-T
71
2. C
CoMPILER OPERATIONS
H2 org and
H2 end. The program begins
at_HO_org. -
-w
-v
+O[i]
This option instructs the linker to write the subsequent object module to the next or specified code
segmenL
+C[cdb]
+F [cdb]
+A
-c.
72
-Q
These options instruct the linker to store information for the source level debugger (not currently
implemented).
+Q
-M
+L
ABACUS
+s
+ss
+sss
73
2.
C COMPILER OPERATIONS
2.4
The debugger
Theoretically your C program should run correctly. But what do you do
if the program doesn't run, and you just can't explain why the program
doesn't do what you had wanted it to?
The debugger can help you find errors. It monitors executable programs
for runtime errors. There are two basic types of errors:
1.
2.
With the first type of error, you enter the debug mode of the included
debugger (bin directory of the third disk) with the al command. This
command waits until a program has been started from the Workbench or
the CLI.
Mter you have started the program the debugger responds immediately.
It attempts to load a symbol table (Loading syms ...). This symbol
table is similar to that which you examined for the compiler. Here the
table must be created by the linker (-w option), which contains all of
the labels used as well as their offsets from the start of the program.
Therefore in the development phase you should link your program with
-We The final and error-free version of the program should not be linked
with the -W option, especially if you intend to publish the program in
any form. This additional information is very useful for others who
want to know how your program works.
Mter the symbol table loads, the debugger displays all of the CPU
registers. In addition to the program counter (pc), status register (Sr)
and condition flags (x, n, Z, v, c), all of the data and address registers are
also displayed. In addition, the debugger disassembles the first instruction of the C program, e.g.:
' __HO_org jrnp .begin'
74
2.4
ABACUS
THE
DEBUGGER
But being able to see the program doesn't help much when looking for
errors. Therefore the debugger gives you something called single-step
mode. Each time you select s, the current or previously displayed
instruction executes. The registers are then redisplayed and the next
command is disassembled.
It's very tedious to have to step through the startup code at the beginning of the program. The startup code cannot be omitted, however,
since it is here that the command parameters are processed and important libraries are opened. Therefore you should set a breakpoint at the
routine _main (bs_main (breakpoint set. The _main routine is
called at the end of .begin.
After setting the breakpoint you can start the program by selecting g
(go). The program (startup code) executes until a breakpoint is reached.
Since this occurs at _main, you can proceed through the actual
program in the single-step mode, whereby the command parameters
fetched by the startup code are also taken into account.
If during single-stepping you come across a call to printf, for
example, you don't have to worry, you don't have to go step by step
through this long routine. The t (trace) command executes this routine
and returns to the next instruction in the program.
If you have a copy of the assembler listing augmented by the C
statements as comments (-T compiler option). you can go through the
program step by step, and when you discover an error, you know
exactly which C statement is at fault.
Now to debugging a program which gives you a Finish ALL disk
acti vi ty ... requester. One possibility would be to debug the
program in the manner just described until you can find the error. Or
you can debug the task in which the error occurs.
Imagine the following example: A program leads to a division by O.
This displays the requester just mentioned, and you can return to normal
operation only by resetting the computer.
But when you start the debugger (through a new eLI) or are in an
already running debugger, you may be able to save the system before a
total crash, and perhaps save important data (by jumping to a routine
which stores the data, for example).
To do this, you must use the ap command (debug post mortem). With
this command you can debug a task after the Finish... requester. You
are asked for the number of the task which you want to debug. It would
probably be useful to have previously used the at command to view
the current system tasks. There you might see a line like:
3:
(OOOOa708)
Wait
Initial eLI
<div>
75
2.
C COMPILER OPERATIONS
rts'
76
2.S
ABACUS
2.5
2.5.1
77
2.
C COMPILER OPERATIONS
2.5.2
Dynamic arrays
The second programming example concerns the creation of dynamic
arrays. Let's say you want to store an arbitrary number of values. The
number of values to be stored is not detennined until the program is run
(by the user, for example).
Nonnally the user is given some restrictions on the use of the program.
Perhaps the maximum number of elements in a given array is limited
to 100. This is not particularly elegant, however. It would be better if
the maximum number of elements were limited only by available
memory. Here again pointers can help us.
First derme a pointer to the elements which make up the array:
int *array;
Now calculate how many array elements can be stored in this space:
NumElements = size/sizeof(*array);
You can now ask the user how many elements are used (elements).
Then you just have to allocate the memory:
array
Now you can access the individual array elements with array [x] and
they are stored in the allocated memory.
78
ABACUS
2.5.3
Function tables
Function tables are arrays which store the addresses of individual routines. Such an array is defined as follows:
void (*functions[4]) ();
79
2. C COMPILER OPERATIONS
int i;
functions[O] = FunctO;
functions [1] = Funct1;
functions [2] = Funct2;
functions [3] = Funct3i
i f (argc != 2)
{
(*functions [i]) () I
else printf ("Number of the function must be between 0\
- and 3\nn);
80
3.
Intuition
and
C
3.
ABACUS
3.
INTUITION AND
Intuition and C
This chapter concerns programming the Amiga operating system,
especially system programming using Intuition. What do we mean by
system programming? Here are some examples:
How can I tell the system that the user has made some inputs (with
the mouse or with the keyboard)?
These are two questions which come under the topic "system programming." There are certainly others, but this chapter answers these questions precisely.
Intuition is a type of communication (user interface) between you, the
user and the Amiga. By communication we mean any realtime exchange
of information. The keyboard and mouse are the most obvious input
devices-both are included with the computer, but there are certainly
other devices. Output occurs mainly to the video screen. There are also
printers and disks, but Intuition has no control over these devices.
Screens
What does Intuition offer? Let's look at what Intuition can do with
output. When booting with the Workbench disk you immediately see
two output displays offered by Intuition. The first and most important
is the screen, which forms the foundation of every output. An
important example is the Workbench screen, which appears when
booting. It accompanies the user through everything he does, and acts
as the basic output device for many programs.
Windows
Gadgets
Now we come to user input. Gadgets are the simplest input media.
They are found in both screens and windows. The simplest gadgets react
to a single mouse click. Naturally there are more complex types into
which you can enter text, but these involve keyboard access. First we'll
concentrate on mouse input, then look at keyboard input.
Menus were intended for elementary program-specific functions. They
are also controlled by the mouse, but the emphasis is on text selection,
while the gadgets are generally represented with graphics. There are,
however, exceptions to these rules.
83
3.
INTUITION AND
3.1
AMIGA
Windows
As we mentioned in the introduction to this chapter, windows are the
most important input/output medium.
your own programs with only a few changes. You should prepare a disk
for this data, since you'll be using the modules created throughout the
course of this book.
3.1.1
External
features
Sizes and positions of every window can be adjusted. You can set the
following values: X position (upper left comer). Y position (upper left
comer), width and height.
In addition, you can set the colors used to draw the window border and
the gadgets found within the window. based upon the screen colors. The
84
ABACUS
3.1
WINDOWS
Internal
features
Programinternal
features
Refreshing
window
displays
8S
3.
INTUITION AND
AMIGA
Borderless
windows
Refresh
Simple
Flag
Supported
Complete
SMART REFRESH
SUPER_BITMAP
SIMPLE REFRESH
Memory usage
None, since Intuition doesn't
bother with the window
Corresponds to hidden areas
The entire graphic in the
window is buffered
Your window doesn't always have to appear with the familiar border,
title line, gadgets and border lines.
Viewed at its lowest level, a window is just an area on the screen that
separates its contents from other areas present. The border which
Intuition draws merely acts as a visible indicator of the border. If all the
windows on the Workbench were drawn without this border, it would be
very confusing to the user.
However, borderless windows can serve a practical purpose in many
applications. You can prevent the border from being drawn when setting
up a new window by setting the BORDERLES S flag. Just be sure that a
borderless window won't confuse the user.
86
ABACUS
BACKDROP
windows
3.1 WINDOWS
The title of this paragraph refers to a window which no longer has all
the properties of a normal window. It will always be the window
farthest back, and can be overlapped by any other windows. The user
cannot bring this window to the foreground. This window has no front
or back gadgets-only the close gadget is allowed.
You can use this BACKDROP window type as the basis for any
application. This BACKDROP window can hold the background for an
animation program, or can display program status information, or even
hold gadgets for accessing program tools. The BACKDROP property
supports these applications, since a new window cannot be placed
behind it. Intuition manages the BACKDROP window, so further
programming is unnecessary. If you want to use this window type, you
must set the BACKDROP flag.
An interesting combination would be a BORDERLESS-BACKDROP
window, which would be very useful for the examples just mentioned.
But other types can be combined as well.
An extra
Earlier we described how to create a window which had no border. Here
window border we describe the opposite situation. You know that a normal window is
just a rectangular area with a visual border. A few problems result from
this: If you write a drawing program which uses a normal window, you
can easily overwrite the title bar, the gadgets or the border. The window
only prevents you from drawing outside of the window's border-the
rest of the window is open game.
GIMMEZERO
ZERO
How do you cure this problem? The simplest way; avoid drawing
outside the window's inner surface. But let's be realistic, users don't
blindly accept the universal programmer's instruction, "Don't do it."
The developers of Intuition created GIMMEZEROZERO mode to solve
this problem. This mode changes some window settings. The window
consists of two fields: One field contains all of the gadgets, the title bar
and the border. The other field is the surface in which the user can draw
without crossing over into gadgets or border art. The name
GIMMEZEROZERO comes from the fact that this inner window always
has the upper left comer coordinates 0,0, regardless of the size and location of the window.
This mode requires more memory and processing time. Also, the portion in which the drawing program can operate has different dimensions
from those specified in the window definition. This window type needs
two additional variables which you read to find out the size of the actual
drawing surface. These variables are called GZ Z He i g h t and
G Z Z wid t h . This area lies within the following values:
BorderLeft, BorderTop, BorderRight and BorderLeft.
Since the mouse coordinates of a window always refer to the entire
window area, a GIMMEZEROZERO window can cause certain problems.
Intuition also has relative mouse coordinates to the GIMMEZEROZERO
window: GZZMouseX and GZZMouseY.
87
3.
INTUITION AND
Scrolling data
AMIGA
System
gadgets
Gadgets are fields which can be activated by moving the mouse pointer
onto the gadget and clicking the left mouse button. Intuition offers four
gadgets as a default set. These four system gadgets support functions
which cannot normally be handled by the program. System gadgets also
have a variety of properties. Their positions and appearance are preset;
only the color can be changed. They can always be recognized by the
user in any program, and the user doesn't have to guess what they
mean. Let's look at each of them.
Close gadget
The close gadget closes a window. You are familiar with the procedure
from the Workbench: You click on the gadget in the upper left corner of
the window to close the window. The close gadget appears as a box
with a large point in its center.
Drag bar
The drag bar appears as three heavy lines in the title bar of a window.
You can use it to change the position of the window. Move the mouse
pointer onto the drag bar and press and hold the left mouse button. As
long as you hold down the mouse button the window moves with the
mouse pointer.
Front and
back gadgets
These gadgets always come as a pair. They change the priority of overlapping windows. The front and back gadgets are found in the upper
right comer. The light rectangular surface is the important part: It represents the current window's new position.
Sizing gadget
rectangles. Click on it and press and hold the left mouse button. Now
moving the mouse changes the window's size. The new size can only
be set within the predefmed minimum and maximum sizes.
Since the sizing gadget enlarges the border of the window, the area in
which the Intuition gadgets are located. the programmer must also specify where this border is taken from the actual output window. The right
border or lower border are possibilities, depending on whether you want
to have more rows or more columns in the window. It is also possible
to reserve both areas for Intuition.
88
3.1
ABACUS
WINDOWS
gadget but that Intuition doesn't close that window. The reaction to the
close gadget depends on the program controlling the window. Just as
the program receives a message when the user wants to close the window, it also receives a message when the size of the window has been
changed, since this must be taken into account by the program.
3.1.2
3.1.2.1
89
3.
INTUITION AND
AMIGA
How it works
function. You may want to open more than the Intuition library, in
which case type conversion may cause problems.
The if test first calls the function itself. Then its return value (the
pointer to the Intuition functions) is tested for validity. This value must
be something other than null, in which case it represents the memory
address at which the library begins. A null value indicates an error. If
the if condition is true, the program displays a message in the
AmigaDOS window and exits.
Now you need to know how to conclude library access. When you
actually obtain access (and only then) you can simply close the library
again with the EXEC function CloseLibraryO:
CloseLibrary(LibraryPointer)
-414
Al
3.1.2.2
struct NewWindow
{
Oxoo
Ox02
Ox04
Ox06
90
00
02
04
06
SHORT
SHORT
SHORT
SHORT
LeftEdge;
Top Edge;
Width;
Height;
3.1 WINDOWS
ABACUS
Ox08
Ox09
OxOa
OxOe
Ox12
Ox16
Ox1a
Ox1e
Ox22
Ox26
Ox28
Ox2a
Ox2c
Ox2e
Ox30
08
09
10
14
18
22
26
30
34
38
40
42
44
46
48
UBYTE DetailPen;
UBYTE BlockPen;
ULONG IDCMPFlags;
ULONG Flags;
struct Gadget *FirstGadget;
struct Image *CheckMark;
UBYTE *Title;
struct Screen *Screen;
struct BitMap *BitMap;
SHORT MinWidth;
SHORT MinHeight;
USHORT MaxWidth;
USHORT MaxHeight;
USHORT Type;
may wonder why numbers precede each variable name in the variable
declaration. These numbers indicate the offset of each variable from the
start of' the structure. This can be helpful when working with the
debugger, or if you are using assembly language. The numbers do not
coincide with the actual C structure declaration.
The ftrst column contains the offsets in hexadecimal notation, and the
second in decimal notation. The last number indicates the total length
of the structure.
All library communication occurs in predefmed functions to which you
pass variables (e.g., OpenLibrary and CloseLibrary). Intuition,
like most other libraries, uses structures of its own for various management pwposes.
Creating a
NewWindow
structure
91
3.
INTUITION AND
AMIGA
If you simply jump to the close routine, it might try to close things
which weren't open in the first place. This guarantees a system crash.
So, let's write a close function which examines the pointers obtained
during initialization. This function should only close open items, and
leave everything else alone. Let's look at the two functions:
/***************************************/
/*
*/
/* 3.1.2.2.B
Open_All.c
/*
*/
*/
===================================
/* Author:
/*
Date:
Comment:
----------
/* Wgb
16.10.1987
----------
Intuition only
/*
*/
*/
*/
*/
*/
*/
*/
/***************************************/
*OpenLibrary () ;
if (! (IntuitionBase = (struct IntuitionBase *)
OpenLibrary (n intuition .library", OL)
{
/***************************************/
/*
*/
/*
3.1.2.2.C
Close_All.c
/*
*/
*/
===================================
/* Author:
/*
/* Wgb
Date:
----------
16.10.1987
/*
Comment:
----------
Intuition only
*/
*/
*/
*/
*/
*/
*/
/**************************************/
if (IntuitionBase)
CloseLibrary(IntuitionBase);
How it works
92
The first function should already look familiar to you. The contents of
the second function are almost silly, but the important part is the i f
test The program would crash without this test if the Intuition library
could not be opened for some reason.
3.1
ABACUS
WINDOWS
Both functions look quite simple at the moment, but the basic form
will continually expand as we continue. Enter them carefully and save
them both on your program disk and in a subdirectory created specifically for storing function blocks. Later, when either of them is required,
we will not present the entire listing, just references to the existing part
and the additions required.
Now you can start writing your first program to open and close a
simple window. Let's specify the properties for your window:
Window size
The position and size of the window don't matter too much for your
test. Just be sure that the width and height are large enough to fit
gadgets and the window border. We set ours at position 160, 50 with
the dimensions 320, 150. Write the values in a Newwindow variable
called FirstNewWindow:
FirstNewWindow.LeftEdge
FirstNewWindow.TopEdge
FirstNewWindow.Width
FirstNewWindow.Height
= 160;
= 50;
= 320;
= 150;
Since you don't want to omit the sizing gadget, set the screen dimensions to the maximum. Remember, the minimum shouldn't be too
small.
FirstNewWindow.LeftEdge
FirstNewWindow.TopEdge
FirstNewWindow.Width
FirstNewWindow.Height
= 160;
= 50;
= 320;
150;
Since you don't know how to manage screens on your own yet, use the
Workbench screen. The window type is set and you won't have to fmd a
pointer:
FirstNewWindow.Type
FirstNewWindow.Screen
= WBENCHSCREEN;
=
NULL;
Title bar
Now let's get to the graphic details. First you have the window title.
You can pass a pointer to a string ending with a null, or you can pass a
null pointer to indicate an untitled window. If no system gadget is
permitted, then no title bar is displayed.
When using the Aztec compiler a type cast should precede the string
to avoid a warning from the compiler.
FirstNewWindow.Title
93
3.
INTUITION AND
AMIGA
Two interesting and seldom used parameters are the two pen colors.
Depending on the screen, they can accept values from 0 to 31, which
naturally depends on the number of bit-planes. A special value is -1
(Oxff), which uses the default screen color values. This assigns all of
the windows in the screen the same colors, allowing global color
changes as well. The example uses Workbench color values, but not the
default values. This makes the new window independent of the
superordinate screen's parameters, even if they currently contain the
same values.
FirstNewWindow.DetailPen
FirstNewWindow.BlockPen
= 0;
= 1;
You can only initialize two more parameters. These two flag parameters
present so many options that we must layout descriptions of each flag.
They can be divided into six basic groups: Gadget flags, sizing gadget
position, refresh flags, window type flags, mouse flags and miscellaneous flags.
Gadget flags
Sizing gadget
position
These flags must be set according to the system gadgets you want in
the window.
WINDOWDRAG
WINDOWDEPTH
WINDOWCLOSE
WINDOWS I ZING
Refresh nags
SIZEBRIGHT
SIZEBOTTOM
94
ABACUS
3.1 WINDOWS
SMART REFRESH
Here Intuition buffers every hidden area of the window and then restores the area when made visible.
This causes problems only when resizing windows.
If you make the window smaller, the eliminated
portions are not buffered and not restored.
SUPER BITMAP This type of refresh uses the most memory. In
some cases you may need to store a large graphic
and display it in a window. This is both a window
type and a refresh mode, in that the window can
always be restored by the information contained in
the bit-map, and it also manages graphics larger
than the display window.
NOCAREREFRESH
We have already discussed various window types. Here are the flag
names with brief comments:
This window always has lower priority than other
windows. Only one of these windows should exist
per screen.
BACKDROP
BORDERLESS
GIMMEZEROZERO
95
3. INTUITION AND C
RMBTRAP
AMIGA
Miscellaneous This last flag determines whether or not the window is active when
open.
ACTIVATE
All of these flags are represented as set or cleared bits in the flag entry
of the NewWindow structure. When programming in C you can use
the symbolic names. But for assembly language programmers and those
who want more information, you need the hex values for the flags.
Here's a table of the flags and their values:
Table 3.2:
Window flags
FlaR Name
WINDOWS I ZING
WINDOWDRAG
WINDOWDEPTH
WINDOWCLOSE
Hex Value
OxOOOOOOO1L
OxOOOOOOO2L
OxOOOOOOO4L
OxOOOOOOO8L
GadRet ~roul>
System gadgets
SIZEBRIGHT
SIZEBBOTTOM
OxOOOOOO1OL
OxOOOOOO20L
Gadget position
NOCAREREFRESH
SIMPLE REFRESH
SMART REFRESH
SUPER- BITMAP
OxOOO20000L
OxOOOOOO40L
OxOOOOOOOOL
OxOOOOOO80L
Refresh types
BACKDROP
GIMMEZEROZERO
BORDERLESS
OxOOOOO1OOL
OxOOOOO400L
OxOOOOO800L
Window types
REPORTMOUSE
RMBTRAP
OxOOOOO200L
OxOOOlOOOOL
Mouse flags
ACTIVATE
OxOOOO1OOOL
Miscellaneous
= WINDOWDEPTH
I WINDOWSIZING
WINDOW DRAG I WINDOWCLOSE I
SMART_REFRESH;
Now that you have some knowledge of the simple flags, we'll spend a
little time looking at the IDCMP (Intuition Direct Communication
Message Port) flags. First we need a definition of the IDCMP.
96
3.1 WINDOWS
ABACUS
NULL;
*/
/*
3.1.2.2.0
First_window.c
*/
/*
*/
*/
*1
1*
==================================~
1*
*/
/* Author:
/*
1* Wgb
1*
DAte:
----------
Comment:
*/
----------
*/
*1
*/
/*
*1
/***************************************/
10/16/1987
first test
window
iinclude <exec/types.h>
#include <intuition/intuition.h>
struct IntuitionBase *IntuitionBase;
struct Window
*FirstWindow;
struct NewWindow FirstNewWindow;
main 0
{
FirstNewWindow.LeftEdge
FirstNewWindow.TopEdge
= 160;
=
50;
97
3.
INTUITION AND
FirstNewWindow.Width
FirstNewWindow.Height
FirstNewWindow.DetailPen
FirstNewWindow.BlockPen
FirstNewWindow.IDCMPFlags
FirstNewWindow.Flags
320;
150;
0;
1;
NULL;
WINDOWDEPTH I WINDOWSIZING
I WINDOWDRAG I WINDOWCLOSE
I SMART_REFRESH;
FirstNewWindow.FirstGadget= NULL;
FirstNewWindow.CheckMark = NULL;
FirstNewWindow.Title = (UBYTE *) "System test";
FirstNewWindow.Screen
= NULL;
FirstNewWindow.BitMap
= NULL;
FirstNewWindow.MinWidth
100;
FirstNewWindow.MinHeight
50;
640;
FirstNewWindow.MaxWidth
200;
FirstNewWindow.MaxHeight
WBENCHSCREEN;
FirstNewWindow.Type
Delay(180L);
1***************************************
*
*
* Function: Open library and window
*
* =================================--= *
*
Author:
Date:
Comment:
---------- ----------
*
* Wgb
*
*
10/16/1987
*
*
*
*
*
*
**************************************1
void
*OpenLibrary();
struct Window *OpenWindow();
if (! (IntuitionBase = (struct IntuitionBase *)
OpenLibrary("intuition.library", OL)
{
98
3.1 WINDOWS
ABACUS
/***************************************
*
*
Author:
*
*
* Wgb
Date:
Comment:
10/16/1987
*
*
just Intuition
and window
*
*
*
*
*
*
*
*
*
**************************************/
if (FirstWindow)
if (IntuitionBase)
CloseWindow(FirstWindow);
C1oseLibrary(IntuitionBase);
How it works
The listing contains a few things that may be unfamiliar to you. First,
the Open AIIO function has been modified to open the window, and
Close_ A1IO has been correspondingly modified to close the window.
How does a window open once you define a NewWindow structure? It
is very important that the structure be defined before the mainO function, otherwise the structure values are not available to the other functions. The OpenWindow simply passes the address of the definition
structure (NewWindow). As the return value you get a pointer to the
window structure which was created based on your data. It also contains
additional management information. OpenLibrary must be checked
to ensure there were no errors (you can't assume that everything worked
properly with OpenwindowO). There are many things that could cause
an error in OpenWindowO, such as running out of memory. The
Newwindow structure can then be erased again. It is only needed for
initialization.
WindowPointer
DO
= OpenWindow(NewWindowStructure)
-204
AO
Closing the window has also been added to the Close_AIIO function.
The structure is similar to what you have seen already. The important
thing is that the window closes first, and then the Intuition library. If
these actions were reversed, the operating system would no longer be
able to find the ClosewindowO function and the program would
crash.
CloseWindow(WindowPointer)
-72
AO
99
3.
INTUITION AND
AMIGA
the task of your program into wait status until the specified time has
elapsed (time is specified in "ticks", 60 ticks per second), since this
program doesn't have the ability to read the close gadget in order to
close the window again. Instead of the DelayO function you could also
use a for loop to achieve the delay. But this would have slowed down
the multitasking even though you were only waiting.
Let's look at the start of the program and its comment blocks. Before
the mainO function you define two structures which you need for your
window. The first is the NewWindow structure under the name
FirstNewWindow. This allocates space for the information which
Intuition requires to set up your window. The second structure is a
pointer to a window structure. A Window structure contains much
more information than a Newwindow structure, and is linked to other
windows in the screen. The construction of the system which Intuition
sets up and manages looks like this:
"Ground zero" is a screen, generally the Workbench screen. This
Screen structure, explained in the next section, contains a pointer to
the first window in the screen. The Window structure itself has a
pointer to the following windows. If there are several screens, then the
frrst screen also stores a pointer to the next screen, which can contain
the same window chaining. Here is a figure to clarify the structure'
ScreenlWindOM-Linking
Figure 3.1
The structure definition described above must have access to the
operating system's Intuition functions in order for our new functions to
work. These can only be accessed once the library has been opened. You
must know the address of the library, stored in the pointer
*IntuitionBase.
100
3.1 WINDOWS
ABACUS
date the first lines were written. A new date represents a fundamental
change in the routine. The purpose of the program or function is also
explained in the header.
3.1.2.3
OxOO
Ox04
Ox06
Ox08
OxOa
OxOc
OxOe
OxlO
Ox12
Ox14
Ox16
Ox18
OxIc
Ox20
Ox24
Ox28
Ox2C
Ox2E
Ox32
Ox36
Ox37
Ox38
Ox39
Ox3A
Ox3E
Ox42
Ox46
Ox4A
Ox4E
Ox4F
Ox50
Ox51
Ox52
Ox56
Ox5A
Ox5E
Ox62
Ox63
Ox64
Ox68
00
04
06
08
10
12
14
16
18
20
22
24
28
32
36
40
44
46
50
54
55
56
57
58
62
66
70
74
78
79
80
81
82
86
90
94
98
99
100
104
101
3.
INTUITION AND
Ox6C
Ox6E
Ox70
Ox72
Ox74
Ox78
Ox7C
Ox80
Ox84
};
AMIGA
108
110
112
114
116
120
124
128
132
SHORT GZZMouseX;
SHORT GZZMouseY;
SHORT GZZWidth;
SHORT GZZHeight;
UBYTE *ExtData;
BYTE *UserData;
struct Layer *WLayer;
struct TextFont *IFont;
LeftEdge,TopEdge
Position of the window on the screen, as defined in
Newwindow.
Width, Height Dimensions of the window, as defined in
Newwindow.
MouseX,MouseY
*MenuStrip
*Title
*FirstRequester
Pointer to the first requester set in this window (see
Section 3.5).
*DMRequest
102
3.1
ABACUS
WINDOWS
ReqCount
*WScreen
*RPort
PtrHeight,PtrWidth
Mouse pointer size (X value cannot exceed 16).
XOffset,YOffset
Offsets specifying point in the pointer representing
the click point.
IDCMPFlags
*UserPort
*WindowPort
*MessageKey
DetailPen,BlockPen
Pen colors as defIned in NewWindow.
*CheckMark
* ScreenTi tle Pointer to the string for the screen title (can only
be set by the function SetWindowTitles()).
See explanation below.
GZZMouseX,GZZMOuseY
Mouse coordinates for a GIMMEZEROZERO
window (border is automatically taken into account
and subtracted from the normal value).
103
3.
INTUITION AND
GZZWidth,GZZHeight
Window size for a GIMMEZEROZERO window.
*ExtData
*UserData
*WLayer
*IFont
Intuition-WindoH-Linking
I
I
FirstRequester
YMiR...ester
4j First6adget .
Figure 3.2
RastPort
BorderRastPort
All of these values allow easy access. If you want to know the width of
your GIMMEZEROZERO window, for example, you simply enter the
following in your source code:
width
= FirstWindow->GZZWidth;
You can also change a given value. Perhaps you want to use a different
graphic for the checkmark which appears next to selected menu items.
Just enter the following in your source code:
FirstWindow->CheckMark = NewCheckMark;
104
3.1 WINDOWS
ABACUS
160, 50,
320, 150,
0, I,
1*
1*
1*
1*
1*
LeftEdge, TopEdge
Width, Height
DetailPen, BlockPen
IDCMP Flags
Flags
NULL,
WINDOWDEPTH
WINDOWSIZING I
WINDOWDRAG I
WINDOWCLOSE I
SMART_REFRESH,
NULL,
1* First Gadget
NULL,
1* CheckMark
(UBYTE *)"System test",
NULL,
1* Screen
NULL,
1* BitMap
100, 50,
1* Min Width, Height
640, 200,
1* Max Width, Height
WBENCHSCREEN,
1* Type
.;
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
*/
*1
The advantage is clear: you have much less to type in because the
structure name appears only once. It's up to you whether or not you
want to include the comments.
The structure definition must precede the mainO function. This makes
it a global variable available to all functions. If you want to change a
few values you can do so by accessing each element through its name.
Note that when initializing structures in this manner the order dictates
what you are actually initializing. Changing things around can easily
lead to a crnsh.
105
3.
INTUITION AND
3.1.2.4
AMIGA
Changing
window titles
Let's take a look at the rest of the window functions. The first is
SetWindowTitles(). This lets you change the title of a window
after it has been configured. To demonstrate this, insert the following
lines into the First_Window. c program:
1* 3.1.2.4.A Setwindowstitle.c 1*
/* add after Delay(180L) */
SetWindowTitles(FirstWindow,
"New window title", "The screen has a title now too!");
Delay (180L) ;
As you can probably tell from the text, the window is assigned a new
title. Then the screen gets a title. When you click on a window the
screen title line changes appearance. In most cases the program starts
from the Workbench, and the screen doesn't have a special title. The
same is true for the CLI. The text "Workbench Screen" is simply placed
in the title line. You as the programmer can choose a different title for
your active window. The format of this command looks like this:
SetWindowTitles(Window, WindowTitle, ScreenTitle);
-276
AO
Al
A2
106
3.1
ABACUS
WINDOWS
document to appear as the title of the word processor window. In addition, most programmers also add the path. This lets you see the name
and path of the disk currently under access. The only way to give a title
to the screen is to use SetWindowTitlesO.
If you want to change one title parameter of SetWindowTitlesO
without changing the other parameters (e.g., if you want to change the
title of a window without changing the title of the screen), you can pass
-1 as the parameter instead of a pointer. This leaves the appropriate title
unchanged.
It is also possible to clear the title of either a window or the screen or
both. This is done by passing null to a string instead of a pointer.
Window
activation
The next window handling function first appeared in Version 1.2 of the
Intuition library. The ActivateWindowO function allows the
programmer to activate a window at any time during program
execution. The only argument passed to it is a pointer to the window
structure. Insert the following lines in the original version of the
Fir s t Win do w c program (not including the addition of
SetWindowTitlesO as entered above):
/* 3.1.2.4.B.activatewindow.c*/
/* place after Delay(180L) */
ActivateWindow(FirstWindow);
Delay(180L);
This function is important for input, which occurs only in the active
window. If a program checks to see if a window is active which is not
allowed to be active because no inputs are allowed, it can activate
another window. This prevents the user from entering information in
that window. See Section 3.7 (Reading the rDCMP flags) for a sample
application.
Moving
windows
107
3.
INTUITION AND
AMIGA
Before you save the source code for this program remember to declare
the two delta variables as short, to match the window structure.
Access to the Amiga library requires 32 bit integers so be sure to use
the +L option of the Aztec C compiler to compile this program so all
integers are 32 bits long. When the new program is then started, the
newly opened window moves to the upper left comer of the screen after
a short pause.
Here is the fonnat of the new function:
MoveWindow(MyWindow, DeltaX, DeltaY);
-168
AO
DO
D1
Sizing
windows
108
3.1
ABACUS
WINDOWS
error can occur only if you have not selected a sizing gadget, and did not
have to set the minimum and maximum sizes.
The format is very similar to that of the MoveWindowO function:
SizeWindow(MyWindow, Oe1taX, Oe1taY);
-288
AO
DO
01
Make sure you don't use any illegal values for the deltas. The window
may not be made smaller or larger than the screen allows.
Limiting
window size
1* 3.1.2.4.E.windowlimits.c *1
1* insert after Delay(180L) *1
MinWidth
MinHeight
= FirstWindow->MinWidth;
= FirstWindow->MinHeight;
NewMaxWidth = 200;
NewMaxHeight= 100;
Success = WindowLimits(FirstWindow, MinWidth, MinHeight,
NewMaxWidth, NewMaxHeight);
De1ay(180L);
Before you can try this out, you must declare the limit values as
USHORT and the result value (Success) as BOOL. Be sure to use the
+L option of the Aztec C compiler to compile this program. The
routine returns 1RUE if values were all within the allowed limits.
If the maximum values are reset and the window is already larger than
the new limits, the program returns FALSE as the result. Here is the
format of the function call:
Result
DO
109
3.
INTUITION AND
AMIGA
A newly opened window is always placed in front of all others. The fact
Moving
that one window can cover all the others should be familiar to you from
windows to
front and back your daily use of Intuition. The programmer needs to know how to put
a window into the background when it is not currently needed, and then
returns it to the foreground. The user controls this procedure with the
front and back gadgets. There are two corresponding Intuition functions:
windowToBackO and windowToFrontO.
Both functions require just the pointer to the window structure as the
argument. Since you probably have the DOS window open when you
start all of these programs, you can see how these functions work. Add
the following lines to FirstWindow. c program:
/* 3.1.2.4.F.
windowt~back_front.c
*1
After a short delay the new window goes behind the large AmigaDOS
window then reappears. Here is the format:
WindowToBack(Window);
-306
AO
.
WindowToFront(Window);
-312
AO
3.1.3
110
3.1
ABACUS
3.1.3.1
WINDOWS
All-purpose windows
Each listing here suits a particular range of applications. You can easily
adapt the programs to your own needs by making a few changes to the
arguments.
About graphic Let's assume that the screen to be drawn on has the appropriate color
settings. We'll use the Workbench colors and screen. Section 3.2 shows
programs
you how to open your own screens.
Since no operating system functions support drawing graphics on a
screen, you have to use windows. The window should resemble a screen
as much as possible: maximum size, completely blank and moveable
up and down.
Your window can use the full size normally occupied by the screen. For
the blank surface, enable the BORDERLESS flag to prevent drawn
borders. If the program opens other windows, you will have problems,
because these windows may be placed behind your drawing window.
You won't be able to access them because your window has no border
or gadgets. Omitting the front and back gadgets contradicts good
programming sense, nor would it be user-friendly. Simply enable the
BACKDROP flag to have a window with the properties of a screen.
Here's part of the listing:
/* 3.1.3.1.A. GraphicWindow1.c *1
struct NewWindow FirstNewWindow =
{
0, 0,
640, 200,
0, 1,
NULL,
BACKDROP 1
BORDERLESS
SMART_REFRESH,
NULL,
NULL,
NULL,
NULL,
NULL,
0, 0,
0, 0,
WBENCHSCREEN,
};
1* LeftEdge, TopEdge
1* Width, Height
*1
*1
/* DetailPen, BlockPen *1
1* IDCMP Flags
*1
/* Flags
*1
1* First Gadget
/* CheckMark
/* Window Title
1* Screen
*1
*1
*1
*1
/* BitMap
/* Min Width, Height
/* Max Width, Height
*/
1* Type
*1
*1
*/
You won't find a complete listing printed here because it's unnecessary.
Simply use this structure in the First_Window. c program.
Let's look at the structure values: The position and dimensions correspond to the normal Workbench screen. The window contains no
111
3.
INTUITION AND
AMIGA
/* Wgb
1* JLD
----------
12/06/1987
01/06/1988
----------
BACKDROP
BORDERLESS
1*
*/
*1
*/
*1
/******************************************/
#include <exec/types.h>
#include <intuition/intuition.h>
struct IntuitionBase *IntuitionBase;
struct Window
*FirstWindow;
struct IntuiMessage *message;
struct NewWindow FirstNewWindow =
{
0, 0,
640, 200,
0, 1.
NULL,
/*
WINDOWCLOSE
BACKDROP I
BORDERLESS I
SMART_REFRESH,
NULL,
NULL,
NULL,
NULL,
NULL,
0, 0,
0, 0,
112
1* LeftEdge, TopEdge
*/
/* Width, Height
*/
/* DetailPen, BlockPen */
/* IDCMP Flags
*/
1* Flags
*/
1* First Gadget
/* CheckMark
/*Window Title*/
1* Screen
1* BitMap
/* Min Width, Height
1* Max Width, Height
*/
*/
*/
*/
*/
*/
3.1
ABACUS
WBENCHSCREEN,
};
/* Type
WINDOWS
*/
main ()
{
ShowTitle(FirstWindow->WScreen, FALSE);
Delay(180L);
exit (TRUE) ;
}
/***************************************
*
*
===================================
* Author:
*
*
*
Date:
Comment:
10/16/1987
Wgb
*
*
*
*
*
*
**************************************/
void
*OpenLibrary();
struct Window *OpenWindow();
if (! (IntuitionBase = (struct IntuitionBase *)
OpenLibrary("intuition.library", OL)
{
if (! (FirstWindow
= (struct Window *)
OpenWindow(&FirstNewWindow)
113
3.
INTUITION AND
AMIGA
/***************************************
*
*
* Function: Close everything opened *
* =================================== *
*
*
Comment:
* Author: Date:
*
*
*
---------- ---------10/16/1987 just Intuition *
* Wgb
and window
*
*
*
*
**************************************/
if (First Window)
if (IntuitionBase)
CloseWindow(FirstWindow);
CloseLibrary(IntuitionBase);
* =================================== *
*
*
* Author: Date:
Comments:
*
*
---------- ---------*
* Wgb
10/16//1987 close with
*
01/09/1989 a Gadget
*
* JLD
*
*
***************************************/
#include <exec/types.h>
#include <intuition/intuition.h>
struct IntuitionBase *IntuitionBase;
struct Window
*FirstWindow;
struct IntuiMessage *message;
struct NewWindow FirstNewWindow =
{
160, 50,
320, 150,
0, 1,
CLOSEWINDOW,
WINDOWDEPTH I
WINDOWSIZING I
WINDOWDRAG I
WINDOWCLOSE I
114
/*
/*
/*
/*
/*
LeftEdge, TopEdge
Width, Height
DetailPen, BlockPen
IDCMP Flags
Flags
*/
*/
*/
*1
*/
3.1
ABACUS
GIMMEZEROZERO I
SMART_REFRESH.
NULL.
1*
NULL.,
1*
(UBYTE *)"GIMMEZEROZERO
NULL.
1*
NULL.
1*
100. 50,
640. 200,
WBENCHSCREEN.
};
First Gadget
CheckMark
Test".
Screen
BitMap
1* Min Width. Height
1* Max Width. Height
1* Type
WINDOWS
*1
*1
*1
*1
*1
*1
*1
main ()
(
ULONG MessageClass;
USHORT code;
struct Message *GetMsg();
FOREVER
{
MessageClass = message->Class;
code = message->Code;
ReplyMsg(message);
switch (MessageClass)
{
1***************************************
*
*
* Function: Library and Window open *
* =================================== *
*
* Author:
*
*
*
wgb
Date:
Comments:
---------- ----------
10/16//19B9
*
*
*
*
*
***************************************1
115
3.
INTUITION AND
AMIGA
void
*OpenLibrary();
struct Window *OpenWindow();
if (! (IntuitionBase = (struct IntuitionBase *)
OpenLibrary("intuition.library". OL)
{
/***************************************
* =================================== *
*
* Author: Date:
*
---------10/16/1987
* Wgb
*
*
Comments:
----------
for Intuition
and Window '\
*
*
*
*
*
***************************************/
if (FirstWind6w)
if (IntuitionBase)
CloseWindow(FirstWindow);
CloseLibrary(IntuitionBase);
Before you compile and run the program, lets talk a bit about reading
the system gadgets. In order to get information about the gadget status
you need a Me s sag e structure, defined at the beginning of this
program. In addition you need a function to get messages for you. The
GetMsgO function returns a pointer to a message data structure. You
then divide this message into MessageClass, the origin of the
message, and Code. This Code is included in the message, but you
have no use for it yet. In a switch statement you then test to see if the
message is of type CLOSEWINDOW (a symbol defined in one of the
include files) and if so, the window is closed.
This method can be used for much more, but this will be handled in
several sections: Section 3.4 (Gadgets), Section 3.7 (Reading the
IDCMP flags) and Section 3.8.3 (Reading the menus).
116
3.1
ABACUS
WINDOWS
Both are assigned values in the FOREVER loop before the if test
Mx = FirstWindow->MouseX;
My = FirstWindow->MouseY;
After you have run the first version of the program, make the following
changes: Add the GIMMEZEROZERO flag to the NewWindow structure. Then declare the variables Gx and Gy as short and assign them
values after Mx and My as follows:
Gx = FirstWindow->GZZMouseX;
Gy = FirstWindow->GZZMouseY;
Now when you run the program you will see the difference between the
normal and GIMMEZEROZERO windows.
Note the amount of free memory space given in the Workbench display
when neither of the programs are running. Then start the first program
and write down the memory display value (you will see this when you
click somewhere on the Workbench surface with the left mouse button).
Then end the first program by clicking on the close gadgeL After starting the second program you wi1l notice that it requires more memory
than the first.
3.1.3.2
Editor window The first window allows text output and editing. We'll call this window
the editor window.
117
3.
INTUITION AND
File selector
The second window will be configured as a fIle selector box. This type
of box lists all filenames in a directory, and lets you select a fIle using
the mouse. Actually this involves a requester, but we will not be discussing gadgets and requesters until later. It is only important that you
have the file available so that you can use it.
Message
window
640, 200,
0, 1,
1*
1*
1*
1*
1*
LeftEdge, TopEdge
Width, Height
DetailPen, BlockPen
IDCMP Flags
Flags
NULL,
WINDOWDEPTH
WINDOWSIZING I
WINDOWDRAG I
WINDOWCLOSE I
SMART_REFRESH,
NULL,
1* First Gadget
NULL,
1* CheckMark
(UBYTE *)"System programming Editor",
NULL,
/* Screen
NULL,
1* BitMap
100, 50,
1* Min Width, Height
640, 200,
1* Max Width, Height
WBENCHSCREEN,
/* Type
);
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
The same parameters set for the editor window apply to the fIle selector
box, except that the file selector box doesn't take up the entire screen.
We only include the WINDOWDEPTH and WINDOWDRAG gadgets
because WINDOWCLOSE is replaced by another gadget, and sizing
would complicate the whole thing even more. Here is this structure:
118
3.1 WINDOWS
ABACUS
Structure 3.2:
File selector
box
/* 3.1.3.2.B.filewindow */
struct NewWindow FileWindow =
{
180, 25,
/* LeftEdge, TopEdge
250, 150,
/* Width, Height
0, 1,
/* DetailPen, BlockPen
NULL,
/* IDCMP Flags
WINDOWDEPTH I
/* Flags
WINDOWDRAG I
SMART_REFRESH,
NULL,
/* First Gadget
NULL,
/* CheckMark
(UBYTE *)"File-Select-Box",
NULL,
/* Screen
NULL,
/* BitMap
0, 0,
/* Min Width, Height
0, 0,
/* Max Width, Height
WBENCHSCREEN,
/* Type
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
};
The last window which you will always need for the text editor is the
message window. It mainly displays error messages, so it is somewhat
smaller than the file selector box. In addition, it doesn't contain any
system gadgets since it is read and then confirmed, not moved or
resized. The window structure is rather simple:
Structure 3.3:
Message
window
/* 3.1.3.2.C.msgwindow */
struct NewWindow MessageWindow =
{
250, 100,
140, 80,
0, 1,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
0, 0,
0, 0,
WBENCHSCREEN,
};
/*
/*
/*
/*
/*
/*
Width, Height
DetailPen, BlockPen
IDCMP Flags
Flags
First Gadget
CheckMark
/*
/*
/*
/*
/*
Screen
BitMap
Min Width, Height
Max Width, Height
Type
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
119
3.
INTUITION AND
3.1.3.3
AMIGA
Program 3.5:
NeweLl:
1****************************************1
1*
*1
1* 3.1.3.3.A.
CLled.c
*1
1*
*1
*1
/* Program: A new CLI Ed
/* ==================================== *1
1*
*1
Comments:
*1
1* Author: Date:
1*
*1
/* Wgb
*/
#inc1ude <exec/types.h>
#inc1ude <intuition/intuition.h>
struct IntuitionBase *IntuitionBase;
struct Window
*FirstWindow;
struct IntuiMessage *message;
struct NewWindow ConsoleWindow =
{
0, 0,
640, 101,
2, 3,
1* LeftEdge, TopEdge
1* Width, Height
CLOSEWINDOW,
WINDOWDEPTH I
WINDOWSIZING I
WINDOWDRAG I
WINDOWCLOSE I
ACTIVATE I
SMART_REFRESH,
NULL,
1* First Gadget
NULL,
1* CheckMark
(UBYTE *)"Wgb Prod. presents BECKERshell",
NULL,
1* Screen
NULL,
1* BitMap
120
*/
*/
/* DetailPen, BlockPen *1
1* IDCMP Flags
*/
1* Flags
*/
*1
*1
*1
*/
3.1 WINDOWS
ABACUS
640, 50,
640, 200,
WBENCHSCREEN,
};
*/
*/
*/
main 0
(
ULONG MessageClass;
USHORT code;
struct Message *GetMsg();
FOREVER
{
MessageClass = message->Class;
code = message->Code;
ReplyMsg(message);
switch (MessageClass)
{
1***************************************
*
*
* =============%=:=================== *
*
*
* Author:
* Wgb
Date:
Comments:
10/20/1987
* No parameters
*
*
*
*
*
*
***************************************/
121
3. INTUITION AND C
AMIGA
/***************************************
*
*
*
*
*
*
*
*
*
*
*
=================================== *
Author:
Wgb
Date:
Comments:
10/20/1987
No parameters
*
*
*
*
*
*
***************************************/
if (FirstWindow)
CloseWindow(FirstWindow);
if (IntuitionBase)
CloseLibrary(IntuitionBase);
122
3.2
ABACUS
3.2
SCREEN FUNDAMENTALS
Screen Fundamentals
The last section described many parameters which affect the appearance
of windows. Screens play an even more important role in graphic
display. Each window parameter depends on the screen to some extent.
Window color, resolution and position can be affected by the screen, and
base their definitions on the context of the screen.
What purpose does the screen serve in Intuition? The screen contains
the essential display parameters. It acts as the background for all visual
output But output has different uses, and there are many different basic
assumptions. Let's take two really crude examples:
1.)
2.)
These two examples make it clear that different basic properties have to
be available to program. The Amiga goes one step farther here. It
allows the programmer to display several different modes using screens,
where only one might normally be displayed.
Intuition allows any number of these screens with any number of
configuring modes. In principle the procedure is similar to that used for
windows. The following pages explain this in more detail.
3.2.1
3.
INTUITION AND
3.2.1.1
The
AMIGA
NewScreen
structure
Lets take a look at this NewScreen structure and its individual parameters. Here it is:
1* 3.2.1.1.A.newsereen_struet *1
struet NewSereen
{
OxOO
Ox02
Ox04
Ox06
Ox08
OxOa
OxOO
OxOe
OxOe
Ox10
Ox14
Ox18
Ox1e
Ox20
};
00
02
04
06
08
10
11
12
14
16
20
24
28
32
SHORT LeftEdge;
SHORT TopEdge;
SHORT Width;
SHORT Height;
SHORT Depth;
UBYTE DetailPen;
UBYTE BloekPen;
USHORT ViewModes;
USHORT Type;
struet TextAttr *Font;
UBYTE *DefaultTitle;
struet Gadget *Gadgets;
struet BitMap *CustomBitMap;
The Depth setting of a screen appears as a new argument Here you set
the number of bit-planes allocated for a screen. This determines both the
number of colors that can be displayed at once and the amount of
memory required. We are interested mainly in the number of colors,
calculated according to the formula Colors = 2"Depth.
Let's look at the parameters DetailPen and BlockPen. These
determined the colors which Intuition used to draw windows. Carried
over to the screen, they set the colors used by Intuition when drawing
the title bart the front gadget and back gadget.
It should be noted that only colors allowed by the Depth can be seL
For example, if you set a Depth of three bit-planes, then the
maximum number of colors allowed is eight (numbered zero to seven).
124
ABACUS
NewScreen
As we mentioned before, different types of programs have different res0lution requirements. Intuition has a maximum resolution of 320 pixels
in the X direction in normal cases. This means that in the rectangular
space of your screen there are 320 horizontal points. These pixels are
relatively wide and can easily be seen by the eye. In higher resolution
the pixel width splits in half, and you have 640 horizontal points.
Flat name
OxOOOO8000L
OxOOOOOOO4L
Description
Screen with double X resolution
Screen with double Y resolution
HAM
OxOOOOO800L
EXTRA_HALFBRITE OxOOOOOO80L
HIRES
LACE
Hex value
PFBA
OxOOOOOO40L
DUALPF
SPRITES
OxOOOOO400L
OxOOOO4000L
VP_HIDE
OxOOOO2000L
GENLOCK AUDIO
GENLOCK VIDEO
OxOOOOOlOOL
OxOOOOOOO2L
125
3.
INTUITION AND
Screen type
The next structure value sets the screen type. There are only two types:
the Workbench screen, opened by the system, and the CustomScreen
(user screen).
Associated with these two flags are three others which let you enter
special features. Intuition must organize memory for the bit-maps. You
can do this yourself, however, and set it with another parameter. To
inform Intuition that you have already allocated graphic memory, you
must set the third flag.
The fourth flag enables you to open the new screen behind all others.
This lets you draw a graphic while the screen is hidden without having
to fIrst open the screen and then move it to the background.
The fIfth flag disables drawing the system gadgets and the title bar. In
the program you must then ensure that the right mouse button cannot
be used. This can be done by setting a flag. Otherwise the title bar with
the menus would be drawn, but since deleting is disabled, it would not
be removed.
Here is a summary of the two screen types and the fIve flags just
described:
Table 3.4:
Screen type
Hex value
Description
FlaK name
WBENCHSCREEN OxOOOOOOOlL Screen type
CUSTOMSCREEN OxOOOOOOOFL
CUSTOMBITMAP
SCREENBEHIND
SCREENQUIET
SHOWTITLE
BEEPING
Just like each window, a screen has a title bar containing a text string.
You can set this title through a window. But if no windows are active,
the title bar must remain empty.
Text insertion The next two parameters allow basic text to be displayed. First, you can
use Font to point to a TextAttr structure, from which you
determine the font used for the screen title and all other Intuition text
displayed in the screen. If you want to use the font which the user
selected with Preferences, use the value null (0) instead of a
pointer here.
The second value is simply a pointer to a string. If you use null here,
no title appears in the title bar.
All we can say about the pointer to the gadget structure is that it was
included in the NewScreen structure for upward compatibility. Unfor-
126
3.2
ABACUS
SCREEN FUNDAMENTALS
tunately, you can't add custom gadgets to a screen. You should always
initialize this value as null.
The last pointer normally points to a Bi tMap structure initialized by
the program itself. If you don't use this function, you should use the
value null here.
3.2.1.2
Program 3.5:
Window on
custom screen
1***************************************1
1*
*1
1* 3.2.1.2.A customscreen_1.c
*1
1*
*1
1* Program: Window on Custom Screen
*1
1* =================================== *1
1*
*1
Comments:
1* Author: Date:
*1
1*
*1
10/16/1987 simple Screen
1* Wgb
*/
*/
1*
*/
1*
/***************************************/
finclude <exec/types.h>
#include <intuition/intuition.h>
struct
struct
struct
struct
IntuitionBase
Screen
Window
IntuiMessage
*IntuitionBase;
*FirstScreen;
*FirstWindow;
*message;
1,
0, 1,
HIRES,
CUSTOMSCREEN,
NULL,
(UBYTE *)"Screen Test",
NULL,
NULL,
/* LeftEdge, TopEdge
/* Width, Height
/* Depth
1* DetailPen, BlockPen
/* ViewModes
/* Type
/* Font
*/
*/
*/
*/
*/
/* Gadgets
/* CustomBitMap
*/
*/
*1
*/
127
3.
INTUITION AND
};
1*
1*
1*
1*
1*
160, 50,
320, 150,
0" I,
LeftEdge, TopEdge
Width, Height
DetailPen, BlockPen
IDCMP Flags
Flags
CLOSEWINDOW,
WINDOWDEPTH I
WINDOWSIZING I
WINDOWDRAG I
WINDOWCLOSE I
SMART_REFRESH,
NULL,
1* First Gadget
NULL,
1* CheckMark
(UBYTE *) "Test Custom-Screen",
NULL,
1* Screen *1
NULL,
1* BitMap
100, 50,
1* Min Width, Height
640, 200,
1* Max Width, Height
CUSTOMSCREEN,
1* Type
};
main ()
(
ULONG MessageClass;
USHORT code;
struct Message *GetMsg();
FOREVER
{
MessageClass = message->Class;
code = message->Code;
ReplyMsg(message);
switch (MessageClass)
{
128
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
3.2
ABACUS
SCREEN FUNDAMENTALS
/***********************************************
* =========================================== *
*
*
Comments:
* Author: Date:
*
---------- ---------*
*
10/16/1987
* Wgb
*
*
*
*
*
***********************************************/
Open_All ()
(
void
*OpenLibrary();
struct Window *OpenWindow();
struct Screen *OpenScreen();
if (! (IntuitionBase = (struct IntuitionBase *)
OpenLibrary(lintuition.library", OL)
(
FirstNewWindow.Screen = FirstScreen;
if (! (FirstWindow = (struct Window *)
OpenWindow(&FirstNewWindow)
(
/***************************************
* =================================== *
*
*
Comments:
* Author: Date:
*
*
*
---------- ---------10/16/1987 Window, screen *
* Wgb
and intuition
*
*
*
*
***************************************/
129
3.
INTUITION AND
AMIGA
Close_All ()
{
if (FirstWindow)
if (FirstScreen)
if (IntuitionBase)
CloseWindow(FirstWindow);
CloseScreen(FirstScreen);
CloseLibrary(IntuitionBase);
How it works
You should be familiar with the structure of this listing. After the general pointer and structure definitions comes the main function which
calls the OpenAllO function, then branches to a loop which waits
until you click on the close gadget The CloseAllO function ends the
program. Let's look at the four main sections:
Among the pointers we find one for screen structure. It places the
window later. First you need the NewScreen structure, which opens a
simple screen which has the same properties as the Workbench screen,
with the exception that you request only two colors (i.e., one bitplane).
You should recognize the ma~n program since we explained how 10
read the close gadget at the end of the last section.
The Open AllO function includes a screen opening routine. The order
of openingl.s as follows: Library (no library, no openScreenO function), screen. window. A pointer to the custom screen appears in the
window.
The Close AII0 function closes the window ftrst, then the screen,
then the Iibffiry. Everything occurs in the reverse of the order of the
opening process.
If you like, you can change one or two of the arguments of the
NewWindow or NewScreen structure. Don't make too many changes.
at once though, since it's easy to overlook an error.
3.2.1.3
OxOO
Ox04
OxOS
OxOA
130
00
04
08
10
ABACUS
OxOC
OxOE
Ox10
Ox12
Ox14
Ox16
Ox1A
Ox1E
Ox1F
Ox20
Ox21
Ox22
Ox23
Ox24
Ox25
Ox26
Ox27
Ox2B
12
14
16
18
20
22
26
30
31
32
33
34
35
36
37
38
39
43
SHORT Width;
SHORT Height;
SHORT MouseY;
SHORT MouseX;
USHORT Flags;
UBYTE *Title;
UBYTE *DefaultTitle;
BYTE BarHeight;
BYTE BarVBorder;
BYTE BarHBorder;
BYTE MenuVBorder;
BYTE MenuHBorder;
BYTE WBorTop;
BYTE WBorLeft;
BYTE WBorRight;
BYTE WBorBottom;
struct TextAttr *Font;
struct ViewPort ViewPort;
OxOO
Ox04
Ox08
OxOC
Ox10
Ox14
Ox18
Ox1A
Ox1C
Ox1E
Ox20
Ox22
Ox23
Ox24
Ox28
00
04
08
12
16
20
24
26
28
30
32
34
35
36
40
};
Ox53
83
OxOO
Ox04
Ox08
OxOC
Ox10
Ox14
Ox18
Ox19
Ox1A
Ox1B
Ox1C
OxlD
Ox1E
Ox1F
Ox20
Ox22
Ox24
Ox26
Ox28
00
04
08
12
16
20
24
25
26
27
28
29
30
31
32
34
36
38
40
131
3.
INTUITION AND
AMIGA
Ox30
Ox32
Ox34
Ox38
Ox39
Ox3A
Ox3C
Ox3E
Ox40
Ox42
Ox46
Ox4E
Ox5C
Ox64
48
50
52
56
57
58
60
62
64
66
70
78
92
100
SHORT PenWidth;
SHORT PenHeight;
struct TextFont *Font;
UBYTE AlgoStyle;
UBYTE TxFlags;
UWORD TxHeight;
UWORD TxWidth;
UWORD TxBaseline;
WORD TXSpacing;
APTR *RP_User;
ULONG longreserved[2];
UWORD wordreserved[7];
UBYTE reserved[8];
};
OxB7 183
OxOO
Ox02
Ox04
Ox05
Ox06
Ox08
Ox28
00
02
04
05
06
08
40
UWORD BytesPerRow;
UWORD Rows;
UBYTE Flags;
UBYTE Depth;
UWORD pad;
PLANEPTR Planes[8);
};
OxDF 223
OxOO
Ox04
Ox08
OxOC
Ox18
Ox3C
Ox4A
Ox4E
Ox50
Ox51
Ox52
Ox54
Ox58
Ox5C
00
04
08
12
24
60
74
78
80
81
82
84
88
92
};
Ox13B
Ox13F
Ox140
Ox141
Ox143
Ox147
Ox14B
Ox14F
315
319
320
321
323
327
331
335
};
*NextScreen Like the windows, all open screens are linked together. The link pointer resides in this variable.
132
3.2
ABACUS
SCREEN FUNDAMENTALS
*FirstWindow
This value is initialized as in the NewScreen structure, with two additional flags:
SHOWTITLE
BEEPING
*Ti tIe
*DefaultTitle
BarVBorder
BarHBorder
WBorLeft
WBorRight
WBorBottom
*Font
133
3.
INTmTION AND
AMIGA
ViewPort
RastPort
Bi tMap
Layerlnfo
*FirstGadget
3.2.1.4
SaveColorO
*BarLayer
*ExtData
*UserData
For instance, Intuition functions support clicking on the front and back
gadgets. With ScreenToFrontO and ScreenToBackO you can
move any screen to which you have the pointer to the foreground or
background. To demonstrate this, remove the gadget test from the
screen example program listed above. The main function should then
look like this:
134
ABACUS
main ()
{
Delay(180L);
Close_All () ;
exit (TRUE) ;
)
The DelayQ line must also be inserted so that you notice that
something is happening when the new screen opens. Add all of the
extensions after the Delay(180L); line unless told otherwise. First an
example of ScreenToBackO and ScreenToFrontO:
ScreenToBack(FirstScreen);
Delay(180L);
ScreenToFront(FirstScreen);
This very simple example moves the screen to the background and the
foreground after a short delay. but it shows you how to use the
functions. Here is the general syntax with the arguments, registers and
function offset
ScreenToBack(Screen);
-246
aO
ScreenToFront(Screen);
-252
AD
The same possibilities which can be used for a custom screen can also
be used for the Workbench screen, of course. Normally the problem is
that you don't have a pointer to the Workbench screen. Another
function does this without using pointers:
WBenchToBack();
-336
WBenchToFront();
-342
You may be familiar with the following situation in BASIC: You try
to move out of the text area with the cursor in the editor and the screen
flashes and beeps. Intuition sends this flash as a warning. The following routine produces five warning signals in a row:
135
3. INTUITION AND C
To start the new program, fIrst declare the two loop variables i and j
as into The program then flashes the new custom screen fIve times.
If you want to flash all of the screens (e.g., if you don't know what
screen is currently in the foreground), then null can be used as the
pointer to tell Intuition that it should flash all of the screens:
DisplayBeep(NULL);
No
WorkBench
AO
Many applications may run out of graphic memory very quickly. This
cannot be corrected by expanding the memory because the chip RAM
area cannot be expanded beyond 512K.
The Amiga developers decided that a program would not need the Workbench screen if it opened a screen of its own. In these cases you can
close the Workbench screen. The only condition: No other programs
can be running which display data on a window in the Workbench
screen. If this condition is met, Intuition lets you close the Workbench
window with CloseWorkBenchO.
How can you start a program without using the Workbench? Unfortunately you cannot start your program from the CLI window because the
CLI is an independent program which outputs to the Workbench screen.
Therefore we will give the program an icon so that it can be called by
double-clicking on the icon.
Add the following lines to CustomScreenl. c program and save it
program, and remember
to link it with startup.o. (You may have to compile startup.h).
At the end of the Open_AllO function after opening the window add:
CloseWorkBench();
Next copy the CLI. INFO icon to CloseBench. INFO. Close all
programs that you started from the workbench, the CLI is a running
program, and start the Close Bench program. When you pull the
screen down you will notice that there is no longer a Workbench screen
behind it.
136
3.2
ABACUS
Moving
Screens
SCREEN FUNDAMENTALS
Each screen whose title bar is not hidden by a BACKDROP window can
be moved vertically by the user. As with windows, an Intuition
function allows screens to be moved. This is no problem with
MovescreenO and the corresponding delta values.
We have prepared two lines which move the screen back up if you
move it down. Insert the following lines in CustomScreenl. c
program in the FOREVER loop before the message test:
if (FirstScreen->TopEdge != 0)
MoveScreen(FirstScreen, 01, -11);
AO
DO
Dl
= GetScreenData(Buffer,
-426
AO
D1
Al
AO
These are all the Intuition functions which control screens. We hope the
examples helped you understand the functions.
137
3.
INTUITION AND
3.2.2
AMIGA
3.2.2.1
General examples
In this section we present two screen examples in the subject of graphic
programs. These examples may give you some ideas for your own
programming.
First is a super-high-resolution screen with just one bit-plane. Superhigh-resolution means a resolution of 640x400 pixels on an NTSC
monitor (or 640x512 on a PAL monitor). We chose just one bit-plane
because we use enough memory in the higher resolution. You must
remember that you only have 512K of graphic memory available
(CHIP_MEMORY).
Structure 3.3:
Super screen
0, 0,
640, 400,
1,
0, 1,
HIRES I
LACE,
CUSTOMSCREEN
SCREENQUIET,
NULL,
NULL,
NULL,
NULL,
};
1*
1*
1*
1*
1*
LeftEdge, TopEdge
Width, Height
Depth
DetailPen, BlockPen
ViewModes
*1
*1
*1
*1
*1
1* Type
*1
1* Font
*1
1* Gadgets
/* CustomBitMap
*1
*1
This mode is suited for digitized pictures and for super hi-res graphics.
Here is the complete example program:
138
3.2
ABACUS
SCREEN FUNDAMENTALS
/***************************************
* Program: Superscreen.c
*
* =================================== *
*
*
Comments:
* Author: Date:
*
*
*
---------- ---------10/16/87
one Screen
* Wgb
*
*
*
*
*
***************************************/
#include <exec/types.h>
#include <intuition/intuition.h>
struct
struct
struct
struct
IntuitionBase
Screen
Window
IntuiMessage
*IntuitionBase;
*FirstScreen;
*FirstWindow;
*message;
0, 0,
640, 400,
1,
0, 1,
HIRES
LACE,
CUSTOMSCREEN
SCREENQUIET,
NULL,
NULL,
NULL,
NULL,
};
/*
/*
/*
/*
/*
LeftEdge, TopEdge
Width, Height
Depth
DetailPen, BlockPen
ViewModes
*/
*/
*/
*/
*/
/* Type
*/
/* Font
*/
/* Gadgets
/* CustomBitMap
*/
*/
160, 50,
/* LeftEdge, TopEdge
320, 150,
/* Width, Height
0, 1,
/* DetailPen, BlockPen
CLOSEWINDOW,
/* IDCMP Flags
/* Flags
WINDOWDEPTH I
WINDOWSIZING I
WINDOWDRAG I
WINDOWCLOSE I
SMART_REFRESH,
/* First Gadget
NULL,
NULL,
/* CheckMark
(UBYTE *)"Test Custom-Screen",
NULL,
/* Screen
/* BitMap
NULL,
*/
*/
*/
*/
*/
*/
*/
*/
*/
139
3.
INTUITION AND
AMIGA
100, 50,
640, 150,
CUSTOMSCREEN,
};
*/
*/
*/
main ()
{
Open_All () ;
Delay(180L);
Close_All 0 ;
exit (TRUE) ;
}
/***********************************************
=========~============================~===
**
*
***********************************************1
Open_All 0
{
void
*OpenLibrary();
struct Window *OpenWindow();
struct Screen *OpenScreen();
if (!(IntuitionBase = (struct IntuitionBase *)
OpenLibrary("intuition.library", OL)
{
FirstNewWindow.Screen
FirstScreen;
140
3.2
ABACUS
SCREEN FUNDAMENTALS
1***************************************
***************************************1
Close_All ()
{
if (FirstWindow)
if (FirstScreen)
if (IntuitionBase)
CloseWindow(FirstWindow);
CloseScreen(FirstScreen);
CloseLibrary(IntuitionBase);
Partial screens In the second example we want to show you that you don't have to use
the full resolution. An interlace screen can also be initialized with 200
lines. It then takes up only half the monitor. It should be mentioned
that any other screen used along with an interlace screen also flickers.
Structure 3.4:
Utility screen
LeftEdge, TopEdge
Width, Height
Depth
DetailPen, BlockPen
ViewModes
Type
Font
*1
*1
*1
*1
*1
*1
*1
Gadgets
CustomBitMap
*1
*1
};
The structure presented here opens a screen in the lower 50 lines of the
Workbench screen. You might want to open a BORDERLESS window
here in which you could place gadgets for calling up various utility
functions. Be sure to adjust your window size so it fits on the screen.
3.2.2.2
141
3.
INTUITION AND
AMIGA
C FOR
ADVANCED PROGRAMMERS
/* 3.2.2.2.A. edscreen */
struct NewScreen EditorScreen =
0, 0,
/* LeftEdge, TopEdge
640, 200,
/* Width, Height
1,
/* Depth
1, 0,
/* DetailPen, BlockPen
HIRES,
/* ViewModes
CUSTOMSCREEN,
/* Type
NULL,
/* Font
(UBYTE *)"Abacus C Editor Screen",
NULL,
/* Gadgets
NULL,
/* CustomBitMap
};
142
*/
*/
*/
*/
*/
*/
*/
*/
*/
3.3
ABACUS
3.3
OUTPUT
Output
After having completed the frrst two sections of this chapter, you now
know almost every capability of Intuition screens and windows; how
they open and close; and how other functions interact with them.
But what use is a screen or window that appears on the screen and does
nothing? Except, perhaps, letting the user move it up or down.
At the beginning of this book we mentioned that windows are the basis
for all input and output. The screens simply exist to subdivide areas and
specify default properties of the windows. You know how to set up
screens and windows, but how do you control input and output? This
section discusses the subject of output.
The Amiga offers two types of output. The frrst type comes from the
output functions found in graphics.library. These are the basis
for all of the screen output functions. The second type is supported by
intuition.library. It offers only three output functions, but they
are flexible enough that all other elements can be constructed from
them. All user elements such as gadgets, requesters, and menus use the
three graphic functions supplied by Intuition.
You see how important it is to explore these functions. Later you will
see how easy it is to construct the most complex objects using the
building block system of Intuition.
In addition, you'll get to use the windows for a real application.
3.3.1
Text output
We'll go through the individual output options step by step. Let's look
at text output frrst, since it's the most important form of communication with the user.
When starting out, it is important to know the parameters your text can
have. They are easy to understand.
143
3.
INTUITION AND
3.3.1.1
AMIGA
The first mode takes into account what is drawn but not the
background. This displays the text lines only, without disturbing the
background. This mode is called JAMl because color 1 is "jammed"
into the graphic.
JAM2
In the second mode Intuition displays both the characters and the back-
ground. This mode is called JAM2 because both colors are drawn.
INVERSEVID
The third mode is similar to the second except that colors are
exchanged. You need INVERSEVID for the default colors, however,
which are designated with -1 (OxFF) and cannot be simply exchanged.
COMPLEMEN'r
Table 3.5:
DrawModes
Drawin! mode
JAMl
JAM2
INVERSEVID
COMPLEMENT
Description
Only pen 1 used
Both pens used
Like JAM2, with pens exchanged
Like JAMl set points cleared,-cleared set
With the last text parameter you can specify a character font as a
TextAttr structure.
3.3.1.2
144
ABACUS
3.3 OUTPUT
1* 3.3.1.2.A. intuitext_struct *1
struct IntuiText
{
OxOO
Ox01
Ox02
Ox03
OxOs
Ox07
OxOB
OxOF
Ox13
};
00
01
02
03
05
07
11
15
19
OBYTE FrontPen;
OBYTE BackPen;
OBYTE DrawMode;
SHORT LeftEdge;
SHORT TopEdge;
struct TextAttr *ITextFont;
OBYTE *IText;
struct IntuiText *NextText;
The last two elements of the structure need some explaination. IText
represents a pointer to a string terminated by null. With the pointer
Next Text, the system makes it possible to link several such
Int ui Text structures together into a chain. The advantage of this lies
in the call to produce the output. This capability means that you aren't
limited to a single line or color setting per call. This allows maximum
flexibility with just one function call.
3.3.1.3
OxOO
Ox04
Ox06
Ox07
Ox08
00
04
06
07
08
STRPTR ta_Name;
OWORD ta_YSize;
OBYTE ta_style;
OBYTE ta_Flags;
);
145
3.
INTUITION AND
AMIGA
Characters
Font size
TOPAZ SIXTY
Sixty
Eighty
TOPAZ EIGHTY
Height
At 640 pixels = 9 pixels
At 640 pixels = 8 pixels.
Flag name
FS NORMAL
FSF ITALIC
FSF- BOLD
FSF UNDERLINED
FSF EXTENDED
Hex value
OxOO
Ox04
Ox02
OxOl
Ox08
Meaning
No effect
Italic
Boldface
Underline
Extended characters
In the last value of the structure you use flags to specify additional
information about your character set. These flags are called font preference flags. We are interested in just two of these flags-the two which
indicate from where Intuition should get the font. You can use ROM
fonts or disk fonts. Since topaz.font is in ROM, you set the corresponding flag:
Table 3.8:
Font type
Flag name
FPF ROMFONT
FPF DISKFONT
FPF REVPATH
FPF_TALLDOT
FPF WIDEDOT
FPF PROPORTIONAL
FPF DESIGNED
FPF REMOVED
Hex value
OxOl
Ox02
Ox04
Ox08
OxlO
Ox20
Ox40
Ox80
Meaning
Get font from ROM
Get font from disk
146
3.3 OUTPUT
ABACUS
TOPAZ_EIGHTY,
FS_NORMAL,
FPF_ROMFONT
};
3.3.1.4
PrintIText
Now that you've seen the individual parameters, it's time to work with
the IntuiText structure.
The P r i n tIT ext function is required for displaying the
IntuiText. In addition, you need an argument from the window in
which the output appears. Your argument is the RastPort, without
this it is impossible to perform any output.
You can do this with the following definition and assignment:
struct RastPort *MyWindowRastPort;
MyWindowRastPort = MyWindow->RPort;
1*
1*
1*
1*
FrontPen, BackPen
DrawMode
LeftEdge, TopEdge
15, 0,
Font (Standard)
NULL,
(UBYTE *) "System programming on the Amiga!",
1* NextText
NULL
1, 0,
JAM2,
*1
*1
*1
*1
*1
};
You are almost ready to call PrintITextO. All you need yet are
coordinates.
147
3.
INTUITION AND
You may wonder why you need them. Take a look at the following
graphic:
B 59 199
PrintIText()Offset-Defto
9~iiii~==========:3~.
Offset of window from function (S9l, Sal)
50
190
Itt:"'"
T
Relative position of structure
(2B, 20)
Figure 3.3
When you call the PrintITextO function, you determine the point
relative to the RastPort to which all other parameters in the structures refer. This point becomes the origin. The position specified in the
IntuiText structure then relates to this. Insert the following line in
your Customscreenl. c program:
PrintIText(MyWindowRastPort, FirstText, 10L, 20L);
Note:
to
*
*
* ===================z=z=======z===== *
*
*
**
Program: PrintIText.c
Comments:
* Author: Date:
*
*
*
10/16/1981 PrintIText Test *
* Wgb
Window
*
*
*
*
***************************************/
--------- ----------
iinc1ude <exec/types.h>
iinclude <intuition/intuition.h>
struct IntuitionBase *IntuitionBase;
struct Window
*FirstWindow;
struct NewWindow FirstNewWindow
148
ABACUS
3.3
160, 50,
320, 150,
/*
/*
/*
/*
/*
0, 1,
NULL,
WINDOWDEPTH I
WINDOWSIZING I
WINDOWDRAG I
WINDOWCLOSE I
SMART_REFRESH,
NULL,
/* First Gadget
NULL,
/* CheckMark
(UBYTE *)"System programimg test",
NULL,
/* Screen
NULL,
/* BitMap
100, 50,
/* Min Width, Height
640, 200,
/* Max Width, Height
WBENCHSCREEN,
/* Typ
OUTPUT
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
};
/* FrontPen, BackPen
*/
JAM2,
/* DrawMode
*/
15, 0,
/* LeftEdge, TopEdge
*/
NULL,
/* Font (Standard)
*/
(UBYTE *) "System programing on the Amiga!",
NULL
/* NextText
*/
};
1, 0,
main ()
{
MyWindowsRastPort
FirstWindow->RPort;
/***************************************
===================================
*
*
***************************************/
149
3. INTUITION AND C
AMIGA
void
*OpenLibrary();
struct Window *OpenWindow();
if (! (IntuitionBase = (struct IntuitionBase *)
Open Library ("intuition.library", OL)
{
/***************************************
*
*
*
*
*
*
*
***************************************/
if (FirstWindow)
if (IntuitionBase)
CloseWindow(FirstWindow);
CloseLibrary(IntuitionBase);
PrintIText format:
PrintIText(RastPort, IText, LeftOffset, TopOffset);
-216
AO
Al
DO
Dl
Linked text
1, 0,
JAM2,
15, 20,
150
/* FrontPen, BackPen
/* DrawMode
/* LeftEdge, TopEdge
*/
*/
*/
ABACUS
3.3
NULL,
1* Font (Standard)
(UBYTE *) "This text is an example ",
NULL
1* NextText
};
OUTPUT
*1
*1
0, 1,
1* FrontPen, BackPen
JAM2,
1* DrawMode
15, 30,
1* LeftEdge, TopEdge
NULL,
1* Font (Standard)
(UBYTE *) "of linking ",
&FourthText
1* NextText
};
struct IntuiText SecondText
*1
*1
*1
*1
*1
2, 0,
1* FrontPen, BackPen *1
JAM2,
1* DrawMode
*1
15, 40,
1* LeftEdge, TopEdge *1
NULL,
1* Font (Standard)
*1
(UBYTE *) "multiple strings ",
&ThirdText
1* NextText
*1
};
struct IntuiText FirstText
{
3, 0,
1* FrontPen, BackPen
JAM2,
1* DrawMode
15, 50,
1* LeftEdge, TopEdge
NULL,
1* Font (Standard)
(UBYTE *) "with IntuiText.",
&SecondText
1* NextText
};
*1
*1
*1
*1
*1
IntulText structure
UBYIE:
UBYIE:
BatkPen
UBlYE:
DrlNltode
SHORT:
LeftEdae
SHIRl:
T~Edge
srtutt Textftttr:
*ITextAttr
UBYTE:
*!Text
strutt IntuiText r*IIextText
UBYIE:
ri frontPen
UBYlE:
BltkPen
UBT'tE:
DrlNltode
SHORT:
LeftElli
SHIIlI:
TI!PEdge
srtUtt TextRttr:
*ITextAttr
UBYlE:
*!Text
strutt IntulText t*IIextText
UBYIE:
FrontPen
UBYlE:
BatkPen
UBlYE:
DrlNltode
SHORT:
LeftEdge
SHIIlI:
lopEdge
srtutt TextAttr:
*!TextAttr
UBYIE:
*IText
strutt Intullext
*IIextText
r+
Figure 3.4
151
3. INTUITION AND C
3.3.2
Drawing lines
The second output element which Intuition supports is line output.
You may be familiar with the importance of these lines if you have
ever seeri a Workbench requester. Each gadget has a double border surrounding it. The scroll bars in the directory windows are also framed
with a box made up of these lines.
First we'll examine the parameters in the Border structure so that you
can draw simple lines.
3.3.2.1
used (a line really doesn't have a defined background). For this same
reason you can't use all four modes provided for DrawMode. Only
JAMl and INVERSVID make sense because the other two refer to the
second color (again, an unused color in this case).
3.3.2.2
OxOO
Ox02
Ox04
OxOS
Ox06
Ox07
Ox08
Oxoc
Ox10
};
152
00
02
04
05
06
07
08
12
16
SHORT LeftEdge
SHORT TopEdge;
UBYTE FrontPen
UBYTE BackPen;
UBYTE DrawMode;
BYTE Count;
SHORT *XY;
struct Border *NextBorder;
ABACUS
3.3
OUTPUT
3.3.2.3
58
188
158
2BO
5B
(1BO, 60)
,j.
(23B, 68)
J,
188
Figure 3.5
A coordinate table could look like this:
SHORT TestValues[]
{
0, 0,
50, 0,
50, 12,
153
3. INTUITION AND C
AMIGA
0, 12,
0, 0
};
3.3.2.4
DrawBorder function
Now that we've taken stock of the situation, let's take advantage of
Intuition's command for drawing borders. To do this you need a completely defined Border structure and a coordinate table:
/* 3.3.2.4.A. testborderstruct *1
struct Border TestBorder =
{
50, 20,
2, 0,
JAM1,
5,
&TestValues,
NULL
};
Let's use the table printed above as the coordinates. In addition, you
need the RastPort and the offsets for the function:
struct RastPort *MyWindowRastPort;
MyWindowRastPort = MyWindow->RPort;
DrawBorder(MyWindowRastPort, TestBorder, 10L, 10L);
Add the Border structure, the coordinate table, the RastPort and
the function call to CustomScrteen C program. After starting the
modified program, a small box appears in the window. Here is the
complete program:
1***************************************
*
*
*
*
Program: DrawBorder.c
===================~===-=======~===
* Author: Date:
*
* Wgb
10/16/1987
----------
*
*
Comments:
---------Border box
in Window
..*
*
....*
..
*
*
***************************************1
154
3.3 OUTPUT
ABACUS
#include <exec/types.h>
#include <intuition/intuition.h>
struct IntuitionBase *IntuitionBase;
struct Window
*FirstWindow;
struct NewWindow FirstNewWindow
160, 50,
320, 150,
0, 1,
NULL,
WINDOWDEPTH I
WINDOWSIZING I
WINDOWDRAG I
WINDOWCLOSE I
SMART_REFRESH,
/*
/*
/*
/*
/*
LeftEdge, TopEdge
Width, Height
DetailPen, BlockPen
IDCMP Flags
Flags
1* First Gadget
/* CheckMark
(UBYTE *)"System programing test",
NULL,
/* Screen
/* BitMap
NULL,
/* Min Width, Height
100, 50,
640, 200,
/* Max Width, Height
WBENCHSCREEN,
/* Typ
};
NULL,
NULL,
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
SHORT TestValue[]
{
0,
0,
50, 0,
50, 12,
0, 12,
0, 0
};
50, 20,
2, 0,
JAM1,
5,
TestValue,
NULL
};
main 0
{
lSS
3.
INTUITION AND
AMIGA
MyWindowsRastPort = FirstWindow->RPort;
DrawBorder(MyWindowsRastPort, &TestBorder, lOL, lOLl;
Delay(lBOL);
1***************************************
*
*
* Function: Library and Window open *
* =================================== *
***************************************1
void
*OpenLibrary();
struct Window *Openwindow();
if (! (IntuitionBase = (struct IntuitionBase *)
OpenLibrary (nintuition .libraryn, OL)
{
1***************************************
*
*
*
* =================================== *
*
*
***************************************/
if (FirstWindow)
if (IntuitionBase)
}
156
CloseWindow(FirstWindow);
CloseLibrary(IntuitionBase);
3.3
ABACUS
OUTPUT
Finally, here is an example which combines several Border structures. This is how programmers create multicolor boxes around system
requesters.
/* 3.3.2.4.B.multiborders */
SHORT WhiteValues[]
{
0, 0,
50, 0,
50,12,
0,12,
0, 0
};
SHORT RedValues[]
{
0, 0,
54, 0,
54,16,
0,16,
0, 0
};
20, 20,
1, 0,
JAM1,
5,
&WhiteValues,
NULL
};
18, 18,
2, 0,
JAM1,
5,
&RedValues,
&WhiteBorder
};
157
3.
INTUITION AND
Border structure
SHORT:
leftEdge
Fu nctlon SHDRT:
TllpEdae
UBYTE:
FrontPen
UBYTE:
BatkPen
UBTYE:
Drawllode
BYTE:
Count
SHORT:
*xy
strutt Border:
*HextBorder
SHDRT:
rt leftEdge
f-
SHORT:
TopEdge
UBYTE:
FrontPen
UBVTE:
BatkPen
UBTYE:
DrilMl10de
BYTE:
Count
SHORT:
*xy
strutt Border:
*HextBorder
r-t
r-
SHORT:
leftEdge
SHII!T:
TopEdge
UBYTE:
FrontPen
UBYTE:
BatkPen
UBTYE:
Drawllode
BYTE:
Count
SHORT:
*xy
strutt Border:
*HextBorder
r+
r-
Figure 3.6
3.3.3
Graphic output
Now let's look at Intuition's most important output function: graphics.
Look at the Workbench with its icons. Each icon and system gadget is
a small graphic image.
3.3.3.1
158
ABACUS
3.3.3.2
3.3
OUTPUT
OxOO
Ox02
Ox04
Ox06
Ox08
OxOA
OxOE
OxOF
Ox10
Ox14
};
00
02
04
06
08
10
14
15
16
20
SHORT LeftEdge;
SHORT TopEdge;
SHORT Width;
SHORT Height;
SHORT Depth;
USHORT *ImageData;
UBYTE PlanePick
UBYTE PlaneOnOff;
struct Image *NextImage;
The fIrst five values have been explained. Let's look at the remaining
four.
*ImageData
This pointer points to one or more data fIelds containing graphic data.
*NextImage
This pointer lets you connect multiple Image structures. This allows you to easily generate complex
graphics.
The Intuition DrawImageO function writes the graphic data into the
bit-planes of our window or screen.
The P l.aneP ick flag tells the function which planes of your graphic
to transfer to those of the window. Each bit represents a plane:
Table 3.9:
P l.aneP ick,
Pl.aneOnO
Planes used
None
Plane 0
Plane 1
Plane 2
etc.
Plane 0 and 1
PianeOand2
Plane 0, 1 and 2
etc.
159
3.
INTUITION AND
AMIGA
3.3.3.3
~++rH~++Hrl+++rHH++~HH++H
H4++~~.++HH++~HH++~H4++~
H4++~~.++HH++~HH++~H4++~
H4++~rlH,++HH++~HH++~Hrl++H
H4++hHHHf++Hrl++~HH++~Hrl++H
~++hHHH++Hrl+++rHH++~Hrl++H
Figure 3.7
HH++~HH++~~++~~++HH++~
HH+++HHH++~~++~~++HH++~
HH++~HH++~~++~~++HH++~
HH++~HH++~~++MH~++HH++~
HH++~HH++~~++~~++HH++~
~~~uu. .~~~~~. .~~~
Bltplane 1
ax
ax
ex
Ox
ex
ex
ex
ex
ex
ex
ex
ex
ex
ex
ax
ax
, ax
, ex
,,Ox
ex
,ex
,ex
,ex
,ex
,ex
,ex
,ex
,aX
, e~
,Bx
,ax
, ax
Sltplant 2
ax
ax
ax
Ox
ex
ax
ax
ax
ex
a~
Bx
ex
ax
ex
ax
ax
, ax
,ax
,ax
, ax
, Ox
, ex
,ax
, ax
I ax
,ex
,ex
, ax
,ex
I ax
,ax
,ax
If your graphic consists of multiple bit-planes, you must list the data
for the first bit-plane, then the data for the second, etc.
3.3.3.4
The
Drawlmaqe
function
Now we corne to some visible examples. Let's look at the first Image
structure:
160
ABACUS
3.3
OUTPUT
/* 3.3.3.4.A.imageexample *1
struct ImageExample =
{
20, 10,
16, 16,
2,
&ExampleData[O],
3, 0,
NULL
};
4++++++-H-++t-I-tt++-l
-+.++++++-H-++l-H+++-I
.w-H++Hf++t-I
~H++Hf++t-I
-++H-++HH++-I
-++t-H++t++++IH++t1f+tffi-t+tt-I
-+++-HH+++-I+I
.w-H-++HH++-I
-+.++++++-H-+-I+H+++-I
4++++++-H-+++I-tt++-l
H+H+++1H+H++f-If+tH
H-HH+H+H+ttt+H-tH
Bltplana 1
Bltplana 2
BxD7F8,
BxD7FB,
BdE1E,
BxlE1E,
Dx78BD,
Bx78BD,
Dx7BoB,
Bx7BBD,
Bx7B8D,
Bx7BBDi
OdElE,
OdElE,
oxB7FB,
Bxo7FB,
oxBDBo,
oxBDBB,
BxBBBB,
BxDBBo,
BxBBBB,
Bx8B8B,
BxDDBB,
Bx8888,
BxD808,
oxBOBB,
BxB8BB,
8xBD88,
oxBOBO,
oxB8Bo,
BxBBDB
BxBDBB
BxBDDB
BxBBDo
BxFEDo
BxFBDo
Bx8DDo
Ox888o
BxFBBo
BxFE80
odDBo
ox8DBo
oxB8Bo
Bxooo8
OxooBo
OxoOOD
OxoOBD
BxBDBB
8xFEOB
8xFBOD
Dx8000
8x8888
OxFBOD
BxFEBB
OxBOB8
8xOB88
BxDD80
oxoOB8
Figure 3.8
The data field for the example looks like this (remember when inserting
this code in CustomScreenl.c that it must be defined~ the
Image structure):
1* 3.3.3.4.B. sampledata */
USHORT ExampleData[] =
{
/* first BitPlane *1
Ox07F8, OxOOOO,
Ox07FO, OxOOOO,
Ox1E1E, OxOOOO,
Ox1E1E, OxOOOO,
Ox7800, OxFEOO,
Ox7800, OxF800,
Ox7800, OxOOOO,
Ox7800, OxOOOO,
Ox7800, OxF800,
Ox7800, OxFEOO,
Ox1E1E, OxOOOO,
Ox1E1E, OxOOOO,
Ox07F8, OxOOOO,
Ox07F8, OxOOOO,
OxOOOO, OxOOOO,
161
3. INTUITION AND C
OxOOOO, OxOOOO,
/* second BitPlane */
OxOOOO, OxOOOO,
OxOOOO, OxOOOO,
OxOOOO, OxOOOO,
OxOOOO, OxOOOO,
OxOOOO, OxFEOO,
OxOOOO, OxF800,
OxOOOO, OxOOOO,
OxOOOO, OxOOOO,
OxOOOO, OxF800,
OxOOOO, OxFEOO,
OxOOOO, OxOOOO,
OxOOOO, OxOOOO,
OxOOOO, OxOOOO,
OxOOOO, OxOOOO,
OxOOOO, OxOOOO,
OxOOOO, OxOOOO
};
Finally, you have to find the RastPort of the window and call the
DrawlmageO function:
struct RastPort *MyWindowRastPort;
MyWindowRastPort = MyWindow->RPort;
Drawlmage(MyWindowRastPort, Example, 10L, 10L);
The format:
Drawlmage(RastPort, Image, LeftOffset, TopOffset);
-114
AO
A1
DO
D1
Remember that your graphics chip can access only the chip memory
(the lower 512K). There are two ways to ensure that graphic data are in
this memory area. A linker option (+c in Aztec) transfers all data to the
chip memory. This is probably the simplest method. But it can also be
done within a program. To do this you must allocate space in chip
memory and then copy the graphic data into it. Then you can initialize
the graphic and display it. Here is the complete Drawlmage. c
program, remember to use the +c option in the Aztec linker to force
the program into chip memory.
/***************************************
*
*
* Program: Drawlmage.c
*
* =================================== *
*
*
Comments:
* Author: Date:
*
---------*
*
10/16/1987 Use +C linker
* Wgb
*
option (Aztec) *
*
*
*
***************************************/
----------
162
3.3
ABACUS
OUTPUT
#include <exec/types.h>
#include <intuition/intuition.h>
struct IntuitionBase *IntuitionBase;
struct Window
*FirstWindow;
struct NewWindow FirstNewWindow =
{
160, 50,
320, 150,
0, 1,
/*
/*
/*
/*
LeftEdge, TopEdge
Width, Height
DetailPen, BlockPen
IDCMP Flags
/* Flags
NULL,
WINDOWDEPTH
WINDOWSIZING I
WINDOWDRAG I
WINDOWCLOSE I
SMART_REFRESH,
/* First Gadget
NULL,
NULL,
/* CheckMark
(UBYTE *)"System programming test",
/* Screen
NULL,
NULL,
/* BitMap
/* Min Width, Height
100, 50,
/* Max Width, Height
640, 200,
WBENCHSCREEN,
/* Typ
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
};
USHORT ExampleData[]
{
/* first BitPlane */
Ox07F8, OxOOOO,
Ox07FO, OxOOOO,
Ox1E1E, OxOOOO,
Ox1E1E, OxOOOO,
Ox7800, OxFEOO,
Ox7800, OxF800,
Ox7800, OxOOOO,
Ox7800, OxOOOO,
Ox7800, OxF800,
Ox7800, OxFEOO,
Ox1E1E, OxOOOO,
Ox1E1E, OxOOOO,
Ox07F8, OxOOOO,
Ox07F8, OxOOOO,
OxOOOO, OxOOOO,
OxOOOO, OxOOOO,
/* second BitPlane */
OxOOOO, OxOOOO,
OxOOOO, OxOOOO,
OxOOOO, OxOOOO,
OxOOOO, OxOOOO,
OxOOOO, OxFEOO,
OxOOOO, OxF800,
163
3.
INTUITION AND
AMIGA
OxOOOO,
OxOOOO,
OxOOOO,
OxOOOO,
OxOOOO,
OxOOOO,
OxOOOO,
OxOOOO,
OxOOOO,
OxOOOO,
};
OxOOOO,
OxOOOO,
OxFBOO,
OxFEOO,
OxOOOO,
OxOOOO,
OxOOOO,
OxOOOO,
OxOOOO,
OxOOOO
20, 10,
32, 16,
2,
&ExampleData[O],
3, 0,
NULL
};
main ()
{
MyWindowsRastPort = FirstWindow->RPort;
DrawImage(MyWindowsRastPort, &Example, 10L, lOLl;
Delay(lBOL);
1***************************************
*
*
* Function: Library and Window open *
* =================================== *
*
*
***************************************1
void
*OpenLibrary();
struct Window *OpenWindow();
if (! (IntuitionBase = (struct IntuitionBase *)
OpenLibrary ("intuition. library" , OL)
{
164
3.3 OUTPUT
ABACUS
/***************************************
i f (FirstWindow)
if (IntuitionBase)
CloseWindow(FirstWindow);
CloseLibrary(IntuitionBase);
3.3.4
In addition, this section includes some small graphics, texts and lines
which you will use in defining the gadgets described in Section 3.4.
We're expanding the "toolkit" needed to write our C source code editor,
a little at a time.
165
3.
INTUITION AND
3.3.4.1
AMIGA
Structure 3.6:
De f a ul. t Text
/*3.3.4.1.A.textdefinition */
struct TextAttr DefaultFont
{
(STRPTR) "topaz.font",
TOPAZ_EIGHTY,
FS_NORMAL,
FPF_ROMFONT
};
struct IntuiText DefaultText {
1, 0,
JAM2,
1, 1,
&DefaultFont,
NULL,
NULL
};
/*
/*
/*
/*
/*
/*
FrontPen, BackPen
DrawMode
LeftEdge, TopEdge
Font
Textpointer
NextText
*/
*/
*/
*/
*/
*/
The TextAttr structure is also initialized here for correct font usage.
For the strings which are later inserted into this structure, you define a
pointer array of type char so that you can access each string easily:
/*3.3.4.1.B.stringaccess *1
char *Textarray[] =
{
Now you just need an array of IntuiText structures into which you
will copy the default structure values and then add the string pointer.
struct IntuiText AllTexts[4];
166
3.3
ABACUS
Function 3.3:
Make_Text
OUTPUT
/*3.3.4.1.C.Make_Text function */
Make_Text (Number, Text, Struktur)
int
Number;
char
*Text[];
struct IntuiText Struktur[];
int i;
for (i=O; i<Num,ber; i++)
{
Struktur[i]
= DefaultText;
Struktur[i] .IText
(UBYTE *) Text[i];
Struktur[i] .NextText = &Struktur[i+1];
}
Struktur[Number-1].NextText = NULL;
}
How it works
The subroutine does its work in a small loop. Here the structure defined
as an array element is first assigned the default structure values. Then
you add the pointer to the first string and join this first IntuiText
structure to the second. The loop then executes again for the next value.
To prevent the last structure from pointing to a nonexistent structure,
the extra pointer must be deleted again.
If you don't want to link the texts together, you can omit the two lines
* Program: Make_Text.c
*
* =================================--= *
*
*
* Author: Date:
Comments:
*
*
*
10/16/1987 Make text
* Wgb
*
01/16/1988 Structures
* JLD
*
*
*
***************************************/
---------- ----------
#include <exec/types.h>
#include <intuition/intuition.h>
struct IntuitionBase *IntuitionBase;
struct Window
*FirstWindow;
struct NewWindow FirstNewWindow
160, 50,
400, 150,
/* LeftEdge, TopEdge
/* Width, Height
*/
*/
167
3.
INTUITION AND
AMIGA
0, 1,
1* DetailPen, BlockPen
NULL,
1* IDCMP Flags
WINDOWDEPTH I
1* Flags
WINDOWSIZING I
WINDOWDRAG I
WINDOWCLOSE I
SMART_REFRESH,
NULL,
1* First Gadget
NULL,
1* CheckMark
(UBYTE *)"System programming test",
NULL,
1* Screen
NULL,
1* BitMap
100, 50,
1* Min Width, Height
640, 200,
1* Max Width, Height
WBENCHSCREEN,
1* Typ
};
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
1, 0,
JAM2,
1, 1,
&DefaultFont,
NULL,
NULL
};
1*
1*
1*
1*
1*
1*
FrontPen, BackPen
DrawMode
LeftEdge, Top Edge
Font
Textpointer
NextText
char *Textfield[]
{
main ()
{
168
= FirstWindow->RPort;
*1
*1
*1
*1
*1
*1
3.3
ABACUS
OUTPUT
/***************************************
*
*
*
*
*
***************************************/
void
*OpenLibrary();
struct Window *OpenWindow();
if (! (IntuitionBase = (struct IntuitionBase *)
OpenLibrary("intuition.library", OL)
{
/***************************************
===================================
*
*
*
***************************************/
if (FirstWindow)
if (IntuitionBase)
CloseWindow(FirstWindow);
CloseLibrary(IntuitionBase);
169
3. INTUITION AND C
AMIGA
int
Number;
char
*Text[];
struct IntuiText Struktur[];
int i;
for(i=O; i<Nurnber; i++)
{
Struktur[i]
Struktur[i] .IText
Struktur[i] .TopEdge
Struktur[i] .NextText
Defau1tText;
= (UBYTE *) Text[i];
= 20+i*10;
= &Struktur[i+1];
Struktur[Number-1].NextText
= NULL;
3.3.4.2
0, 0,
66, 0,
66,10,
0,10,
0, 0
};
170
*1
ABACUS
3.3
OUTPUT
0, 0,
1, 0,
JAM1,
5,
&singleValues,
NULL
};
0, 0,
70, 0,
70,14,
0,14,
0, 0
};
-2, -2,
2, 0,
JAM1,
5,
&DoubleValues,
NULL
};
Next we offer a solution for the problem of defIning a gadget border for
text whose length is not known until the program is running. A
Border structure and value array must be defined preceding the
main () function. This would look like this:
/*3.3.4.2.C.borderlong*/
SHORT Values []
{
0, 0,
999, 0,
999,10,
0,10,
0, 0
};
0, 0,
1, 0,
171
3.
INTUITION AND
AMIGA
JAM1,
5,
Values,
NULL
};
1, 0,
JAM2,
2, 2,
NULL,
(UBYTE *) "Test",
NULL
};
/*
/*
/*
/*
/*
/*
FrontPen, BackPen
DrawMode
LeftEdge, TopEdge
Font (Standard)
Textpointer
NextText
*/
*/
*/
*/
*/
*/
Both Text and Bard are written to RastPart at the same offsets:
DrawBorder(RastPort, Bord, 10L, 30L);
PrintIText(RastPort, Text, 10L, 30L);
Before this can be done, you have to call a function which calculates the
correct border values for the text:
/*3.3.4.2.E.Calc*/
Calc_Border (Text)
struct IntuiText
Text;
int Width
0;
Width = IntuiTextLenght(Text);
Values [2]
Values [4]
Width+4;
Width+4;
* Program: Calc_Border.c
*
*
*
*
*
172
============================~=~====
Author:
Wgb
Date:
10/16/1987
Comments:
for testing
*
*
*
*
*
*
*
3.3
ABACUS
only
OUTPUT
*
*
***************************************1
#include <exec/types.h>
#include <intuition/intuition.h>
struct IntuitionBase *IntuitionBase;
struct Window
*FirstWindow;
struct NewWindow FirstNewWindow
160, 50,
320, 150,
0, 1,
1*
1*
1*
1*
1*
LeftEdge, TopEdge
Width, Height
DetailPen, BlockPen
IDCMP Flags
Flags
NULL,
WINDOWDEPTH I
WINDOWSIZING I
WINDOWDRAG I
WINDOWCLOSE I
SMART_REFRESH,
1* First Gadget
NULL,
1* CheckMark
NULL,
(UBYTE *)"System programming test",
NULL,
1* Screen
1* BitMap
NULL,
1* Min Width, Height
100, 50,
1* Max Width, Height
640, 200,
WBENCHSCREEN,
1* Typ
*1
*1
*1
*1
*/
*1
*1
*1
*1
*1
*1
*1
};
SHORT Values [ 1
{
0, 0,
999, 0,
999,10,
0,10,
0, 0
};
0, 0,
1, 0,
JAMl,
5,
Values,
NULL
};
struct IntuiText Text =
{
1, 0,
1* FrontPen, BackPen
*1
173
3. INTUITION AND C
AMIGA
JAM2,
2, 2,
NULL,
(UBYTE *) "Test",
NULL
};
1*
1*
1*
1*
1*
DrawMode
LeftEdge, TopEdge
Font (Standard)
Textpointer
NextText
*1
*1
*1
*1
*1
main ()
{
RP
= FirstWindow->RPort;
Calc_Border(&Text);
DrawBorder(RP, &Bord, 10L, 30L)1
PrintIText(RP, &Text, 10L, 30L);
Delay(180L);
Close_All 0 ;
}
1********************************
*
*
*
********************************1
void Open_All ()
{
void
*OpenLibrary()1
struct Window *OpenWindow();
if(! (IntuitionBase = (struct IntuitionBase *)
OpenLibrary("intuition.library", OL)
(
174
ABACUS
3.3
OUTPUT
1***************************************
*
*
***************************************/
void Close_AII()
{
if (FirstWindow)
if (IntuitionBase)
CloseWindow(FirstWindow);
CloseLibrary(IntuitionBase);
Calc_Border (Text)
struct IntuiText
*Text;
int Width
Width
0:
0:
0;
IntuiTextLength(Text);
Values[2J
Values[4J
0:
0:
Width+4;
Width+4;
3.3.4,,3
175
3. INTUITION AND C
AMIGA
Ox8100,
Ox!Uff,
-t-t-+HH-tlI-H-t-t-+H+t-+-H-t-t-+H Bx8CBl,
-t-t-+t-~-t+t-H++++-ir-t+++-H OxffBl,
..-+-H--..+++-H4-+++-HH-+++1-4 OxlOCl,
H-t___++-I+t-~-t+-a-I+++-H-+++-H+++-H"'" DxlB21,
H-t-e-++-I+t-++-I-t+.-...t++++-I-+++-H+++-H,.... OxlBBl,
-+++-H+++-H-+++-H~ BxlOOl,
-+++++++-1-++++-11-++-1-4 Bxlfff,
H-t-t+++-I++++-I-t++-H+++-H-+++-H+++-H,.... BxBBBO,
H-t-t+++-I++++-I-t++-H+++-H-+++-H+++-H,.... BxODBD,
t-H-H-t-H-H-t-H-t+t-H-H-t-H-t+++-I++++1H BxBDDO I
H+++-t+tt-H-++++-t-++H-t+Hf-t-t++!-++~ BxODBO I
H+++-t+tt-H-++++-t-++H-t+Hf-t-t++!-++~ BxDDDD,
L..U..........I..I...I.~L..U....L..L..Ju...L."-U...I..I..U-I...I...I..L...I..,I...L...I..J BxBBBB I
-t+t+++t-i-t+t++-H-H
-t+t+++t-i-t+t++-H-H
Figure 3.9
Resize Gadget
OxDDOO
DxODDO
DxDDDB
BxDBOB
OxOBOB
OxBBOB
OxBBDB
BxOOOB
OxBBOB
OxDBBB
DxBBBB
BxBDDO
BxDOOB
OxDBOO
OxDDBS
176
3.4 GADGETS
ABACUS
3.4
Gadgets
Application programs need the input of general information, like text or
data in order to operate properly. In addition, they need instructions to
guide them in their work.
As an experienced Amiga owner you know that gadgets provide the
easiest way to send information to the computer. These are rectangular
selectable fields displayed in windows or screens. Programs react when
you click the mouse button with the pointer on a gadget
The Amiga can support more than just simple gadgets which just pass
on a selection. Some gadgets can be turned off and on, some can be
used for text input, and others can specify positions. These different
gadget types can be further divided into subgroups. Using gadgets, you
have many different ways to get information from the user.
You wouldn't be able to recognize these gadgets if they were only
placed on the screen, Intuition allows the user to "decorate" these
gadgets with borders, graphics and text. This is why the display
techniques explained in the previous section are so important
3.4.1
Boolean
gadgets
Boolean gadgets are intended for very simple commands and data which
consist of just a boolean value (true or false). They offer two modes:
The first mode is called "hit select." This means that Intuition sends a
report when you click the gadget with the left mouse button (called the
select button in this case). The appearance of the gadget changes while
the mouse selects it. If you move the mouse pointer away or release the
select button, the appeamnce reverts to its original form. This mode can
be used to issue very simple, specific commands.
177
3.
INTUITION AND
AMIGA
With the second mode, "toggle select," you turn the gadget on with the
ftrst click. It then changes its appearance as if the mouse button were
being held on it. A second click changes the gadget to its original state.
This method is particularly good for turning choices on and off.
Proportional
gadgets
If you want to represent areas or positions, this is the gadget type for
you. With it you can display specific proportions in one or two
dimensions and let the user of your program change them.
These gadgets consist of a rectangular box containing an object. We're
using a generic term here because you can define any graphic you like
as the object. For example, in Preferences the screen position object
represents the edge of the Workbench screen, which you can set using a
proportional gadget. In this case it is two-dimensional. An example of a
one-dimensional proportional gadget is the scroll bar in a directory
window. The bar, your "object," represents the size of the window
display and can be moved back and forth.
For each proportional gadget you can defme the size of the scroll box
(container), the object used (knob), and the size of the object.
String gadgets The string gadget allows simple text input. Like a proportional gadget,
there is a "container" into which you enter a single line of text. The
length is irrelevant: Intuition scrolls the text as necessary.
Editing is made easier by additional functions like Undo.
Now you know all of the gadget types and their basic properties. Let's
look at the details of each type.
3.4.1.1
Boolean gadgets
One of the most common gadget types is the boolean gadget. Boolean
gadgets appear three times in the system gadgets: Close gadget, front
gadget and back gadget. Boolean gadgets represent the basic gadget type,
which is why we will discuss them in such detail.
Each gadget can be assigned a position, height and width. Let's take a
look at the structure before we go into the other values:
1* 3.4.1.1.A.booleanstruct *1
struct Gadget
{
OxOO
Ox04
Ox06
Ox08
OxOA
178
00
04
06
08
10
3.4
ABACUS
OxOC
OxOE
Oxl0
Ox12
Ox16
Ox1A
Ox1E
Ox22
Ox26
Ox28
Ox2C
};
12
14
16
18
22
26
30
34
38
40
44
GADGETS
USHORT Flags;
USHORT Activation;
USHORT GadgetType;
APTR GadgetRender;
APTR SelectRender;
struct IntuiText *GadgetText;
LONG MutualExclude;
APTR Speciallnfo;
USHORT GadgetID;
APTR UserData;
Just like all of the other Intuition structures you have looked at, gadgets
can be linked together. This is the purpose of the first pointer. After the
values for the left and top edges, height, and width you find a flag
which performs several tasks.
Flags first show you the settings for the gadget graphic. You can
specify how the appearance of the gadget is changed when it is clicked.
There are four GADGHIGHBITS available for this:
GADGHCOMP
GADGHBOX
GADGHlMAGE
GAGDHNONE
GRELBOTTOM
GRELRIGHT
The size of the gadget can also be set in relationship to the window:
GRELWIDTH
179
3.
INTUITION AND
GRELHEIGHT
AMIGA
variable.
GADGDISABLED
Fla2
GADGHIGHBITS
GADGHCOMP
GADGHIMAGE
GADGHBOX
GADGHNONE
Hex value
OxOOO3L
OxOOOOL
OxOOO2L
OxOOOlL
OxOOO3L
GRELBOTTOM
GRELRIGHT
GRELWIDTH
GRELHEIGHT
OxOOO8L
OxOOlOL
OxOO20L
OxOO40L
GADGlMAGE
OxOOO4L
SELECTED
OxOO80L
GADGDISABLED OxOlOOL
Now we come to the next value of the Gadget structure. ThIS value is
a set of flags which contain information about how the gadget is acti
vated and whether it is located in the window border.
Let's first look at the flags which have to do with the activation status:
TOGGLESELECT
180
ABACUS
3.4
GADGETS
GADGIMMEDIATE
LEFTBORDER
TOPBORDER
BOTTOMBORDER
Top border
Gadgets in Uindow
....................................
..................... ..
.............................. .. .........................
".11
f
I
t 1,1
' "
I '"
I , . , , . , I " , . , . "
I I
I I I . _
I I
I I . . . . . . . . . . . . . . . . . . . . . . . I I I I I
II I.' I I I I 1""
I . I I ' . , . I I .- I , , I I
I I I
" , , , I I
I I I I I I I I I . I "
Left
border
I I
I I I I I I I I I "
I I
...........................................................
I I I I I I,' I "
Figure 3.10
Bottom border
Right border
181
3. INTUITION AND C
AMIGA
Activation f1al!
TOGGLES ELECT
RELVERIFY
GADGIMMEDIATE
Hex value
Ox01OOL
OxOOO1L
OxOOO2L
Comment
Switch gadget
RIGHTBORDER
LEFTBORDER
TOP BORDER
BOTTOMBORDER
OxOO1OL
OxOO20L
OxOO40L
OxOO80L
STRINGCENTER
STRINGRIGHT
LONGINT
ALTKEYMAP
BOOLEXTEND
ENDGADGET
FOLLOWMOUSE
Ox0200L
Ox0400L
Ox0800L
OxlOOOL
Ox2000L
OxOOO4L
OxOOO8L
Let's return to the Gadget structure. We now come to the last flag
value which has to do with the type of gadget. Gadget Type also
contains more information:
BOOLGADGET
A boolean gadget
PROPGADGET
STRGADGET
These three flags determine the gadget type. With two more flags,
which are set by the system, you can tell where the gadget carne from
and where it lies.
SCRGADGET
SYSGADGET
182
3.4 GADGETS
ABACUS
Table 3.12
Gadget types
Gadget Type
BOOLGADGET
GADGETOOO2
PROPGADGET
STRGAD GET
Hex value
OxOOO1L
OxOOO2L
OxOOO3L
OxOOO4L
SYSGADGET
SCRGADGET
GZZGADGET
GIMMEZEROZERO
REQGADGET
Ox8000L
Ox4000L
Ox2000L
OxlOOOL
Comment
Unused
System gadget
Screen gadget
Window gadget
Reauester midget
You can't just use a Border structure one time and an Image structure the next. As you can see above, a flag exists for each mode. This
means that the GAGDIMAGE flag must be set for an Image structure
and cleared for a Border structure.
GADGIMAGE also affects SelectRender, but it doesn't say that a
pointer has to be stored there. Intuition looks for a structure pointer
there only if the GADGIMAGE flag is set.
NULL,
10, 40,
60, 20,
/* NextGadget
/* LeftEdge, TopEdge
/* Width, Height
*/
*/
*/
183
3.
INTUITION AND
AMIGA
GADGHBOX,
RELVERIFY,
BOOLGADGET,
(APTR)&GadgetBorder,
NULL,
&GadgetText,
NULL,
NULL.
1,
NULL,
};
Structure
description
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
Flags
Activation
Gadget Type
GadgetRender
Select Render
GadgetText
MutualExclude
Special Info
GadgetID
UserData
*/
*1
*1
*1
*1
*1
*1
*1
*1
*1
SHORT GadgetPairs[}
{
160, 50,
320, 150,
184
1* LeftEdge, TopEdge
/* Width, Height
*1
*1
0, 1,
/* DetailPen, BlockPen */
CLOSE WINDOW
GADGETUP,
WINDOWDEPTH
1* IDCMP Flags
*1
/* Flags
*1
3.4 GADGETS
ABACUS
WINDOWSIZING I
WINDOWDRAG I WINDOWCLOSE
SMART_REFRESH,
&BooIGadget,
/* First Gadget
NULL,
/* CheckMark
(UBYTE *) "Gadget Programming Test",
NULL,
/* Screen
NULL,
/* BitMap
100, 50,
/* Min Width, Height
640, 200,
/* Max Width, Height
WBENCHSCREEN,
1* Type
};
*/
*1
*/
*/
*/
*1
*1
Take a look at the new IDCMP flag, which sends you messages whenever a gadget is selected.
With this addition you also have to extend the testing of the IDCMP
flag for this new case. You can get messages from a self-defined user
gadget as well as from the close gadget
1*3.4.1.1.E.othergadget_loop */
FOREVER
{
Wait(lL
continue;
FirstWindow->UserPort->mp_SigBit);
MessageClass = message->Class;
code = message->Code;
ReplyMsg(message);
switch (MessageClass)
{
case GADGETUP
nr = nr + 1;
printf("Gadget activated %d times!\n", nr);
break;
case CLOSEWINDOW
Close_AII();
exit(TRUE);
break;
The test loop here offers another important advantage over the previous
one: The Wai function places the task in the wait state until a signal
occurs. Therefore the loop doesn't use any processor time when it has
nothing to do.
to
The test for the new gadget is easy. The swi tchO statement tests to
see if a user-defined gadget has been selected. Since you only used one,
you don't have to sort things out any further, and output a counter
185
3. INTUITION AND C
AMIGA
which indicates how many times the gadget has been clicked. It's
important not to forget to declare this variable.
How it works
*
*
*
* ================================== *
* Gadgets: Boolean-Gadget.c
*
*
Comments:
* Author: Date:
*
*
*
10/16/1987 only for
* Wgb
*
testing
*
*
*
*
**************************************/
---------- ----------
#include <exec/types.h>
#include <intuition/intuition.h>
struct IntuitionBase *IntuitionBase;
struct Window
*FirstWindow;
struct IntuiMessage *message;
SHORT GadgetPairs[]
186
3.4 GADGETS
ABACUS
NULL,
10, 40,
60, 20,
GADGHBOX,
RELVERIFY,
BOOLGADGET,
(APTR)&GadgetBorder,
NULL,
&GadgetText,
NULL,
NULL,
1,
NULL,
);
1*
1*
1*
1*
1*
1*
1*
1*
1*
1*
NextGadget
LeftEdge, TopEdge
Width, Height
Flags
Activation
Gadget Type
GadgetRender
Select Render
GadgetText
MutualExclude
/* SpecialInfo
1* GadgetID
1* UserData
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
160, 50,
320, 150,
0, 1,
CLOSEWINDOW
GADGETUP,
WINDOWDEPTH
WINDOWSIZING I
WINDOWDRAG I
WINDOWCLOSE I
SMART_REFRESH,
1*
1*
1*
1*
1* Flags
*1
&BoolGadget,
NULL,
1* First Gadget
1* CheckMark
*1
*1
LeftEdge, TopEdge
Width, Height
DetailPen, BlockPen
IDCMP Flags
*1
*1
*1
*1
. *1
*1
NULL,
NULL,
1* Screen
1* BitMap
100, 50,
640, 200,
*1
WBENCHSCREEN,
};
1* Type
*1
*/
main ()
{
ULONG MessageClass;
USHORT code, nr ; 0;
struct Message *GetMsg();
187
3.
INTUITION AND
AMIGA
Wait(lL
continue;
FirstWindow->UserPort->mp_SigBit);
MessageClass = message->Class;
code = message->Code;
ReplyMsg(message);
switch (MessageClass)
{
case GADGETUP
: nr += 1 ;
printf("Gadget activated tu times!\n", nr);
break;
case CLOSEWINDOW
Close_All 0;
exit (TRUE);
break;
Open_All ()
{
void *OpenLibrary();
struct Window *OpenWindow();
if (! (IntuitionBase = (struct IntuitionBase *)
OpenLibrary("intuition.library", OL)
printf (IIIntuition Library not found!\n");
Close_All () ;
exit(FALSE);
}
Close_All ()
{
if (First Window)
if (IntuitionBase)
}
188
CloseWindow(FirstWindow);
CloseLibrary(IntuitionBase),
3.4
ABACUS
3.4.1.2
GADGETS
Proportional gadgets
All other gadget types are extensions of the boolean gadget, including
the proportional gadget. This gadget is used for the Workbench scroll
bars. This is useful when the material to be displayed is larger than the
actual display area. Then a scroll bar indicates the relative size of the
display area and allows the user to view a different portion of the whole.
Preferences uses a two-dimensional proportional gadget to allow you to
set the position of the Workbench screen. This is another application of
this gadget type.
For the fIrst proportional gadget, let's take a simple basic gadget structure with a long vertical click field:
1* 3.4.1.2.A.propgadget */struct Gadget PropGadget =
{
1* NextGadget
NULL,
100, 20,
20, 60,
GADGHCOMP,
RELVERIFY,
PROPGADGET,
(APTR) Buffer,
NULL,
NULL,
NULL,
&ExampleProp,
/* LeftEdge, TopEdge
/* Width, Height
1* Flags
/* Activation
/* Gadget Type
/* GadgetRender
/* Select Render
/* GadgetText
/* MutualExclude
1* Speciallnfo
1* GadgetID
1* UserData
2,
NULL,
*/
*/
*1
*1
*/
*/
*/
*1
*/
*/
*/
*/
*1
};
As you can see, this structure is a little short on arguments. But you
don't need many: You don't need a text in this example, and a border
around the container is drawn automatically for proportional gadgets.
We have, however, defIned the value SpecialInfo. This pointer is
used whenever a boolean gadget isn't enough. It then points to an
extension structure. In this case you need a P ropInfo structure,
which looks as follows:
/* 3.4.1.2.B.propinfostruct*/
struct Proplnfo
{
OxOO
Ox02
Ox04
Ox06
Ox08
OxOA
OxOC
00
02
04
06
08
10
12
USHORT
USHORT
USHORT
USHORT
USHORT
USHORT
USHORT
Flags;
HorizPot;
VertPot;
HorizBody;
VertBody;
CWidth;
CHeight;
189
3. INTUITION AND C
OxOE
OxlO
Ox12
Ox14
Ox16
};
AMIGA
14
16
18
20
22
USHORT
USHORT
USHORT
USHORT
HPotRes;
VPotRes;
LeftBorder;
TopBorder;
So you see, you need more data for a proportional gadget. These contain
the dimensions of the container and the knob. The knob is the actual
graphic that is moved by the user. The container is the actual area in
which the knob can be moved. Here are the descriptions of each
parameter:
Flags
AUTOKNOB
FREEHORIZ
If you set this flag, the knob can be moved horizontally. The knob can be moved in both directions if
KNOBHIT
PROPBORDERLESS
190
HorizPot
VertPot
HoriZBody
Vert Body
3.4 GADGETS
ABACUS
None of the following values need be set by the programmer. They are
calculated by Intuition and can be read after the gadget has been initialized.
cwidth
Container width
CHeight
Container height
HPotRes
VPotRes
LeftBorder
TopBorder
The example code used to access the Proplnfo structure would look
something like this:
1*3.4.1.2.C.exampleprop*1
struct Proplnfo ExampleProp
AUTOKNOB I FREEVERT,
Ox8000,
Ox8000,
Ox0800,
Ox0800,
0,
0,
0,
0,
0,
0
1*
1*
1*
1*
1*
1*
1*
1*
1*
1*
1*
Flags
HorizPot
VertPot
HorizBody
VertBody
CWidth
CHeight
HPotRes
VPotRes
LeftBorder
TopBorder
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
};
Now you can insert the structure definitions in your program. Remember that the Proplnfo structure must be defmed first so that the value
of &ExampleProp is known to the gadget structure. The same
applies to the window structure. in which the address of the gadget
structure is set in place.
Even after you've done all this. you're still not finished. With a
proportional gadget you get a message when the gadget has been
released (GADGET UP) instead of when you ''press it" (GADGETDOWN).
You have to specify this in your loop:
Program 3.3:
Test loop for
proportional
gadget
1*3.4.1.2.D.prop forever *1
FOREVER
{
191
3.
INTUITION AND
Wait(lL
continue;
FirstWindow->UserPort->mp_SigBit);
MessageClass = message->Class;
code = message->Code;
ReplyMsg(message);
switch (MessageClass)
(
case GADGETUP
printf("Position: u%\n",
ExampleProp.VertPot);
break;
case CLOSEWINDOW
Close_All () ;
exit(TRUE);
break;
case GADGETDOWN
nr += 1;
printf ("Gadget activated for
the %nr time!\n", nr);
break;
*
*
* Program: Proportional-Gadget.c
*
* =================================== *
*
* Author:
*
* Wgb
*
*
Date:
Comments:
----------
----------
10/16/1987
for testing
only
*
*
*
*
*
***************************************/
#include <exec/types.h>
#include <intuition/intuition.h>
struct IntuitionBase *IntuitionBase;
struct Window
*FirstWindow;
struct IntuiMessage *message;
WORD Buffer[4];
struct Proplnfo ExampleProp
{
AUTOKNOB I FREEVERT, /*
Ox8000,
/*
Ox8000,
/*
Ox0800,
/*
Ox0800,
/*
192
Flags
HorizPot
VertPot
HorizBody
Vert Body
*/
*/
*/
*/
*/
3.4
ABACUS
0,
0,
0,
0,
0,
0
1*
1*
1*
1*
1*
1*
CWidth
CHeight
HPotRes
VPotRes
LeftBorder
TopBorder
GADGETS
*1
*1
*1
*1
*1
*1
};
1* NextGadget
*1
1* LeftEdge, TopEdge *1
1* Width, Height
*1
GADGHCOMP,
1* Flags
*1
RELVERIFY,
1* Activation
*1
PROPGADGET,
1* Gadget Type
*1
(APTR) Buffer,
1* GadgetRender
*1
NULL,
1* Select Render
*1
NULL,
1* GadgetText
*1
NULL,
1* MutualExclude
*1
(APTR) &ExampleProp, 1* SpecialInfo
*1
2,
1* GadgetID
*1
NULL,
1* UserData
*1
NULL,
100, 20,
20, 60,
};
1*
1*
1*
1*
LeftEdge, TopEdge
Width, Height
DetailPen, BlockPen
rDCMP Flags
CLOSEWINDOW
GADGETUP,
WINDCWDEPTH 1
1* Flags
WINDOWSIZING I
WINDOWDRAG I
WINDOWCLOSE I
SMART_REFRESH,
&PropGadget,
1* First Gadget
NULL,
1* CheckMark
(UBYTE *) "Gadget Programmng Test",
NULL,
1* Screen
NULL,
1* BitMap
100, 50,
1* Min Width, Height
640, 200,
1* Max Width, Height
WBENCHSCREEN,
1* Type
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
};
main ()
{
ULONG MessageClass;
USHORT code, nr ~ 0;
struct Message *GetMsg();
193
3.
INTUITION AND
AMIGA
FOREVER
{
Wait(1L
continue;
FirstWindow->UserPort->mp_SigBit);
MessageClass = message->Class;
code = message->Code;
ReplyMsg(message);
switch (MessageClass)
{
case GADGET UP
ExampleProp.VertPot);
printf("Position: %u\n",
break;
case CLOSEWINDOW
Close_All ();
exit (TRUE) ;
break;
case GADGETDOWN
nr +~ 1;
printf ("Gadget activated %d.
times!\n", nr);
break;
1***************************************
***************************************1
void
*OpenLibrary();
struct Window *OpenWindow();
if (! (IntuitionBase~ (struct IntuitionBase *)
OpenLibrary("intuition.library", OL)
{
194
3.4 GADGETS
ABACUS
1***************************************
*
*
*
*
*
***************************************1
if (FirstWindow)
if (IntuitionBase)
CloseWindow(FirstWindow);
CloseLibrary(IntuitionBase);
3.4.1.3
NULL,
50, 40,
120, 10,
GADGHCOMP,
RELVERIFY t
STRINGCENTER,
STRGADGET,
/* NextGadget
1* LeftEdge, TopEdge
1* Width, Height
1* Flags
1* Activation
*1
*1
*1
*1
*1
1* Gadget Type
*1
195
3.
INTUITION AND
(APTR)&GadgetBorder,
NULL,
&GadgetText,
NULL,
(APTR)&Stringlnfo,
3,
NULL,
/*
/*
/*
/*
/*
/*
/*
GadgetRender
Select Render
GadgetText
MutualExclude
SpecialInfo
GadgetID
UserData
*/
*/
*/
*/
*/
*/
*/
};
This flag centers the text in the gadget when set. The
default setting (unset STRINGCENTER flag) leftjustifies the text in the gadget.
STRINGRIGHT This flag right-justifies the text in the gadget when
set. The default setting (unset STRINGRIGHT flag)
ALTKEYMAP
After you specify the gadget type with STRGADGET, you must not
forget to identify the click field with a graphic. Borders are drawn
automatically only for proportional gadgets. We used a simple Border
structure here:
/*3.4.1.3.B.gadgetpairs*/
SHORT GadgetPairs[) =
{
The gadget text is not required, but you should include it so that the
user knows what to enter:
196
3.4 GADGETS
ABACUS
/*3.4.1.3.C/gadgettext*/
struct IntuiText GadgetText
{
OxOO
Ox04
Ox08
OxOA
OxOC
OxOE
Ox10
Ox12
Ox14
Ox16
Ox18
Ox1C
Ox20
Ox24
00
04
08
10
12
14
16
18
20
22
24
28
32
36
UBYTE *Buffer;
UBYTE *UndoBuffer;
SHORT BufferPos;
SHORT MaxChars;
SHORT DispPos;
SHORT UndoPos;
SHORT NumChars;
SHORT DispCount;
SHORT CLeft;
SHORT CTop;
struct Layer *LayerPtr;
LONG Longlnt;
struct KeyMap *AltKeyMap;
};
The first two values are the most important. They point to a buffer in
which the strings are stored. The first buffer contains the text which
was actually entered. The second buffer is used for the Undo editing
feature. The user just has to press a key to restore the text.
You can quickly create such a buffer. You simply define an array with
the corresponding number of characters. It looks like this:
#define STRINGSIZE 80
unsigned char StringBuffer[STRINGSIZE] = "Hello Amiga!";
unsigned char UndoBuffer [STRINGSIZE];
You don't have to provide an undo buffer, but then the user can't use
this function.
You can use BufferPos to initialize the cursor at a specific location.
This is used when text is already in the buffer before the call, as in the
example above.
Since Intuition does not know how large the text buffer is, you have to
tell it in MaxChars how many characters can be entered. Note that the
terminating null byte must be included in the string length.
197
3. INTUITION AND C
AMIGA
The last value which can be set by the programmer is DispPos. The
number which Intuition finds there designates the first character which
is displayed in the gadget field.
All other values are handled by Intuition itself. You can read them for
information.
UndoPos
NumChars
DispCount
CLeft
CTop
LayerPtr
LongInt
Al tKeyMap
&StringBuffer[O],
&UndoBuffer[Oj,
0,
STRINGSIZE,
0,
0,
0,
0,
0, 0,
NULL,
0,
NULL,
};
198
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
Buffer
Undo Buffer
Buffer Position
MaxChars
Display Position
Undo Position
NumChars
Display Counter
CLeft, CTop
LayerPtr
Longlnt
AltKeyMap
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
3.4
ABACUS
SHORT GadgetPairs[]
GADGETS
/* NextGadget
/* Left Edge , TopEdge
/* Width, Height
/* Flags
/* Activation
*/
*/
/*
/*
/*
/*
/*
/*
*/
*/
*/
*/
Gadget Type
GadgetRender
Select Render
GadgetText
MutualExclude
Speciallnfo
/* GadgetID
/* UserData
*/
*/
*/
*/
*/
*/
*/
160, 50,
320, 150,
0, 1,
/*
/*
/*
/*
LeftEdge, TopEdge
Width, Height
DetailPen, BlockPen
IDCMP Flags
CLOSEWINDOW
GADGETUP,
WINDOWDEPTH
/* Flags
WINDOWSIZING I
WINDOWDRAG I
WINDOWCLOSE I
SMART_REFRESH,
/* First Gadget
&stringGadget ,
NULL,
/* CheckMark
(UBYTE *)"String Gadget Test",
NULL,
/* Screen
NULL,
/* BitMap
/* Min Width, Height
100, 50,
/* Max Width, Height
640, 200,
WBENCHSCREEN,
/* Type
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
};
199
3.
INTUITION AND
AMIGA
case GADGETUP
&St~ingBuffer[O]);
break;
case CLOSEWINDOW
How it works
Close_All();
exit (TRUE) ;
break;
*
*
*
* ================================== *
* Program: String-Gadget.c
*
*
Comments:
* Author: Date:
*
*
---------- ---------*
10/16/1987 for testing
* Wgb
*
only
*
*
*
*
**************************************1
tinclude <exec/types.h>
tinclude <intuition/intuition.h>
struct IntuitionBase *IntuitionBase;
struct Window
*FirstWindow;
struct IntuiMessage *message;
#define STRINGSIZE 80
unsigned char StringBuffer[STRINGSIZE]
unsigned char UndoBuffer [STRINGSIZE];
struct Stringlnfo Stringlnfo
"Hello Amiga";
StringBuffer,
UndoBuffer,
0,
STRINGSIZE,
0,
200
1*
1*
1*
1*
1*
Buffer
Undo Buffer
Buffer Position
MaxChars
Display Positoin
*1
*1
*1
*1
*1
3.4 GADGETS
ABACUS
0,
0,
0,
0, 0,
NULL,
0,
NULL,
};
1*
1*
1*
1*
1*
1*
1*
Undo Position
NumChars
Display Counter
CLeft, CTop
LayerPtr
LongInt
AltKeyMap
*1
*1
*1
*1
*1
*1
*1
SHORT GadgetPairs[] =
{
NULL,
50, 40,
120, 10,
GADGHCOMP,
RELVERIFY I
STRINGCENTER,
STRGADGET,
(APTR)&GadgetBorder,
NULL,
&GadgetText,
NULL,
(APTR)&StringInfo,
1,
NULL,
1*
1*
1*
1*
1*
NextGadget
LeftEdge, TopEdge
Width, Height
Flags
Activation
*1
*1
1*
1*
1*
1*
1*
1*
1*
1*
Gadget Type
GadgetRender
Select Render
GadgetText
MutualExclude
Special Info
GadgetID
UserData
*1
*1
*1
*1
*1
*1
*/
*1
*1
*/
*/
};
*1
160, 50,
320, 150,
0, 1,
CLOSEWINDOW
GADGETUP I
GADGETDOWN,
/* LeftEdge, TopEdge
/* Width, Height
1* DetailPen, BlockPen
/* IDCMP Flags
*1
*1
WINDOWDEPTH
WINDOWSIZING I
WINDOWDRAG I
WINDOWCLOSE I
SMART_REFRESH,
/* Flags
*1
*J
201
3.
INTUITION AND
/* First Gadget
/* CheckMark
&StringGadget,
NULL,
*/
*/
/* Screen
/* BitMap
*/
*/
100, 50,
640, 200,
*/
*/
WBENCHSCREEN,
/* Type
*/
);
main ()
(
ULONG MessageClass;
USHORT code;
struct Message *GetMsg();
Wait(lL
continue;
FirstWindow->UserPort->mp_SigBit);
MessageClass = message->Class;
code = message->Code;
Rep1yMsg(message);
switch (MessageClass)
{
case GADGET UP
%s\n",&StringBuffer[O);
case CLOSEWINDOW
Open_All ()
{
void *OpenLibrary();
struct Window *OpenWindow();
202
3.4
ABACUS
GADGETS
Closeyll ()
{
if (FirstWindow)
if (IntuitionBase)
CloseWindow(FirstWindow);
CloseLibrary (IntuitionBase) ;
3.4.2
Gadgets in action
Now we will present an example of multiple applications of the three
gadgets just described.
Gadgets are user friendly because of their graphic nature. But string and
proportional gadgets are not without problems. That is why we want to
employ a few examples that can be built into other programs, but have
been designed specifically with our editor in mind.
Until now the gadgets were always connected with the Newwindow
structure, and the program simply displayed them. This must be
changed, because not all gadgets are needed when you first open a
window. Gadgets are usually inserted temporarily into a window then
removed from the window after the user response has been determined.
The Intuition gadget functions are used to control the display of the
gadgets.
The largest use of gadgets are in requesters. They are collections of
multiple gadgets. Because we need many gadgets, we want to give you
some examples that will be completed in the chapter on requesters.
203
3. INTUITION AND C
3.4.2.1
AMIGA
Gadget functions
First we want to concern ourselves with the problem mentioned above,
that not all gadgets are always wanted when opening a window. In
many programs, having a gadget inserted in a window can be useful.
Intuition has many functions that can do this.
Let's imagine that we've opened a nonnal window. Then, during
program execution, a gadget is placed in the window structure and then
appears on the screen.
We described the operation in single steps because we must proceed in
the same single steps when programming. As a base program you will
use the First_window. c program from section 3.1, including the
global NewWindow structure. Define a toggle select boolean gadget
with the following gadget structure, but don't place it in the
Newwindow structure:
1*3.4.2.1.A.togglegadget *1
SHORT GadgetPairs[]
{
NULL,
120, 40,
60, 20,
GADGHBOX,
RELVERIFY
TOGGLESELECT,
BOOLGADGET,
(APTR)&GadgetBorder,
NULL,
&ToggleText,
NULL,
NULL,
1,
NULL
};
204
1*
1*
1*
1*
1*
NextGadget
LeftEdge, TopEdge
Width, Height
Flags
Activation
*1
*1
*1
*1
*1
1*
1*
1*
1*
1*
1*
1*
1*
Gadget Type
GadgetRender
Select Render
GadgetText
MutualExclude
Speciallnfo
GadgetID
UserData
*1
*1
*1
*1
*1
*1
*1
*1
3.4
ABACUS
GADGETS
A toggle gadget goes into the "selected" status when clicked on, and
when they are clicked on again they are "unselected."
We place this gadget in the window using an Intuition function. It is
called AddGadgetO and has the following syntax:
RealPosition
DO
We fIrst pass the window in which the gadget should be inserted along
with the address of the gadget itself. Next we test for the position that it
should take in the list As a result we get the current position.
For our toggle gadget the command looks like this:
Pos
= AddGadget(FirstWindow,
ToggleGadget, -1L);
The -1 states that the gadget should be inserted as the last thing in the
list.
With this function call the gadget takes control in the window list, but
it cannot yet be seen by the user. It still needs an additional function
call, called automatically when a new window is set up. We call this
function using its name:
RefreshGadgets(Gadgets, Window, Requester);
-222
AO
A1
A2
This function shows all of the given gadgets in the window list. Not
everything new is displayed with this; we test with gadgets to see
which gadget the routine should begin with. That is why we placed our
new gadget at the end of the list. We enter its number, and this is
displayed.
We insert RefreshGadgetO following the AddGadgetO statement:
RefreshGadgets(Pos, FirstWindow, NULL);
This makes the gadget visible. Now let's look at the toggle gadget
check:
1* 3.4.2.1.B.toggle_forever *1
FOREVER
{
Wait(1L
continue;
FirstWindow->UserPort->mp_SigBit);
205
3.
INTUITION AND
AMIGA
MessageClass = message->Class;
code = message->Code;
GadgetPtr = (struct Gadget *) message->IAddress;
SelectMode= GadgetPtr->Flags & SELECTED;
ReplyMsg(message);
switch (MessageClass)
{
case GADGET UP
i f (SelectMode)
{
printf (ltActivated
It);
else
{
printf(ltDeactivated\n lt );
}
break;
case CLOSEWINDOW
Close_All();
exit (TRUE) ;
break;
The test to see if a gadget was pressed is done with a case statement.
The gadget status is also analyzed to see if it is "selected" or
"unselected". A simple AND comparison does this. The program also
displays the status of the gadget in the eLi window. Here is the
complete program listing:
/**************************************
* Program: Toggle-gadget.c
*
* ================================== *
*
*
Comments:
* Author: Date:
*
*
---------- ---------*
10/16/1988 for testing
*
* Wgb
*
only
*
*
*
**************************************1
#include <exec/types.h>
#include <intuition/intuition.h>
struct IntuitionBase *IntuitionBase;
struct Window
*FirstWindow;
struct IntuiMessage *message;
SHORT GadgetPairs[]
{
206
3.4 GADGETS
ABACUS
};
struct Border GadgetBorder
NULL,
10, 40,
60, 20,
GADGHCOMP,
TOGGLESELECT,
BOOLGADGET,
(APTR)&GadgetBorder,
NULL,
&GadgetText,
NULL,
NULL,
1,
NULL,
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
NextGadget
LeftEdge, TopEdge
Width, Height
Flags
Activation
Gadget Type
GadgetRender
Select Render
GadgetText
MutualExclude
Special Info
GadgetID
UserData
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
);
160, 50,
320, 150,
0, 1,
CLOSEWINDOW
GADGETUP,
/*
/*
/*
/*
LeftEdge, TopEdge
Width, Height
DetailPen, BlockPen
IDCMP Flags
*/
*/
*/
*/
WINDOWDEPTH
WINDOWSIZING I
WINDOWDRAG I
WINDOWCLOSE I
SMART_REFRESH,
/* Flags
*/
&BoolGadget,
NULL,
/* First Gadget
/* CheckMark
*/
*/
/* Screen
/* BitMap
*/
*/
100, 50,
640, 200,
*/
*/
WBENCHSCREEN,
};
/* Type
*/
207
3.
INTUITION AND
AMIGA
main ()
{
ULONG MessageClass;
USHORT code;
struct Message *GetMsg();
Wait(lL
continue;
FirstWindow->UserPort->mp_SigBit);
MessageClass = message->Class;
code = message->Code;
GadgetPtr = (struct Gadget *) message->IAddress;
Select Mode = GadgetPtr->Flags & Selected;
ReplyMsg(message);
switch (MessageClass)
{
case GADGETUP
if (SelectMode)
{
printf(nActivated\n");
}
else
printf(nDeactivated\n A ) ;
break;
case CLOSEWINDOW
Close_All();
exit(TRUE);
break;
Open_All ()
{
void *OpenLibrary();
struct Window *OpenWindow();
if (!(IntuitionBase = (struct IntuitionBase *)
OpenLibrary(nintuition.library". OL)))
208
3.4
ABACUS
GADGETS
Close_All ()
{
if (FirstWindow)
if (IntuitionBase)
CloseWindow(FirstWindow);
CloseLibrary(IntuitionBase);
At the moment this new toggle gadget has no further function. What
can turn this gadget on and off? Another gadget, of course. We'll add a
new gadget and let it tum the toggle gadget on and off. This way we
learn about two new functions.
As the second gadget we choose a completely normal boolean gadget to
minimize programming hassles. Here's the structure:
/* 3.4.2.1.C secondgadget */
SHORT SelectPairs[]
{
NULL,
10, 40,
60, 20,
GADGHCOMP
GADGDISABLED,
RELVERIFY,
BOOLGADGET,
(APTR)&SelectBorder,
NULL,
/* NextGadget
/* LeftEdge, TopEdge
/* Width, Height
/* Flags
*/
*/
*/
*/
/*
/*
/*
/*
*/
*/
*/
*/
Activation
Gadget Type
GadgetRender
Select Render
209
3.
INTUITION AND
AMIGA
&Se1ectText,
NULL,
NULL,
2,
NULL,
};
*!
/* GadgetText
/* Mutua1Exc1ude
/* SpecialInfo
*/
*/
/* GadgetID
/* UserData
*/
*/
As usual, we have supplied you with the border and the IntuiText
structure. We now turn our attention to the Select gadget, which is
the same as a normal boolean gadget with the exception that it is
"disabled." It is set in this condition using the GADGDISABLED flag;
the user cannot choose it. The entire click area is displayed in a graphic
status called "ghosted."
This second gadget must also be added to the window. We can construct
a second AddGadgetO function. Intuition supplies another function in
case more gadgets must be added. The entire list of gadgets can be added
to the window with AddGListO. Set a pointer to the second gadget in
the ftrst gadget. Now, you can insert both gadgets into the window list
using the Rea 1 P 0 s i t ion 0 function. The format for
RealPositionO:
Rea1Position = AddGList(Window, Gadget, Position, Numgad,
DO
-438
AO
Al
DO
D1
Requester);
D2
The ftrst three arguments may be familiar to you from the simple functions. By using Numgad you can state how many gadgets the list
contains, and Requester must point to the requester when you need
one. In this case, though, the value is stored with null.
The same problem that we had with AddGadgetO is also encountered
with RefreshGadgetsO. We ftnd the first gadget's position with
AddGListO. We state this with the Refresh function. Only two
gadgets should be newly displayed. For this reason there is also a list
refresh command:
RefreshGList(Gadgets, Window, Requester, Numgad);
-432
AO
Al
A2
DO .
210
3.4
ABACUS
GADGETS
AO
A1
A2
AO
A1
A2
Both functions need the same arguments: a pointer to the gadget whose
status should be changed, the pointer to the window in which the gadget
is found and a pointer to a requester, if it is presenL The last variable
does not concern us at the moment.
Change the check so that when the SELECT flag is positive the
program calls OnGadgetO, and when the flag is negative, the program
calls OffGadgetO. Every time you pass a pointer to the SELECT
gadget:
switch (GadgetNr)
(
case 2
case 1
if (selectmode)
(
printf("activated ");
OnGadget(&SelectGadget, FirstWindow,NULL);
RefreshGadgets(&SelectGadget, FirstWindow,NULL);
}
else
(
printfC"deactivated\n");
OffGadget (&SelectGadget, FirstWindow,NULL);
}
break;
}
break;
Insert this instead of the if check by the case GADGETUP. In addition you must calculate the ushort variable GadgetNr before this
point:
GadgetNr
How it works
= GadgetPrt->GadgetID;
When you have added all of the new sections to the first program, you
should see the following when the program is run:
A window with two gadgets appear. The right gadget turns the left
gadget on and off. The second gadget fIrst appears as "ghosted." When
you click on the right gadget, the type of the other gadget should be
readable, not ghosted. Now you can select this gadget as well. The
program shows its reactions using text in the CLI window. The text
211
3.
INTUITION AND
AMIGA
"activated" and "deactivated" appear for the toggle gadget. When you can
click on the left gadget, the program sends the text "selected!"
Here is the complete listing of the program:
/********~*****************************
*
*
*
* ================================== *
* Program: OnOff-gaggets.c
*
*
Comments:
* Author: Date:
*
*
*
---------- ---------10/16/1988 for testing
*
* Wgb
only
*
*
*
*
**************************************/
#include <exec/types.h>
#include <intuition/intuition.h>
struct IntuitionBase *IntuitionBase;
struct Window
*FirstWindowi
struct IntuiMessage *messagei
SHORT SelectPairs[]
{
NULL,
10, 40,
60, 20,
GADGHCOMP,
RELVERIFY,
BOOLGADGET,
(APTR)&SelectBorder,
NULL,
&SelectText,
NULL,
NULL,
212
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
NextGadget
LeftEdge, Top Edge
Width, Height
Flags
Activation
Gadget Type
GadgetRender
Select Render
GadgetText
MutualExclude
SpecialInfo
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
3.4
ABACUS
2,
NULL,
};
1* GadgetID
1* UserData
GADGETS
*1
*1
SHORT GadgetPairs[]
(
&SelectGadget,
120, 40,
60, 20,
GADGHBOX,
RELVERIFY
TOGGLESELECT,
BOOLGADGET,
(APTR)&GadgetBorder,
NULL,
&BoolText,
NULL,
NULL,
1,
NULL
};
1*
1*
1*
1*
1*
NextGadget
LeftEdge, TopEdge
Width, Height
Flags
Activation
*1
*1
*1
*1
*1
1*
1*
1*
1*
1*
1*
1*
1*
Gadget Type
GadgetRender
Select Render
Gadget Text
MutualExclude
SpecialInfo
GadgetID
UserData
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
160, 50,
320, 150,
0, 1,
CLOSEWINDOW
GADGETUP I
GADGETDOWN,
1*
1*
1*
1*
WINDOWDEPTH
WINDOWSIZING I
WINDOWDRAG I
WINDOWCLOSE I
SMART_REFRESH,
1* Flags
*1
&BoolGadget,
NULL,
1* First Gadget
1* CheckMark
*1
LeftEdge, TopEdge
Width, Height
DetailPen, BlockPen
IDCMP Flags
*/
213
3.
INTUITION AND
AMIGA
NULL,
NULL,
1* Screen
1* BitMap
*1
lOa, 50,
640, 200,
*1
*1
WBENCHSCREEN
};
1* Type
*1
*/
main 0
(
ULONG MessageClass;
USHORT code, selectmode, GadgetNr;
struct Message *GetMsg();
struct Gadget *Gadgetptr;
Wait (1L
continue;
FirstWindow->UserPort->mp_SigBit);
MessageClass = message->Classi
code = message->Code;
GadgetPtr = (struct Gadget *) message->IAddress;
GadgetNr = GadgetPtr->GadgetID;
selectmode= GadgetPtr->Flags & SELECTED;
ReplyMsg(message);
switch (MessageClassl
{
case GADGET UP
case GADGETDOWN
switch (GadgetNr)
{
case 2
case 1
if (selectmode)
(
printf("activated H);
OnGadget(&SelectGadget,
FirstWindow,NULL);
214
3.4
ABACUS
GADGETS
RefreshGadgets(&SelectGadget, FirstWindow,NULL);
}
else
{
printf ("deactivated\n") ;
Of f Gadget (&SelectGadget,
FirstWindow,NULL);
break;
break;
case CLOSEWINDOW
Close_All();
exit(TRUE);
break;
Open_All ()
{
void *OpenLibrary();
struct Window *OpenWindow();
if (! (IntuitionBase = (struct IntuitionBase *)
Open Library ("intuition.library", OL)
{
Close_All ()
{
if (FirstWindow)
if (IntuitionBase)
CloseWindow(FirstWindow);
CloseLibrary(IntuitionBase);
Now that we've finished with boolean gadgets, let's describe the other
two types. First we should turn our attention to the proportional
gadgets, for which there are two special functions. This example will
supply a graphic to the knob. Here is the pertinent Image structure
with the graphic data:
215
3.
INTUITION AND
AMIGA
USHORT KnobGraphic[]
{
OxCCCC,
Ox6666,
Ox3333,
Ox9999,
OxCCCC,
Ox6666,
Ox3333,
Ox9999,
OxCCCC,
Ox6666,
Ox3333,
Ox9999,
OxCCCC,
Ox6666,
Ox3333,
Ox9999
};
struct Image Knob
0, 0,
16, 16,
1,
&KnobGraphic[O],
1, 0,
NULL
};
FREEVERT,
Ox8000,
Ox8000,
Ox0800,
Ox1000,
0,
0,
0,
0,
0,
0
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
Flags
HorizPot
VertPot
HorizBody
Vert Body
CWidth
CHeight
HPotRes
VPotRes
LeftBorder
TopBorder
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
};
struct Gadget Slider
{
NULL,
260, 40,
24, 120,
GADGHNONE
GADGIMAGE,
RELVERIFY,
PROPGADGET,
216
/*
/*
/*
/*
NextGadget
LeftEdge, TopEdge
Width, Height
Flags
/* Activation
/* Gadget Type
*/
*/
*/
*/
*/
*/
3.4
ABACUS
(APTR)&Knob,
NULL,
NULL,
NULL,
(APTR)&SliderProp,
1,
NULL,
};
Note:
/*
/*
/*
/*
/*
/*
/*
GadgetRender
Select Render
GadgetText
MutualExclude
SpecialInfo
GadgetID
UserData
GADGETS
*/
*/
*/
*/
*/
*/
*/
The graphic data of the mover must be stored in chip memory or it will
not be displayed, use the +c option of the Aztec compiler to place this
data in chip memory.
We should read the mover's position. Insert these two lines in the
gadget check:
case GADGETUP :printf("Mover position: %x/n",
moveprop.VertPot);
break;
You can check and evaluate the slider's position in the program using
these methods. Here is a complete example program:
/***************************************
*
*
*
* =============================;===== *
* Program: Prop-Image-Gadget.c
*
*
Comments:
*
* Author: Date:
*
---------- ---------*
10/16/1988 for testing
* Wgb
*
only, place in *
*
chip ram
*
*
***************************************/
iinclude <exec/types.h>
iinclude <intuition/intuition.h>
struct
struct
struct
struct
IntuitionBase
Screen
Window
IntuiMessage
*IntuitionBase;
*FirstScreen;
*FirstWindow;
*message;
USHORT KnobGraphic[)
{
OxCCCC,
Ox6666,
Ox3333,
Ox9999,
OxCCCC,
Ox6666,
Ox3333,
217
3.
INTUITION AND
AMIGA
Ox9999,
OxCCCC,
Ox6666,
Ox3333,
Ox9999,
OxCCCC,
Ox6666,
Ox3333,
Ox9999
};
0, 0,
16, 16,
1,
&KnobGraphic[O],
1, 0,
NULL
};
struct PropInfo SliderProp
{
FREEVERT,
OxBOOO,
OxBOOO,
OxOBOO,
Ox1000,
0,
0,
0,
0,
0,
0
1*
1*
1*
1*
1*
1*
1*
1*
1*
1*
1*
Flags
HorizPot
VertPot
HorizBody
VertBody
CWidth
CHeight
HPotRes
VPotRes
LeftBorder
TopBorder
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
};
NULL,
260, 40,
24, 120,
GADGHNONE
GADGIMAGE,
RELVERIFY,
PROPGADGET,
(APTR)&Knob,
NULL,
NULL,
NULL,
(APTR)&SliderProp,
1,
NULL,
};
218
1*
1*
1*
1*
NextGadget
LeftEdge, Top Edge
Width, Height
Flags
Activation
Gadget Type
GadgetRender
Select Render
GadgetText
MutualExclude
SpecialInfo
GadgetID
/* UserData
1*
1*
1*
1*
1*
1*
1*
1*
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
3.4
ABACUS
GADGETS
160, 0,
/* LeftEdge, TopEdge
320, 200,
/* Width, Height
0, 1,
/* DetailPen, BlockPen
CLOSEWINDOW
/* IDCMP Flags
GADGETUP,
WINDOWDEPTH
/* Flags
WINDOWSIZING I
WINDOWDRAG I
WINDOWCLOSE I
SMART_REFRESH,
&Slider,
/* First Gadget
NULL,
/* CheckMark
(UBYTE *)"Proportional-Gadget",
NULL,
/* Screen
NULL,
/* BitMap
100, 50,
/* Min Width, Height
640, 200,
/* Max Width, Height
WBENCHSCREEN,
/* Type
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
};
main ()
{
ULONG MessageClass;
USHORT code;
struct Message *GetMsg();
FOREVER
{
MessageClass = message->Class;
code = message->Code;
ReplyMsg(message);
switch (MessageClass)
(
219
3. INTUITION AND C
AMIGA
Open_All 0
{
void
*OpenLibrary();
struct Window *OpenWindow();
if (! (IntuitionBase = (struct IntuitionBase *)
Open Library (rrintuition. libraryn, OL)
(
*'
Close_All ()
{
i f (FirstWindow)
if (IntuitionBase)
CloseWindow(FirstWindow);
CloseLibrary(IntuitionBase);
Now you can make changes to the Gadget structure, the Proplnfo
structure and all associated structures. Then it can be added back to the
list with AddGadgetO. To conclude you must update the screen using
RefreshGadgetO
In other cases where entire portions of gadgets are removed, changed and
then reentered in the list, there is an additional function:
220
3.4
ABACUS
GADGETS
-444
AO
Al
DO
Here you give the number of gadgets that should be removed. The
opposite, AddGListO, has already been discussed. Don't forget to
update the graphic with RefreshGListO.
The second way, only for proportional gadgets, saves the time of
deleting and reinserting every gadget from the list The Modi fyP ropO
function lets you change all possible gadget settings in the Proplnfo
structure, after describing the gadget. The gadget is then automatically
redisplayed:
ModifyProp(Gadget, Window, Requester, Flags, HorizPot,
-156
AO
A1
A2
00
01
03
04
AO
A1
A2
DO
01
03
04
05
We would now like to present the structure for a file selector box, used
to select a single file name from a list of many file names. This is used
to load a file into an application program. Examples of these are
loading pictures into a drawing program or loading a text file into an
editor.
If you thought that you could print out the names with the
Int ui text structure, in conjunction with the Gadget structure,
you're wrong. The programming can get rather complicated when a file
list is present that contains more names that can fit in the window. The
text must be scrolled to allow the user to see all of the files.
It would be best to create a routine to display the list of names, beginning with a certain one, in a predetermined order. The number of names
that are to be displayed is the number of gadgets that have no graphics
associated with them. They are only known by their click area and the
221
3.
INTUITION AND
AMIGA
text displayed in them. This cannot be more than one line, 8/9 points,
and must be within the maximum number of characters of a filename.
Because these are all boolean gadgets, we can use the simple gadget
structure:
1*3.4.2.1.D.filegadget_struct*1
struct Gadget FileGadget =
{
NULL,
80, 0,
200, 8,
GADGHCOMP,
RELVERIFY,
BOOLGADGET
REQGADGET,
NULL,
NULL,
NULL,
NULL,
NULL,
0,
NULL,
1*
1*
1*
1*
1*
NextGadget
LeftEdge, TopEdge
Width, Height
Flags
Activation
*1
*1
*1
*1
*1
1*
1*
1*
1*
1*
1*
1*
1*
Gadget Type
GadgetRender
Select Render
GadgetText
MutualExclude
Speciallnfo
GadgetID
UserData
*1
*1
*1
*1
*1
*1
*1
*1
};
Because of the large width of your gadget we would like to ask that you
now load the First_window. c program from Section 3.1 again and
change the window structure there so that the window has a LeftEdge
of 120 and Width of 360. We want to begin developing a file select
box, and we need a wide enough window to do so. Look at the design:
Load/save file
Next dIrectory
level up
String gadgets
Figure 3.11
222
812345678'812345678
812345678'B12345678
812345678'812345678
812345678'01Z345678
I. OK 1
_ 812345678'012345678
II":=:iCancel 8123456m012345678
~
812345678'812345678
81Z345678'812145678
J.CHDIRII
.
. 912345678'812345678
812345678'812345678
912345678'812345678
812345678'812345678
812345678'012345678
Proportional
gadget
Path: _ _ _ _ __
File::------
Boolean
gadgets
3.4
ABACUS
GADGETS
You also need a large field for the filenames, a scroll bar (which you
programmed above), two string gadgets and three boolean gadgets. After
these digressions for an overview of your project, we turn back to the
many gadgets for selecting a name.
With the gadget structure defined first we don't get very far. You need
something more, defining every structure individually is too hard.
Remember the example of the IntuiText structure. There we
reproduced a structure quite simply. That would be handy to have here:
Preceding the main function we define this gadget structure and a field
with unfilled structures.
struct Gadget Files[15];
We need a function in the program that copies the general structure and
at the same time corrects the Y position.
/*3.4.2.1.E.makefl1es*/
Make_Fl1es(Structur)
struct Gadget Struktur[];
{
int i;
for (i=O; 1<15; i++)
{
Struktur[i]
Struktur[i] .TopEdge
Struktur[i] .GadgetID
Struktur[i] .NextGadget
= FileGadget;
= 30
=
=
+ i
i + 1;
* 8;
&Struktur[i+l];
Struktur[14].NextGadget
NULL;
How it works
The function puts the general structure in the structure field and corrects
the Y value (TopEdge). In addition it links the entire list of 15
gadgets together so that you can move them into the window with a
simple call (AddGList (. Furthermore, every gadget receives an ID
beginning with 1. This lets you establish later which of the user gadgets were clicked on. A Refresh is unnecessary in this case because
no graphic is used.
Now insert the above defined proportional gadget into the program.
Change the variable LeftEdge to a value of 300. The ID of these
gadgets must also receive a value greater than 15 so that it is differentiated from the others. We recommend 20, so there is space for enlarging.
You are now missing the two strings. Because there are only two, you
can define them normally through the structure. Here we encounter the
wonderful condition that we only need one Undo buffer because only
one gadget can be active at a time.
223
3. INTUITION AND C
AMIGA
/* 3.4.2.1.F.buffers*/
unsigned char PathBuffer[512] = "df1:";
unsigned char FileBuffer[31]
I
unsigned char UndoBuffer [512];
.....
&PathBuffer[O],
&UndoBuffer[O],
5,
511,
0,
0,
0,
0,
0, 0,
NULL,
0,
NULL,
},
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
Buffer
Undo Buffer
Buffer Position
MaxChars
Display Position
Undo Position
NumChars
Display Counter
CLeft, CTop
Layerptr
Longlnt
AltKeyMap
*/
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
Buffer
Undo Buffer
Buffer position
MaxChars
Display Position
Undo Position
NumChars
Display Counter
CLeft, CTop
LayerPtr
Longlnt
AltKeyMap
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
*/
*/
*/
*/
*/
*1
*1
*1
*/
*1
*1
31,
0,
0,
0,
0,
0, 0,
NULL,
0,
NULL,
};
SHORT StringPairs[]
{
0, 0, 248, 0
},
224
3.4
ABACUS
GADGETS
NULL,
80, 150,
250, 8,
GADGHCOMP,
RELVERIFY,
STRGADGET,
(APTR)&StringBorder,
NULL,
&PAthText,
NULL,
(APTR) &Pathlnfo,
21,
NULL,
/* NextGadget
*/
/* LeftEdge, TopEdge
*/
/* Width, Height
*/
/* Flags
*/
/* Activation
*/
/* Gadget Type
*/
/* GadgetRender
*/
/* Select Render
*/
/* GadgetText
*/
/* MutualExclude
*/
/* SpecialInfo
*/
/* GadgetID
*/
/* UserData
*/
};
&Path,
80, 165,
250, 8,
GADGHCOMP,
RELVERIFY,
STRGADGET,
(APTR)&StringBorder,
NULL,
&FileText,
NULL,
(APTR)&Filelnfo,
22,
NULL,
};
/* NextGadget
*/
/* LeftEdge, TopEdge
*/
/* Width, Height
*/
/* Flags
*/
/* Activation
*/
/* Gadget Type
*/
/* GadgetRender
*/
*/
/* Select Render
/* GadgetText
*/
/* MutualExclude
*/
*/
/* SpecialInfo
/* GadgetID
*/
/* UserData
*/
You need boolean gadgets as the last gadgets for your file select box.
You simply need one border structure, three IntuiText structures,
and three gadget structures:
/*3.4.2.1.G.finalgadgets*/
SHORT SelectPairs[]
{
OK
II
,NULL,
225
3. INTUITION AND C
AMIGA
*)
"Cancel" ,NULL,
*)
"CHDIR" ,NULL,
1, 0, JAM2, 2, 5, NULL,
};
(UBYTE
struct Gadget OK
NULL,
10, 40,
60, 20,
GADGHCOMP,
RELVERIFY,
BOOLGADGET,
(APTR)&SelectBorder,
NULL,
&OKText,
NULL,
NULL,
23,
NULL,
};
1*
1*
1*
1*
1*
1*
1*
1*
1*
1*
1*
1*
1*
NextGadget
LeftEdge, TopEdg~
Width, Height
Flags
Activation
Gadget Type
GadgetRender
Select Render
GadgetText
MutualExclude
SpecialInfo
GadgetID
UserData
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
NextGadget
LeftEdge, TopEdge
Width, Height
Flags
Activation
Gadget Type
GadgetRender
Select Render
/* GadgetText
1* MutualExclude
1* SpecialInfo
1* GadgetID
1* UserData
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
GADGHCOMP,
RELVERIFY,
BOOLGADGET,
(APTR)&SelectBorder,
NULL,
&CancelText,
NULL,
NULL,
24,
NULL,
};
struct Gadget Return
*/
*1
&Cancel,
10, 90,
60, 20,
GADGHCOMP,
RELVERIFY,
BOOLGADGET,
(APTR)&SelectBorder,
NULL,
&ReturnText,
NULL,
226
1*
1*
1*
1*
1*
1*
1*
1*
1* NextGadget
*!
1* LeftEdge, TopEdge *1
1* Width, Height
*1
1* Flags
*/
1* Activation
*1
1* Gadget Type
*1
1* GadgetRender
*1
1* Select Render
*1
1* GadgetText
*1
1* MutualExclude
*1
3.4
ABACUS
NULL,
25,
NULL,
GADGETS
/* SpecialInfo
/* GadgetID
/* UserData
*/
*/
*/
};
Now you have three linked gadget lists: the file gadgets without graphics, the two strings gadgets, and the boolean gadgets. You also have a
proportional gadget. Now construct all of the gadget definitions in the
standard program and think about the window measurements. Then add
the following after the open_AllO function call in the main
function:
/* 3.4.2.1.H.makingmorefiles */
Make_Files(Files);
FilePos = AddGList(FirstWindow, Files, -lL, 15, NULL);
Strgpos = AddGList(FirstWindow, File, -lL, 2, NULL);
SlctPos = AddGList(FirstWindow, Return, -lL, 3, NULL);
PropPos = AddGadget(FirstWindow, Slider, -lL);
RefreshGList(File, FirstWindow, NULL, 2);
RefreshGList(Return, FirstWindow, NULL, 3);
RefreshGList(Slider, FirstWindow, NULL, 1);
3.4.2.2
NULL,
-10, 15,
8, 10,
GADGHCOMP
GRELRIGHT,
/*
/*
/*
/*
NextGadget
LeftEdge, TopEdge
Width, Height
Flags
*/
*/
*/
*/
227
3. INTUITION AND C
AMIGA
RELVERIFY I
RIGHTBORDER,
BOOLGADGET,
(APTR)&SizingImage,
NULL,
NULL,
NULL,
NULL,
0,
NULL,
};
/* Activation
*1
/* Gadget Type
1* GadgetRender
/* Select Render
/* GadgetText
/* MutualExclude
1* SpecialInfo
1* GadgetID
/* UserData
*/
*/
*/
*/
*/
*/
*/
*/
As the Image structure, we ask you to use the structure that has been
implemented at the end of the preceding section. The check routine
should have the following modifications:
/*3.4.2.2.b.modifications */
Sizing()
{
if (Window->LeftEdge
if (Window->Width
==
==
-l*(Window->Height Window->MinHeight);
MoveWindow(Window, -l*Window->LeftEdge,
-l*Window->TopEdge);
SizeWindow(Window, ScreenWidth - Window->Width,
ScreenHeight - Window->Height);
228
3.5 REQUESTERS
ABACUS
3.5
Requesters
Requesters are an extension of gadgets. Gadgets are used by programs to
request infonnation and user selections during program execution.
Sometimes a program must request information or user input
immediately during its execution. The computer must then ask the user
for the information needed, before the program can execute further.
A requester appears to request this information. A requester is a box
containing at least one gadget to aid user response. Text can be added to
the gadgets to clarify the problem or question. If the programmer
prefers, he can construct a requester entirely of graphics.
System
requester
3.5.1
Automatic requesters
Requesters are a collection of many gadgets. You have already seen that
gadget programming is simple but requires many parameters.
Intuition offers a simple solution to this problem: Use the automatic
requester. Automatic requesters consist of two gadgets and one
prompting text. This simple requester doesn't require any complex
programming. The greatest advantage to the automatic requester is it
doesn't need a structure of its own. You simply give the function the
pointer to the lnt ui Text structure and you get a value back when the
user responds to the requester.
You need a pointer to a window to work with your automatic requester.
This must not be initialized.
229
3. INTUITION AND
AMIGA
Arguments
Window
BodyText
PositiveText
Negati veText
PositiveFlags
Negati veFlags
Width
Height
230
3.5 REQUESTERS
ABACUS
/**********************************
*
* ;===;==;====================== *
*
* Program: AutoRequester.c
*
* Author:
*
*
*
*
*
Wgb
Date:
Comments:
---------- ---------12/12/1987
*
*
*
*
**********************************/
finclude <exec/types.h>
finclude <intuition/intuition.h>
struct IntuitionBase *IntuitionBase;
struct Window
*Window = NULL;
struct IntuiText Body
{
0, 1,
JAM2,
10, 10,
NULL,
(UBYTE *)"00 you wish to program requesters?".
NULL
};
struct IntuiText YES
{
2, 3,
JAM2,
5, 3,
NULL,
(UBYTE *)" I do I!!",
NULL
};
struct IntuiText NO
{
2, 3,
JAM2,
5, 3,
NULL,
(UBYTE *)" I don't
NULL
"
I" I
);
main ()
{
BOOL Answer;
231
3.
INTUITION AND
AMIGA
*OpenLibrary ();
if (!(IntuitionBase = (struct IntuitionBase *)
OpenLibrary("intuition.library", OL)
{
Close_All ()
{
if (IntuitionBase)
CloseLibrary(IntuitionBase);
How it works
AutoRequest
gadget.
The auto requester window has one small disadvantage We can't defme
any features of the requester window, and the AutoRequest assigns
the text, gadget graphics and dimensions.
232
3.5 REQUESTERS
ABACUS
3.5.2
System requesters
The use and programming of Auto-requesters is very simple. One
drawback: There is no possibility for outside manipulation from the
programmer.
The AutoRequest () function calls BuildSysRequest () which
does allow modifications. The following stipulations must be met to
work with BuildSysRequest () :
First, a window must be opened that should state the text size. Then we
assign texts for both gadgets. In addition, we need the IDCMP flag for
the requester window and the size of the requester.
The routine constructs a requester based on these arguments, in the
same style as AutoRequest: Body text in the top section of the
requester, gadgets in the lower left and right comers stating positive and
negative responses. A communication channel returns the pointer to the
requester window, either based on the pointer we assigned, or through
parameters given by the Workbench.
We must write the checking routine for the new requester. We can rely
on all IDCMP options for this routine. All input is permissible-it
doesn't matter whether the input comes from the mouse or the keyboard.
When the routine has problems opening the requester, this requester
changes into a recoverable Alert, just like an AutoRequest. Then
BuildSysRequest () returns a true value to a window instead of a
pointer. This tells us which of the two answers were selected.
Here's the format of this requester making function:
Reqwindow = BuiIdSysRequest(Window, BodyText,
DO
-360
AO
Al
PositiveText, NegativeText, IDCMPFIags, Width, Height);
A2
A3
DO
D2
D3
struct
struct
struct
struct
struct
ULONG
SHORT
Window
*ReqWindow;
*Window;
Window
IntuiText
*BodyText;
IntuiText
*PositiveText;
IntuiText
*NegativeText;
IDCMPFIags;
Width, Height;
For your check routine you'll need some information about the gadgets.
The following gadget flags should be set for each gadget:
BOOLGADGET, RELVERIFY, REQGADGET and TOGGLESELECT
233
3.
INTUITION AND
The gadgets should look the same, just to keep things consistent. Use
the AUTO flags in the <intuition/intuition.h> include
fIle:
AUTODRAWMODE
Value Description
JAM2 Background and foreground color
AUTOFRONTPEN
AUTOBACKPEN
OL
lL
Character color 1
Character color 2
AUTOLEFTEDGE
AUTOTOPEDGE
6L
3L
Auto-Name
AUTOITEXTFONT NULL
AUTONEXTTEXT
NULL
3.5.3
234
3.5 REQUESTERS
ABACUS
3.5.3.1
Requester structure
We need a requester structure for each requester. They contain information similar to that found in the window structure:
struct Requester
{
OxOO
Ox04
Ox06
Ox08
OxOA
OxOC
OxOE
OxlO
Ox14
Ox18
OxlC
OxlE
OxlF
Ox23
Ox43
Ox47
Ox4B
Ox6F
.;
00
04
06
08
10
12
14
16
20
24
28
30
31
35
67
71
75
III
235
3.
INTUITION AND
AMIGA
this new graphic also corresponds to the click area of the gadgets,
because these no longer have their own graphic.
Intuition also sets its own flags in this value. The REQOFFWINDOW
flag indicates that the requester is found outside of the bordering
window, and REQACTIVE indicates when the requester is active. The
flag SYSREQUEST is set when it is handled as a system requester (see
AutoRequest and SystemRequest).
The next variable in the structure is BackFill. Here you find the
number of the drawing pen that should fill in the background of the
requester.
ReqLayer is filled from Intuition and contains a pointer to the layer
3.5.3.2
InitRequest(&FileSelectBox);
FileSelectBox.LeftEdge =
FileSelectBox.TopEdge =
FileSelectBox.Width
=
FileSelectBox.Height
=
FileSelectBox.ReqGadget=
Structure
description
236
2;
10;
360;
200;
&Return;
3.5
ABACUS
REQUESTERS
/*3.5.3.2.b. reqgadgetset */
unsigned char PathBuffer[512] = "dfl:";
nn.,
unsigned char FileBuffer[31]
unsigned char UndoBuffer [512];
USHORT KnobGraphic[]
(
OxCCCC,
OxCCCC,
OXCCCC,
OXCCCC,
Ox6666,
Ox6666,
Ox6666,
Ox6666,
Ox3333,
Ox3333,
Ox3333,
Ox3333,
Ox9999,
Ox9999,
Ox9999,
Ox9999
};
FREEVERT,
Ox8000,
Ox8000,
Ox0800,
OxlOOO,
0,
0,
0,
0,
0,
};
&PathBuffer[O], &UndoBuffer[O],
5, 511, 0, 0, 0, 0, 0, 0, NULL, 0, NULL
};
&FileBuffer[O], &UndoBuffer[O],
0, 31, 0, 0, 0, 0, 0, 0, NULL, 0, NULL
};
SHORT StringPairs[]
(
0, 0, 248, 0
};
SHORT SelectPairs[)
{
237
3.
INTUITION AND
AMIGA
NULL,
80, 0, 240, 8,
GADGHCOMP, RELVERIFY, BOOLGADGET I REQGADGET,
NULL, NULL, NULL,
NULL, NULL, 0, NULL
};
&Files [0),
322, 30, 24, 120,
GADGHNONE I GADGIMAGE, RELVERIFY, PROP GADGET I
REQGADGET,
(APTR)&Knob, NULL, NULL,
NULL, (APTR)&SliderProp, 1, NULL
};
struct Gadget Path
&Slider,
80, 150, 250, 8,
GADGHCOMP, RELVERIFY, STRGADGET I REQGADGET.
(APTR)&StringBorder, NULL, &PathText.
NULL, (APTR)&Pathlnfo. 21. NULL
238
3.5
ABACUS
REQUESTERS
}; /*165 pa1*/
struct Gadget File =
{
&Path,
80, 165, 250, 8,
GADGHCOMP, RELVERIFY, STRGADGET I REQGADGET,
(APTR)&StringBorder, NULL, &FileText,
NULL, (APTR)&FileInfo, 22, NULL
}; /*185pal*/
struct Gadget OK
&File,
10, 40, 60, 20,
GADGHCOMP, RELVERIFY I ENDGADGET , BOOLGADGET
REQGADGET,
(APTR)&SelectBorder, NULL, &OKText,
NULL, NULL, 23, NULL
);
3.5.3.3
239
3. INTUITION AND C
AMIGA
can insert a condition for it later, like a menu item selection, for
example.
1* 3.5.3.3.a.requester additions *1
struct NewWindow FirstNewWindow =
{
1* LeftEdge, ~opEdge *1
1* Width, Height
*1
1* DetailPen, B+ockPen *1
1* IDCMP Flags
*1
160, 0,
370, 200,
0, 1, ,
CLOSEWINDOW
GADGETUP I
REQCLEAR,
WINDOWDEPTH
1* Flags
WINDOWSIZING ,
WINDOWDRAG I
WINDOWCLOSE I
SMART_REFRESH,
NULL,
1* First Gadget
NULL,
1* CheckMark
(UBYTE *)"Requester Test",
NULL,
1* Screen
1* BitMap
NULL,
100, 50,
1* Min Width, Height
640, 200,
1* Max Width, Height
WBENCHSCREEN,
1* Type
*1
*1
*1
*1
*1
*1
*1
*1
);
-240
AO
A1
This call blocks the transfer of input. We only get signals from the
requester. It is best if you write a subroutine for handling the requester
input. We form the program very distinctly by calling the routine after
initialization:
FileSelectBox_Request();
The routine itself tests the data transfer as before. We must pay special
attention to the REQCLEAR flag. It indicates that the user has ended
input and would like to return to normal program execution. The signal
is released from all gadgets that set the ENDGADGET flag when
activated. These are the boolean gadgets Cancel and Ok in our case:
240
3.5
ABACUS
REQUESTERS
1***************************************
*
*
*
*
*
*
*
*
Author: Date:
Comments:
---------- ----------
Wgb
12/28/1987
*
*
*
***************************************1
FileSelectBox_Request()
{
Wait(lL FirstWindow->UserPort->mp_SigBit);
printf(nSIGNAL!\nn);
continue;
)
MessageClass = message->Class;
GadgetPtr ~ (struct Gadget *) message->IAddress;
GadgetID s GadgetPtr->GadgetID;
ReplyMsg(message);
if (MessageClass
==
REQCLEAR)
Waiton s FALSE;
printf(nEnd Requester\nn);
continue;
if (MessageClass
switch (GadgetID)
GADGETUP)
case SLIDER
printf(nSlider\nn);
break;
case PATH
printf(npath\n");
break;
case FILE
printf("File\n");
break;
case RETURN
printf("Return!\n R ) ;
241
3.
INTUITION AND
AMIGA
break;
/* Table output */
printf ("Gadget-Nr. %d\n", GadgetID);
}
3.5.4
242
3.5 REQUESTERS
ABACUS
1***************************************
*
*
3.5
*
Program: Custom Requester
*
****************************************
* Author:
* Wgb
Date:
*
*
*
*
*
Comments
12-26-88
* ------------------------
*
*
****************************************/
#include <exec/types.h>
#include <intuition/intuition.h>
struct
struct
struct
struct
IntuitionBase
Screen
Window
IntuiMessage
*IntuitionBase;
*FirstScreen;
*FirstWindow;
*message;
OxCCCC,
OxCCCC,
Oxcccc,
OxCCCC,
};
Ox6666,
Ox6666,
Ox6666,
Ox6666,
Ox3333,
Ox3333,
Ox3333,
Ox3333,
Ox9999,
Ox9999,
Ox9999,
Ox9999
FREEVERT,
Ox8000,
Ox8000,
Oxl000,
Ox0800,
0,
0,
0,
0,
0,
};
struct StringInfo PathInfo
&PathBuffer[O], &UndoBuffer[O],
243
3. INTUITION AND C
SHORT StringPairs[]
{
0, 0, 248, 0
};
SHORT SelectPairs[]
{
244
3.5 REQUESTERS
ABACUS
NULL,
80, 0, 240, 8,
GADGHCOMP, RELVERIFY, BOOLGADGET I REQGADGET,
NULL, NULL, NULL,
NULL, NULL, 0, NULL
};
&Files[O],
322, 30, 24, 120,
GADGHNONE I GADGlMAGE, RELVERIFY, PROP GADGET I
REQGADGET,
(APTR)&Knob, NULL, NULL,
NULL, (APTR)&SliderProp, 1, NULL
}
&Slider,
80, 150, 250, 8,
GADGHCOMP, RELVERIFY, STRGADGET I REQGADGET,
(APTR)&StringBorder, NULL, &PathText,
NULL, (APTR)&Pathlnfo, 21, NULL
}; /*165 pal*/
struct Gadget File
{
&Path,
80, 165, 250, 8,
GADGHCOMP, RELVERIFY, STRGADGET I REQGADGET,
(APTR)&StringBorder, NULL, &FileText,
NULL, (APTR)&Filelnfo, 22, NULL
}; /*185pal*/
struct Gadget OK
&File,
10, 40, 60, 20,
GADGHCOMP, RELVERIFY I ENDGADGET , BOOLGADGET
REQGADGET,
(APTR)&SelectBorder, NULL, &OKText,
NULL, NULL, 23, NULL
};
struct Gadget Cancel
&OK,
10, 65, 60, 20,
GADGHCOMP, RELVERIFY I ENDGADGET , BOOLGADGET
REQGADGET,
(APTR)&SelectEorder, NULL, &CancelText,
NULL, NULL, 24, NULL
};
245
3. INTUITION AND C
AMIGA
&Cancel,
10, 90, 60, 20,
GADGHCOMP, RELVERIFY, BOOLGADGET I REQGADGET,
(APTR)&SelectBorder, NULL, &ReturnText,
NULL, NULL, 25, NULL
}i
struct Requester FileSelectBox;
struct NewWindow FirstNewWindow
{
160, 0,
370, 200,
0, 1,
/* LeftEdge, TopEdge
*/
/* Width, Height
*/
/* DetailPen, BlockPen */
/* IDCMP Flags
*/
CLOSEWINDOW
GADGETUP I
REQCLEAR,
WINDOWDEPTH
/* Flags
WINDOWSIZING I
WINDOWDRAG I
WINDOWCLOSE I
SMART_REFRESH,
NULL,
/* First Gadget
NULL,
/* CheckMark
(UBYTE *) "Requester Test" ,
NULL,
/* Screen
NULL,
/* BitMap
100, 50,
/* Min Width, Height
640, 200,
/* Max Width, Height
WBENCHSCREEN,
/* Type
};
#define
#define
#define
#define
SLIDER 1
PATH 21
FILE
22
RETURN 25
main ()
{
ULONG MessageClass;
USHORT code;
struct Message *GetMsg();
Make_Files(Files);
InitRequester(&FileSelectBox);
FileSelectBox.LeftEdge
2;
FileSelectBox.TopEdge = 10;
FileSelectBox.Width
= 360;
246
*/
*/
*/
*/
*/
*/
*/
*/
3.5 REQUESTERS
ABACUS
FileSelectBox.Height
200;
FileSelectBox.ReqGadget z &Return;
Request(&FileSelectBox, FirstWindow);
FileSelectBox_Request();
EndRequest(&FileSelectBox, FirstWindow);
/*
Close_All();
exit (TRUE) ;
*/
FOREVER
{
MessageClass = message->Class;
code = message->Code;
ReplyMsg(message);
switch (MessageClass)
{
/***************************************
*
*
Author:
Date:
comment:
----------
*
*
*
*
*
*
---------10/16/1987
* Wgb
*
*
*
*
*
**************************************/
void
*OpenLibrary();
struct Window *OpenWindow();
if (! (IntuitionBase = (struct IntuitionBase *)
OpenLibrary ("intuition .library", OL)))
{
247
3.
INTUITION AND
AMIGA
C FOR
ADVANCED PROGRAMMERS
/***************************************
*
*
*
*
*
* Wgb
*
*
Date:
10/16/1987
Comment:
*
*
*
*
just Intuition
and window
*
*
*
*
**************************************/
if (FirstWindow)
if (IntuitionBase)
CloseWindow(FirstWindow);
CloseLibrary(IntuitionBase);
/****************************************
* Function:
*
*
Gadget duplication
*****************************************/
Make_Files (Struktur)
struct Gadget Struktur[];
int i;
for (i=O; i<15; i++)
{
Struktur til
Struktur[i] .TopEdge
Struktur[i] .GadgetID
Struktur[i].NextGadget
FileGadget;
30 +i * 8;
i + 1;
=&Struktur [i+11;
Struktur[14].NextGadget = NULL;
/***************************************
*
*
*
248
*
*
*
3.5
ABACUS
REQUESTERS
*
*
Comments:
* Author: Date:
*
---------- ---------*
*
12/28/1987
* Wgb
*
*
*
*
*
***************************************/
FileSelectBox_Request()
{
Wait(1L FirstWindow->UserPort->mp_SigBit);
printf("SIGNAL!\n");
continue;
)
MessageClass = message->Class;
GadgetPtr = (struct Gadget *) message->IAddress;
GadgetID = GadgetPtr->GadgetID;
ReplyMsg(message);
if (MessageClass == REQCLEAR)
{
Waiton = FALSE;
printf(IIEnd Requester\n");
continue;
}
if (MessageClass
switch (GadgetID)
GADGETUP)
case SLIDER
printf ("Slider\n") ;
break;
case PATH
printf ("Path\n");
break;
case FILE
printf ("File\n");
break;
case RETURN
printf("Return!\n");
break;
249
3.
INTUITION AND
AMIGA
/* Table output */
printf("Gadget-Nr. %d\n", GadgetID);
}
250
3.6
ABACUS
3.6
ALERTS
Alerts
Alerts are relatives of the requester family. Alerts receive top priority
from the system, but they're at the bottom of the user-friendliness list
Both requesters and alerts help get a decision from the user. Let's look
at the differences between alerts and requesters:
You can make requesters in any size, position, and form: Alerts
shove all screens aside, or append themselves to the current
display. In addition, Alerts can only be displayed on half of the
screen at full screen width.
Requesters only pause the task from which you call them: Alerts
halt the entire operating system.
Note:
251
3.
INTUITION AND
3.6.1
AMIGA
3.6.2
Alert type
We first assign the number which Intuition reads and handles as the
alert type. You differentiate between two recognizable types by their
most significant byte of the long variable. A RECOVERY_ALERT
(which returns to the program after the alert display) has a label of 00,
and a DEADEND_ALERT (displays the alert then resets the machine)
has a label of 80.
Alert height
We give the function the height of our alert in screen lines. The alert
then pushes all screen lines below it to fit on the top of the screen.
Alert text
The pointer to the alert text points to one string that states the desired
information. The alert text consists of many alert strings concluded by
continuation bytes. Continuation bytes are either end of line characters
or end of paragraph characters. At the beginning each alert string has a
position statement in pixels, followed by the text in normal string
format. Then we find a byte that reads the next string, if it is unequal to
null. The alert text closes when done loading the strings.
Unfortunately, Intuition supports little of the complications of alert
management. You won't find the structure in the include fIle.
252
3.6
ABACUS
ALERTS
OxOO
Ox02
Ox03
Ox53
Ox54
J;
00
02
03
83
84
50,
50,
50,
50,
24,
34,
44,
54,
n
n
n
n
);
We defined two lables to signal the continuation bytes. NOEND indicates that another text follows, and END indicates the end of the text.
After defining our first alert structure, we can call it with the
DisplayAlertO function. This function has the following format:
Resply
DO
We can insert the following known values into our text program:
Reply
*
* Program: First Alert
* ===================================
Comments:
* Author: Date:
*
*
*
Wgb
12/29/1987
First Alert
*
*
*
*
*
*
*
***************************************/
253
3.
INTUITION AND
AMIGA
#include <exec/types.h>
#include <intuition/intuition.h>
struct IntuitionBase *IntuitionBase;
struct AlertMessage
SHORT LeftEdge;
BYTE TopEdge;
char AlertText[50];
BYTE Flag;
};
<Right Button>
main ()
{
BOOL Reply;
Reply
= DisplayAlert(RECOVERY_ALERT,
&UserAlert, 80L);
if (Reply == TRUE)
printf ("Left mouse button was pressed!\n");
else
printf("Right mouse button was pressed!\n");
1***************************************
*
* =================================== *
*
* Author: Date:
Comments:
*
...
for Intuition
Wgb
10/16/1987
...*
*
***************************************1
254
'3.6 ALERTS
ABACUS
Open_All ()
{
void
*OpenLibrary () ;
1***************************************
*
*
* =================================== *
*
*
* Author: Date:
Comments:
*
* -----*
10/16/1987 for Intuition
* Wgb
*
*
*
*
***************************************/
Close_All ()
{
if (IntuitionBase)
CloseLibrary(IntuitionBase);
How it works
The program first prepares all of the necessary structures for the
function call. Both include files are included because without their
defmitions, the alert is not possible.
If you define the alert string with all of the coordinates and constants,
the main program can call the Open All () function to ensure access
Intuition. Then DisplayAlert() starts and processes the return
value. Everything can be closed again with Close_All ( ) .
to
Note:
3.6.3
255
3.
INTUITION AND
It's important to install additional alerts at the end of all work in the
program. These alerts usually replace error messages given in the eLI
window during the development phases.
We recommend the following system for appending alerts: Your
program is ready and you've already determined all points in the
program at which an alert can replace a simple error message. Now you
can develop the text to be displayed in the proper case.
For this we recommend a general alert design, and maybe a multi-alert.
Multi-alerts insert just a specification on the margin. The alert texts can
be stored in a file accessed from the structure definition. This file is
included last:
#include <exec/devices.h>
#include <graphics/gfxbase.h>
#includes "Disk:Directory/Alert"
In the Alert file we find, for example, the following error messages
for a word processor:
/*******************************************1
1*
*/
1* 3.6.3.a. alert include
*1
1*
*1
1* include : Alert Structure wI Definition */
/*
=========~%==~====~============~=~=~~~~
/*
/* Author:
Date:
comments:
*1
*/
*/
/*
---------- ---------*1
/* Wgb
29.12.1987 for word processor */
1*
*1
1*******************************************1
#include <exec/types.h>
#include <intuition/intuition.h>
struct AlertMessage
{
SHORT LeftEdge;
BYTE TopEdge;
char AlertText[50};
BYTE Flag;
};
256
--
--
------------------------------------------------------------
3.6
ABACUS
ALERTS
50,
50,
50,
50,
50,
24, "
34, "
44, n
54, II
64, II
};
" , NOEND,
, NOEND,
II
II
NOEND,
" , NOEND,
" , END
50,
50,
50,
50,
50,
50,
24, "
34, II
44, II
54, "
64, II
74, II
II
, NOEND,
,
",
,
,
"
NOEND,
NOEND,
NOEND,
NOEND,
END
",
,
",
",
",
NOEND,
NOEND,
NOEND,
NOEND,
END
II
II
II
};
50,
50,
50,
50,
50,
24, "
34, n
44, "
54, II
64, II
};
II
For the DisplayAlert () call we included the height needed for each
alert in a *define. The program text contains the following lines:
if (Window
==
NULL)
== NULL)
257
3.
INTUITION AND
3.6.4
AMIGA
Gurus and why This is especially important if you do your own programming. If you
know why your own program runs into errors in the test phase, you can
usually find the source of the errors. The only problem with the Guru
Meditation is that it always seems to display the same text, and a
number which doesn't give a clue as to the problem. This is something
we can help change. The following tables are used to decipher any Guru
Meditation.
Guru numbers
55
FF
GGGG
AAAAAAAA
Description
AlERT_TYPE
System class
Error class
Exact description
Address of error solving task
Just a word about the Guru codes listed below: We write out the entire
term along with the number code. If an alternate exists, we include the
word OR.
There are only two possibilities for TT. You read about these values
under the label ALERT TYPE. It can be either a DEADEND ALERT
(80) or a RECOVERY_ALERT (00) (only the latter type returns-data).
10000000 I RECOVERY
ALERT
258
100000000 I 68000
3.6 ALERTS
ABACUS
LIBRARIES
01000000
02000000
03000000
04000000
05000000
060000OO
07000000
08000000
09000000
OAOOOOOO
DEVICES
1()()()()()OO
11000000
12000000
13000000
14000000
15000000
exec.library
.graj)hics.library
l~rs.lilLrary
intuition.library
math.library
clist.library
dos.library
ram.library
icon.lib@!Y
expansion.library
audio.device
console.device
gameport.device
k~board.device
trnckdisk.device
timer.device
RESOURCES
22000000 Misc
MIse
Be sure that the error message is at the beginning of the subsystem list
Here the processor automatically gets the words. When this occurs, it
acts as a processor trap. The rest of the locations of the Guru
Meditation have other meanings:
TRAP CODES
259
3.
INTUITION AND
ERROR CLASS
AMIGA
00010000
00020000
00030000
00040000
00050000
000600OO
00070000
The last four numbers of the error code give us still more detailed
information. These are specified in the subsystems:
exec.l.ibrary
01000000
81000002
81000003
81000004
81000005
81000006
81()()()()()7
81000008
81000009
8100000A
graphics.library
82010001
82010002
82010003
82010004
82010005
82010006
82010007
82010008
82010009
8201000A
8201000B
82010030
82011234
l.ayers.l.ibrary
260
3.6 ALERTS
ABACUS
intuition.library
84000000
04000001
84010002
04010003
04010004
84010005
84000006
84010007
84010008
84000009
8401000A
840 1OOOB
8400000c
8400000D
8400000E
8400000F
gadg_et ~ is unknown
type error with AN .,gadget
not enou"Eh memQ!Y for buildinga~rt
not enough memory for a menu
not enough memory for a submenu
not enou.zh memQ!Y for the menu list
incorrect position of the menu list
not enough memQ!Y forQpenScreel!Q
not enough memory for building a RastPort
unknown or erroneous SCREEN TYPE
not enough memory for p;adp;et
not enough memory for window
bad r~ister status when intuition.li~ ~n
incorrect message over the IDCMP
not enough memory for the message stack
not enoygh memQ!Y for the console. device
dos.library
07000001
07000002
07000003
07000004
07000005
07000006
07000007
07000008
07000009
0700000A
0700000B
0700000c
ram.library
108000001 1 erroneous entry in the management list
expansion~ibrary
hardware
trackdisk.device
timer.device
disk.resource
bootstrap
261
3. INTUITION AND C
3.7
others only provide output, while some can perform both tasks. For
example, we can only transmit data over the audio. device.
narrator.device and printer.device. We can only receive
data from the timer.device, keyboard.device and
gameport . device. Data can be transferred in both directions with
the remaining devices. That's good communication.
All of these input and output devices have a disadvantage. When we
want to send and receive multiple data of different types, we need more
than one device to perform the task. Many devices have certain advantages, while others have strong disadvantages. The end result is that the
program opens a number of devices, but the programmer may lose sight
of what's open and what isn't
For this reason the Amiga's developers came up with the InCMP
(Intuition Direct Communications Message Port). This is a
combination of i npu t .dev ice, gamepo rt .dev ice.
timer.device, trackdisk.device and console.device.
with interfacing through Intuition.
The IDCMP is for data processing, filtering and preparation. Through
the assigned window you can specify which data you want to receive
and which data it should convert into the proper format. Or maybe you
would prefer to leave the data unchanged? This is also possible with the
IDCMP. If you would like to have more flexibility, then you should
avoid the graphics.library. Intuition is usually enough to handle
this. The IDCMP receives all input, and the graphic structures make
output possible.
Let's take a closer look at the IDCMP and its versatile input capabilities.
262
ABACUS
3.7.1
NEWSIZE
REFRESHWINDOW
263
3.
INTUITION AND
AMIGA
GADGETUP
CLOSEWINDOW
REQCLEAR
REQVERIFY
MENUVERIFY
264
MOUSEMOVE
DELTAMOVE
ABACUS
NEWPREFS
DISKINSERTED
VANILLAKEY
265
3.
INTUITION AND
AMIGA
Hex value
OxOOOOOOOlL
OxOOOOOOO2L
OxOOOOOOO4L
OxOOO40000L
OxOOO80000L
Remarks
Window flags
GADGETDOWN
GADGETUP
CLOSEWINDOW
Ox00000020L
Ox00000040L
OxOOOOO200L
Gadget flags
REQSET
REQCLEAR
REQVERIFY
OxOOOOOO80L
OxOOOOlOOOL
OxOOOOO800L
Requester flags
MENUPICK
MENUVERIFY
OxOOOOOlOOL
OxOOOO2000L
Menu flags
MOUSEBUTTONS
MOUSEMOVE
DELTAMOVE
OxOOOOOOO8L
OxOOOOOOlOL
OxOO 1OOOOOL
Mouse flags
INTUITICKS
OxOO400000L
timer.device
NEWPREFS
OxOOOO4000L
Preferences change
DISKINSERTED
DISKREMOVED
OxOOOO8000L
OxOOOlOOOOL
trackdisk.device
RAWKEY
VANILLAKEY
OxOOOOO400L
OxOO200000L
input.device
console.device
WBENCHME SSAGE
LONELYMESSAGE
OxOOO20000L
Ox80000000L
Workbench message
No Intuition/IDCMP message
There are a number of ways to tell the system which region you decided
should receive the messages and information. The fIrst and the simplest:
Insert the corresponding flags in the Newwindow structure. Intuition
establishes the IDCMP; you need only worry about the check. The
second way: Use the ModifyIDCMP () function to display a second
window for information display, without establishing a port. Simply
give the window pointer and all of the flags you want set. This either
establishes a new port or closes an existing one. The command has the
following format:
ModifyIDCMP(Window, IDCMPF1ags);
-150
266
AO
DO
ABACUS
The third possibility converts the previous flags to the new flags'
values.
The functions that influence the InCMP, such as REPORTMOUSE () ,
are also important for the second method. Here the same thing happens,
but in a single flag.
3.7.1.1
Oxoo
Ox14
Ox18
OxlA
OxIC
Ox20
Ox22
Ox24
Ox28
Ox2C
Ox30
Ox34
};
267
3.
INTUITION AND
Structure
description
AMIGA
The structure begins with a message structure. This is needed for the
data transfer of messages from Exec. This is interesting and that additional information can contain important data for other purposes. The
other information depends on the flag.
Variables
stemmed.
SpecialLink is a variable that is used from the system.
As you see, the IntuiMessage structure holds all of the data that
can be important for further processing. Here we find the basic data of
Intuition, whose evaluation is dependent on the impulse received.
3.7.1.2
MsgPort structures
'The MsgPort structure named UserPort signals the receipt of the
message. UserPort doesn't lie within the IntuiMessage structure. By setting a bit in the variable mp SigBi t we can wait for a
message. The structure contains the following elements:
268
ABACUS
OxOO
00
OxOO
Ox04
Ox08
Ox09
OxOA
OxOE
OxOE 14
OxOF 15
Ox10 16
Ox14 20
Ox22 34
};
Structure
description
The structure begins with a node structure which helps in recognizing
the port.
mp_Flags labels the type of message:
PA SIGNAL
PA SOFTINT
PA IGNORE
mp SigBi t is the variable that relates to us. The set bit indicates the
You may be confused after reading all this. considering all we want to
do is check the IDCMP. The IDCMP'S complexity stems from its close
relationship with the Amiga's internal signal system. All we need to
understand for now is the mp SigBi t of the IDCMP. We wait for a
message through the signal forthis bit. What can be found in this bit is
immaterial.
3.7.1.3
269
3. INTUITION AND C
AMIGA
Message
= GetMsg(Window->UserPort);
*1
1* Evaluation *1
This message check is very compatible with our multitasking computer. Although it has nothing to do yet, our program constantly
strives to read a message.
It would be better if the task were in "quiet" status until a message
actually is sent. For this there is an Exec function named Wait ().
wai t () lets you inform the system to not process the task until a
message for it appears. The new version of the FOREVER loop looks
like this:
1* 3.7.1.3.b.msgquiet */
FOREVER
{
Wait(lL
continue;
FirstWindow->UserPort->mp_SigBit);
/* Evaluation */
270
ABACUS
3.7.2
We now create checks for individual flag groups, with which we can
manage the data flow.
3.7.2.1
Gadgets
Once you set the pointer to the IntuiMessage structure, we can
begin with the reading. Let's look at how you program this reading to
accept gadget data. You have already read about the development of
gadget checking.
We fIrst test if it is handled as the gadget IDCMP flag:
if (MessageClass & (GADGETDOWN I GADGETUP
{
1* Gadget check *1
271
3. INTUITION AND C
Note:
case TEXTGADGET
Text_Evaluate();
break;
case SLIDER
New_Value 0 ;
break;
case
All further information can come through the pointer to the Gadget
structure. The mouse coordinates can be found in the IntuiMessage
structure-the gadgets are no longer stored here. The mouse coordinate
check is very simple:
MouseX
MouseY
3.7.2.2
=
=
(SHORT) Message->MouseX;
(SHORT) Message->MouseY;
Window messages
Window flag reading controls more than gadgets. Window flags also
return additional status information.
Two possibilities for this check: the first is direct insertion into the
system. We first select all window flags and read them further:
1* 3.7.2.2.a.windowmsg *1
if (MessageClass & (NEWSIZE I REFRESHWINDOW I ACTIVATEWINDOW
INACTIVATEWINDOW
WindowPtr = (struct Window *) Message->IAddress;
switch (MessageClass)
{
272
ABACUS
case NEWSIZE
: TWidth = WindowPrt->Width:
THeight = WindowPrt->Height;
break:
case REFRESHWINDOW
break;
case ACTlVATEWINDOW
break;
case INACTIVATEWINDOW: ActivateWindow(Windowptrl:
break;
Bear in mind that this method cannot check the SIZEVERIFY because
the exact inspection was answered with ReplyMsg () through the
message. Here a simple flag check before each reply will be enough.
You can simply jump to the ReplyMsg () function.
The last line is quite clever. These lines constantly keep the window in
our program active. As soon as the user clicks on another window, the
program reactivates our program window.
The Workbench also uses this method. When you select the Rename
item from the Workbench menu, a window containing a string gadget
opens. The window is the same size as the string gadget, and has no
system graphics. Click anywhere on the Workbench and the Rename
window remains active.
3.7.2.3
Because the End flag receives priority over the requester routine, we
only need to worry about REQSET. One program line circumvents this
problem:
if (MessageClass & REQSET) DMRequest_Evaluation();
273
3. INTUITION AND C
AMIGA
You can test to see if the process released or if the important work must
still be executed using REQVERIFY. REQVERIFY works just like
SIZEVERIFY. When the flag is set, the program frrst receives this
message before Intuition opens the requester. This can frrst be processed
after the message is answered with ReplyMsg ().
3.7.2.4
Here we see two bytes within three bit groups. The first group
(NNNNN) describes the menu number. We must separate this from the
others to get the number. The work executes the definition
MENUNUM (Code), found in the <intuition/intuition .h>
incl ude file.
The second group (MMMMMM) represents the number of the menu item.
It allows 31 menu titles and 63 menu items. To determine the number
of menu items available, use the definition ITEMNUM (Code).
The third group (uuuuu) contains the number of the submenu item if
one is present. The system allows 31 possible values here. These
values can be reached through the definition SUBITEM(Code).
Definitions makes the menu checking very easy. In our check loop for
all of the IDCMP messages, we must frrst determine whether a menu
message exists. Then we can branch off to a function that can make a
further analysis:
if (MessageClass & MENUPICK) Menu_Evaluation(Code);
The function calculates and utilizes the values one after another:
274
ABACUS
/* 3.7.2.4.a.menufigure */
Menu_Analysis (Menunumber)
USHORT Menunumber;
(
case FILEMENU
FileAnalysis(MenuItem, SubItem);
break;
case WORKMENU
WorkAnalysis(MenuItem);
break;
FileAnalysis(MenuItem, SubItem)
USHORT MenuItem, SubItem;
(
$witch(Menultem)
-{
case LOAD
Load() ;
break;
case SAVE
Save ();
break;
case DELETE
switch (Subltem)
(
DelText ();
break;
break;
WorkAnalysis(MenuItem)
USHORT MenuItem;
(
switch (Menultem)
(
case MARK
Mark ();
break;
case EDIT
Edit ();
break;
case SEARCH
Search();
break;
275
3.
INTUITION AND
Instead of numbers we used expressions for the menus, menu items and
submenu items. This makes the program text easier to understand. Here
are the defines for the above routines:
1* 3.7.2.4.b.menu names *1
1* Menu names */
#define FILEMENO
#define WORKMENU
/* 1. Menu
FILEMENU */
#define LOAD
#define SAVE
#define DELETE
1* 2. Menu WORKMENO */
#define MARK
#define EDIT
#define SEARCH
3.7.2.5
Mouse reading
The mouse serves a number of purposes, including gadget selection and
menu item selection. The programmer can also monitor the mouse's
activities. The IDCMP directly reads the mouse. By setting flags we
indicate whether we would like to know that the user pressed or released
mouse buttons, or even that the user moved the mouse. Everything can
be received through the IDCMP.
In a drawing program it can be important to know when the mouse
button was pressed (e.g., so a point can be placed at that location).
There are two mouse buttons, and the system can check for either
button pressed or released. Accordingly there are four possible results:
SELECTDOWN,SELECTUP
MENUDOWN,MENUUP
Be careful that the left mouse button only sends signals if it is not
needed for gadget work (gadget operation has a high priority). The same
goes for the right mouse button: The right mouse button registers only
if the flag RMBTRAP is set.
276
ABACUS
The check looks very simple in the program. We assume that the program needs both mouse buttons and manages no windows. We set the
IDCMP flags MOUSEBUTTONS and RMBTRAP in the window. When
we get a message, the Code array of the IntuiMessage structure
indicates the type of mouse button:
MousX = (SHORT) Message->MouseX;
MousY = (SHORT) Message->MouseYi
if (MessageClass & MOUSEBUTTONS) MausAbfrage(Code, MousX,
MousY) ;
=
=
(SHORT) Message->MouseX;
(SHORT) Message->MouseY;
i f (MessageClass
MousY) ;
&
switch (Flag)
{
case SELECTDOWN
Begin_Line(MousX, MousY);
break;
case SELECTUP
Begin_Erase(MousX, MousY);
break;
case MENUUP
The example function receives the mouse status along with the coordinates as the assignment value. Depending on which mouse status has
priority, either the lines function of the program executes or ends or the
delete function executes. We then assign the frrst or second comer pixel.
The next important point of the mouse check is the position. In the
first example we used this in part because the mouse position is
supplied to every IntuiMessage relative to the window. When we
want to program a penn anent coordinate display, we cannot wait for the
user to press a key to see the coordinates. He must always have the
277
3.
INTUITION AND
AMIGA
current condition. Intuition offers a message type for this. Simply set
the IDCMP flag REPORTMOUSE, and you receive each movemenL
Note:
We would like to warn you at this time! Each small movement of the
mouse, even one pixel, releases a message. You should also consider
that many steps are needed for the exact positioning.
We recommend a check for mouse movement:
MousX = (SHORT) Message->MouseX;
MousY = (SHORT) Message->MouseY;
The subroutine can then handle analysis. With the above mentioned
methods you get the absolute value for every mouse movement, relative
to the IDCMP window. It is easier in many applications if you know
the difference from the last position. For this change we must set the
DELTAMOVE flag.
3.7.2.6
278
ABACUS
..
,
.-
3D
3.
3F
20
2.
2F
10
1F
OF
3C
.3
J!.L
I
1B
3D
3.
20
2'
, HollIe
,
3A
01
PtUp
. .
3F
.A
, En. ,
10
OF
Figure 3.12
Raw Keyboard
codes
1.
Ina
oc
2F
J PtDn
1F
D., ,:43
3C
Note that the numeric keypad has its own numbering system. When
you want to program a check, you must search the Class array for the
RAWKEY flag, and in the Code array for the key number. The
Qualifier array returns a status message concerning special keys
like <Shift>, <Alt> and <etr}>.
279
3.
INTUITION AND
You can identify the key pressed with the following table:
QUALIFIER TYPES
IEQUALIFIER_LSHIFT
IEQUALIFIER_RSHIFT
IEQUALIFIER_CAPSLOCK
Hex value
OxOOO1L
OxOOO2L
OxOOO4L
IEQUALIFIER_CONTROL
OxOOO8L
IEQUALIFIER_LALT
IEQUALIFIER_RALT
OxOO1OL
OxOO20L
IEQUALIFIER_LCOMMAND
IEQUALIFIER_RCOMMAND
OxOO40L
OxOO80L
IEQUALIFIER_NUMERICPAD
Ox01OOL
IEQUALIFIER_REPEAT
Ox0200L
IEQUALIFIER_INTERRUPT
Ox0400L
IEQUALIFIER_MULTIBROADCAST
Ox0800L
IEQUALIFIER_MIDBUTTON
IEQUALIFIER_RBUTTON
IEQUALIFIER_LEFTBUTTON
OxlOOOL
Ox2000L
Ox4000L
IEQUALIFIER RELATIVEMOUSE
Ox8000L
.280
ABACUS
Print(ZChar);
InputTextBuffer(ZChar);
case IEQUALIFIER_LSHIFT
switch (Code)
{
3.7.2.7
281
3.
INTUITION AND
AMIGA
/* 3.7.2.7.a.DISKRMV/INS */
if (MessageClass & DISKREMOVED)
{
/* Clear table */
282
3.8
ABACUS
3.8
MENUS
Menus
Has this ever happened to you in any computer language? You write a
program which does everything for the user except make coffee in the
morning. You've written subroutines to handle every detail, every error,
every contingency. The user has more functions in this program than he
knows what to do with. But you don't know how to present all these
functions in a fann that the user can easily learn and remember.
We could use the keyooard exclusively, then draw a keyboard overlay to
help the user remember. That's kind of silly, since the Amiga has
Intuition. Maybe we can code user access to functions and commands
through gadgets. That's better, but since we still need room for an input
and output window pair, where do we put the gadgets?
It doesn't matter to the Amiga what you use. Gadgets are good for
limited access, but menus allow the most user-friendly environment for
accessing program commands and functions.
The menus offered by Intuition let us divide all of the functions of our
program into groups. Most of these functions go under menu titles
named F i 1 e and Edi t. Each title lists menu items with names
applicable to the titles. The user selects a function by pressing and
holding the right mouse button, moving the mouse pointer to the menu
title, moving the pointer down to the desired item and releasing the
right mouse button. The Workbench menus were probably the first
menus you ever saw on the Amiga.
We'll write program code in this section that creates multiple menus
with multiple items. Some of the menu items even have submenu
items. These menus make use of all of the menu options.
3.8.1
283
3. INTUITION AND C
Now we consider the fIrst menu: The Workbench menu has six functions, none of which can be selected. All of the text appears in ghost
print. If you click on an icon then select the Workbench menu, many
menu items appear in readable print (you can select one of these).
As the second example we11 use an imaginary drawing program for the
3.8.2
Making a menu
You now have an idea of what menus can do, thanks to the two examples listed above. Your fIrst goal is to create your own menu strip. We
would like to open a new screen with some additional arguments. We
need a window, without which a menu strip cannot exist since the two
are interrelated.
The following listing accesses a standard program for screens and
contains additional font settings:
/***************************************
*
*
*
* =================================== *
*
*
* Author: Date:
Comments:
*
* -----*
* Program : Baseprogram.c
* Wgb
*
2/ 3/1988
Empty Menu-Strip*
*
*
***************************************/
#include <exec/types.h>
#include <intuition/intuition.h>
struct
struct
struct
struct
IntuitionBase
Screen
Window
IntuiMessage
*IntuitionBase;
*FirstScreen;
*FirstWindow;
*message;
284
3.8
ABACUS
MENUS
FS_NORMAL,
FPF_ROMFONT
};
0, 0,
640, 200,
3,
0, 1,
/*
/*
/*
/*
HIRES,
/*
CUSTOMSCREEN,
/*
&ScreenFont,
/*
(UBYTE *)"Screen Test",
NULL,
/*
NULL,
/*
);
LeftEdge, TopEdge
Width, Height
Depth
DetailPen, BlockPen
ViewModes
Type
Font
*/
*/
*/
*/
*/
*/
*/
Gadgets
CustomBitMap
*/
*/
0, 0,
640, 100,
0, 1,
LeftEdge, TopEdge
Width, Height
DetailPen, BlockPen
IDCMP Flags
Flags
CLOSEWINDOW,
WINDOWDEPTH I
WINDOWSIZING I
WINDOWDRAG I
WINDOWCLOSE I
SMART_REFRESH,
NULL,
/* First Gadget
NULL,
/* CheckMark
(UBYTE *)"Test Menu-Strip" ,
NULL,
/* Screen (kommt noch)
NULL,
/* BitMap
100, 50,
/* Min Width, Height
640, 200,
/* Max Width, Height
CUSTOMSCREEN,
/* Type
};
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
main ()
(
ULONG MessageClass;
USHORT code;
struct Message *GetMsq();
FOREVER
{
Wait(lL
continue;
FirstWindow->UserPort->mp_SigBit);
285
3.
INTUITION AND
AMIGA
MessageClass = message->Class;
code = message->Code;
ReplyMsg(message);
switch (MessageClass)
{
case CLOSEWINDOW
Close_All () ;
exit (TRUE) ;
break;
/***********************************************
* =========================================== *
*
*
* Author: Date:
* Wgb
10/16/1987
Comments:
functions!
*
*
*
*
*
*
*
***********************************************1
Open_All ()
{
void
*OpenLibrary();
struct Window *OpenWindow();
struct Screen *OpenScreen();
if (! (IntuitionBase = (struct IntuitionBase *)
OpenLibrary(lintuition.library", OL)
{
FlrstNewWindow.Screen
= FirstScreen;
286
3.8 MENUS
ABACUS
1***************************************
*
*
* Function: Close all
*
* =============================-===== *
*
*
* Author: Date:
Comments:
*
*
*
---------- ---------16.10.19S7 Window, Screen *
* Wgb
and Intuition
*
*
*
***************************************1
Close_All ()
{
if (First Window)
if (First Screen)
if (IntuitionBase)
CloseWindow(FirstWindow);
CloseScreen(Firstscreen);
CloseLibrary(IntuitionBase);
3.8.2.1
OxOO
Ox04
Ox06
OxOS
OxOA
OxOC
OxOE
OxOF
Ox13
Ox15
Ox17
Ox19
Ox1B
00
04
06
OS
10
12
14
15
19
21
23
25
);
287
3. INTUITION AND C
AMIGA
Structure
description
*NextMenu
LeftEdge, width
These values aren't currently implemented. Later versions may allow graphics in the menu strip.
Flags
*MenuName
*Menultem
All other variables are used from Intuition and do not need
initialization.
We'll create three menus in our test program to start. The first menu,
called Files, should support all disk functions. It can be used in any
program because disk access is important in most programs. The second
menu will be called Style and the third menu, Graphics.
Here are the three structures. All you need to do is type them in and
append them to the test program, before the main () function.
/* 3.8.2.1.b. menu_titles */
struct Menu Graphics =
{
NULL,
220, 0,
90, 10,
MENUENABLED,
(BYTE *) "Graphics",
NULL
};
struct Menu Style
{
288
3.8
ABACUS
MENUS
&Graphics.
100. 0,
70. 10,
MENUENABLED.
(BYTE *) "Style",
NULL
};
&Style.
10, 0,
60, 10,
MENUENABLED,
(BYTE *) "File",
NULL
};
DO
= SetMenuStrip(Window,
-264
AO
Menu);
Al
The Window lists the window and its address. The menu strip is
already active. You can work with it as long as the window is active.
Our 0 pen _A 11 () function needs the following after the
OpenWindow () function:
SetMenuStrip(FirstWindow, &File);
AD
Remember that you must remove the menu strip before closing the
window, otherwise it could lead to a system crash. In addition, you
must remove the menu strip from the window before any menu change.
Mter the change it can again be added with SetMenuStrip ().
Program
description
289
3.
INTUITION AND
3.8.2.2
We now have the menu texts but need a structure for each menu item.
The Menultem structure reserves all of the important data for each
menu item.
/* 3.S.2.2.h. menuitem */
struct MenuItem
{
OxOO
Ox04
Ox06
OxOS
OxOA
OxOC
OxOE
Ox12
Ox16
Ox1A
290
00
04
06
OS
10
12
14
IS
22
26
3.8
ABACUS
Ox1B
Ox1F
Ox21
);
27
31
33
MENUS
Structure
description
*NextItem
LeftEdge,TopEdge
SelectFill
Command
NextSelect
Flags
291
3.
INTUITION AND
Flags
AMIGA
1* 3.8.2.2.c.menuitems *1
struct Menultem Programend =
{
NULL,
1, 40,
120, 10,
NULL,
);
&Programend.
292
3.8 MENUS
ABACUS
1, 26,
120, 10,
ITEMTEXT I ITEMENABLED I HIGHCOMP,
0,
(APTR) &DeleteText,
NULL,
0,
NULL,
o
};
&Delete,
1, 14,
120, 10,
ITEMTEXT I ITEMENABLED I HIGHCOMP,
0,
(APTR)&SaveText,
NULL,
0,
NULL,
o
};
&Save,
1, 2,
120, 10,
ITEMTEXT I ITEMENABLED I HIGHCOMP,
0,
(APTR) &LoadText ,
NULL,
0,
NULL,
o
};
*
*
*
*
*
*
*
*
Comments:
* Author: Date:
*
*
---------- ---------*
* Wgb
2/ 3/1988 Menu-Strip
*
one menu built *
*
*
*
***************************************/
#include <exec/types.h>
#include <intuition/intuition.h>
293
3.
INTUITION AND
struct
struct
struct
struct
IntuitionBase
Screen
Window
IntuiMessage
*IntuitionBase;
*FirstScreen;
*FirstWindow;
*message;
(STRPTR) "topaz.font",
TOPAZ_SIXTY,
FS_NORMAL,
FPF_ROMFONT
);
0, 0,
640, 200,
3,
0, 1,
1*
1*
1*
1*
1*
1*
1*
LeftEdge, TopEdge
Width, Height
Depth
DetailPen, BlockPen
ViewModes
Type
Font
HIRES,
CUSTOMSCREEN,
&Font,
(UBYTE *) "Screen Test",
NULL,
1* Gadgets
NULL,
1* CustomBitMap
*1
*1
*1
*1
*1
*/
*1
*1
*1
};
0, 0,
640, 100,
0, 1,
1*
1*
1*
1*
1*
LeftEdge, TopEdge
Width, Height
DetailPen, BlockPen
rDCMP Flags
Flags
CLOSEWINDOW,
WINDOWDEPTH I
WINDOWSIZING I
WINDOWDRAG I
WINDOWCLOSE I
SMART_REFRESH,
NULL,
1* First Gadget
NULL,
1* CheckMark
(UBYTE *)"Test Menu-Strip",
NULL,
1* Screen (kommt noch)
NULL,
1* BitMap
100, 50,
1* Min Width, Height
640, 200,
1* Max Width, Height
CUSTOMSCREEN,
1* Type
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
);
294
3.8 MENUS
ABACUS
(APTR)&ProgramendText,
NULL,
0,
NULL,
);
&Programend,
1, 26,
120, 10,
ITEMTEXT J ITEMENABLED I HIGHCOMP,
0,
(APTR) &DeleteText,
NULL,
0,
NULL,
};
struct Menultem Save
&Delete,
1, 14,
120, 10,
ITEMTEXT I ITEMENABLED I HIGHCOMP,
0,
(APTR)&SaveText,
NULL,
0,
NULL,
};
295
3.
INTUITION AND
AMIGA
(APTR)&LoadText,
NULL,
0,
NULL,
};
MENUENABLED,
(BYTE *) "Graphics",
NULL
};
&Graphic,
100, 0,
70, 10,
MENUENABLED,
(BYTE *) "Style",
NULL
};
&Style,
10, 0,
60, 10,
MENUENABLED,
(BYTE *) "File",
&Load
};
main ()
(
ULONG MessageClass;
USHORT code;
struct Message *GetMsg();
296
3.8 MENUS
ABACUS
FOREVER
{
Wait(1L FirstWindow->OserPort->mp_SigBit);
continue;
}
MessageClass = message->Class;
code = message->Code;
ReplyMsg(message);
switch (MessageClass)
{
case CLOSEWINDOW
Close_All () ;
exit(TROE);
break;
/***********************************************
* =========================================== *
*
*
* Author: Date:
Comments:
*
*
*
* Wgb
16.10.1987
functioning!
*
*
*
*
*
***********************************************/
Open_All ()
{
void
*OpenLibrary();
struct Window *OpenWindow();
struct Screen *OpenScreen();
if (! (IntuitionBase = (struct IntuitionBase *)
OpenLibrary("intuition.library", OL)
{
297
3. INTUITION AND C
FirstNewWindow.Screen = FirstScreen;
if (!(FirstWindow = (struct Window *)
OpenWindow(&FirstNewWindow)
{
SetMenuStrip(FirstWindow, &File);
/***************************************
* Author: Date:
*
---------10/16/1987
* Wgb
*
Comments:
----------
Window, Screen
and Intuition
*
*
*
*
*
***************************************1
Close_All ()
{
if
if
if
if
(FirstWindow)
(FirstWindow)
(FirstScreen)
(IntuitionBase)
C1earMenuStrip(FirstWindow);
CloseWindow(FirstWindow);
CloseScreen(FirstScreen);
CloseLibrary(IntuitionBase);
3.8.2.3
298
3.8 MENUS
ABACUS
NULL,
1, 40,
170, la,
ITEMTEXT
ITEMENABLED
HIGHCOMP
COMMSEQ,
HIGHCOMP
COMMSEQ,
HIGHCOMP
COMMSEQ,
0,
(APTR)&ProgramendText,
NULL,
Ox45,
NULL,
a
};
&Programend,
1, 26,
170, 10,
ITEMTEXT
ITEMENABLED
0,
(APTR)&De1eteText,
NULL,
Ox44,
NULL,
a
};
&Delete,
1, 14,
170, 10,
ITEMTEXT
ITEMENABLED
0,
(APTR)&SaveText,
299
3.
INTUITION AND
AMIGA
NULL,
Ox53,
NULL,
o
};
&Save,
1, 2,
170, 10,
ITEMTEXT I ITEMENABLED I HIGHCOMP I COMMSEQ,
0,
(APTR)&LoadText,
NULL,
Ox4C,
NULL,
o
Note:
Highlighting
You can change the text position or the font. Many options are
possible with this code.
We must remember to change the MenuItem structure. For one, the
HIGHIMAGE flag must replace the HIGHCOMP flag, and then the
SelectFill pointer must be supplied with the pointer to our text:
300
3.8 MENUS
ABACUS
1*3.8.2.3.c.highimage_struct*/
struct Menultem Delete =
{
&Programend.
1, 26.
170. 10.
O.
(APTR)&DeleteText.
(APTR)&DeleteTextII.
Ox44.
NULL.
};
/*3.8.2.3.d.newsave_struct*/
struct Menultem Save =
{
&Delete,
1. 14.
170, 10,
};
301
3.
INTUITION AND
3.8.2.4
Submenus
We're not done yet-we need to go deeper yet in the menu strip. The
submenus are next. These menus appear when we select a menu item
which requires additional parameters or information.
How do we access a submenu? The Menultem structure performs this
task (see Section 3.8.2.2). We find a pointer to a Subltem therethat's our entrance to the submenu. If you insert the pointer to a new
Menultem structure, that acts as the beginning of the new submenu.
These methods give you the option of expanding the File menu. With
a File menu in a word processor, you may want to enter the ftle type
of the text you want loaded/saved. Enter the submenu. For our example
you can choose from three ftletypes: Text (resident text format used by
the word processor), ASCII (unformatted text file to allow transfer to
any computer), and IFF (Interchange File Format, Electronic Arts
universal ftle exchange format).
/*3.8.2.4.a.text_stuff*/
struct IntuiText TextText =
{
NULL,
140, 22,
100, 10,
ITEMTEXT I ITEMENABLED I HIGHCOMP I COMMSEQ,
0,
(APTR)&IFFText,
NULL,
Ox49,
NULL,
o
};
302
3.8
ABACUS
MENUS
&IFF,
140, 12,
100, 10,
(APTR)&ASCIIText,
NULL,
Ox41,
NULL,
o
};
&ASCII,
140, 2,
100, 10,
(APTR) &TextText,
NULL,
Ox54,
NULL,
o
};
&Save,
1, 2,
170, 10,
(APTR)&LoadText,
NULL,
0,
&Text,
o
};
The same submenu must be expanded with the Save function. You
must expand the Me nul t em structure with the pointer to the
submenus and remove the old command sequence:
1*3.8.2.4.b.bigsave_struct*1
struct Menultem Save =
&Delete,
1, 14,
170, 10,
(APTR)&SaveText,
NULL,
0,
303
3.
INTUITION AND
AMIGA
&SaverText,
o
};
NULL,
140, 22,
100, 10,
ITEMTEXT I ITEMENABLED I HIGHCOMP I COMMSEQ.
0,
(APTR)&IFFText,
NULL,
Ox46,
NULL,
};
&SaveIFF,
140, 12,
100, 10,
ITEMTEXT I ITEMENABLED I HIGHCOMP I COMMSEQ,
0,
(APTR)&ASCIIText,
NULL,
Ox43,
NULL,
o
};
&SaveASCII.
140, 2,
100, 10,
ITEMTEXT I ITEMENABLED I HIGHCOMP I COMMSEQ.
0,
(APTR)&TextText.
NULL,
Ox51,
NULL,
304
3.8 MENUS
ABACUS
3.8.2.5
each bit represents a menu item. All of the menu items marked with a
set bit should be deactivated when a particular menu item is selected.
Bit 0 corresponds to the first menu item, bit 1 to the second menu
item, etc.
3.8.2.6
Graphic menus
After adding type style choices to the Style menu, you have the
option of adding graphics and color choices to the Graphic menu.
This menu goes well in a drawing or paint program.
The menu consists of two menu items. The first, Pen, has a submenu
with two more subitems: A fine point brush graphic and a thick point
brush graphic. The second menu item, Palette, has eight submenu
305
3.
INTUITION AND
AMIGA
items which specify the drawing color. This can be useful for both the
drawing program and the word processor alike.
Here are the Menultem structures for the Pen menu:
/*3.8.2.6.a.brushmenu_struct*/
struct MenuItem PenII =
{
NULL,
90, 2,
10, 10,
ITEMENABLED I HIGHCOMP,
0,
(APTR)&PenIIImage,
NULL,
0,
NULL,
};
&PenII,
102, 2,
10, 10,
ITEMENABLED I HIGHCOMP,
0,
(APTR)&PenIImage,
NULL,
0,
NULL,
};
&Colour,
2, 2,
100, 10,
ITEMTEXT t ITEMENABLED I HIGHCOMP,
0,
(APTR)&PenText,
NULL,
0,
&PenI,
a
};
306
3.8 MENUS
ABACUS
OxOO,
OxOl,
OxOO,
OxOO,
OxOO,
OxFO,
OxOO,
OxOO,
};
USHORT PenIIData[]
{
OxOO,
OxOl,
OxOl,
OxOO,
OxOO,
OxFO,
OxFO,
OxOO,
};
1, 1,
8, 8,
1,
&PenIData[O],
2, 0,
NULL
};
1, 1,
8, 8,
2,
&PenIIData[O],
2, 0,
NULL
};
The data for the color choice submenu follow. We define a MenuItem
structure for each menu item. The submenu represents every color
through In t ui Text structure. First, the main menu:
/*3.8.2.6.d.colortext*/
struct IntuiText ColornText
{
NULL,
2, 14,
100, 10,
ITEMTEXT I ITEMENABLED I HIGHCOMP,
307
3.
INTUITION AND
0,
(APTR)&ColornText,
NULL,
0,
&ColorO,
o
);
.. , NULL
};
.. , NOLL
A, NULL
);
/*3.8.2.6.f.color2*/
struct Menultem Color2
{
&Color3,
90, 26,
50, 10,
ITEMTEXT I ITEMENABLED I HIGHBOX,
0,
(APTR)&Co12Text,
NULL,
0,
NULL,
o
);
308
3.8
ABACUS
MENUS
0,
(APTR) &CollText,
NULL,
0,
NULL,
o
};
&Colorl,
90, 2,
50, 10,
(APTR)&Co10Text,
NULL,
0,
NULL,
};
* =================================== *
*
*
Comments:
*
* Author: Date:
*
---------- ---------*
* Wgb
*
*
2/ 3/1988
Menu-Strip
*
*
***************************************/
#include <exec/types.h>
#include <intuition/intuition.h>
struct
struct
struct
struct
IntuitionBase
Screen
Window
IntuiMessage
*IntuitionBase;
*FirstScreen;
*FirstWindow;
*message;
(STRPTR) "topaz.font",
TOPAZ_SIXTY,
FS_NORMAL,
FPF_ROMFONT
};
309
3.
INTUITION AND
0, 0,
640, 200"
/*
/*
3,
/*
0, 1,
/*
HIRES,
/*
CUSTOMSCREEN.
/*
&Font,
/*
(UBYTE *)"Screen Test",
NULL,
/*
NULL,
/*
};
LeftEdge, TopEdge
Width, Height
Depth
DetailPen, BlockPen
ViewModes
Type
Font
*/
*/
*/
*/
*/
*/
*/
Gadgets
CustomBitMap
*1
*1
1* LeftEdge, TopEdge
*/
/*
/*
/*
/*
*1
*1
*1
Width, Height
DetailPen, BlockPen
IDCMP Flags
Flags
CLOSEWINDOW,
WINDOWDEPTH I
WINDOWSIZING I
WINDOWDRAG I
WINDOWCLOSE I
SMART_REFRESH.
NULL,
/* First Gadget
NULL,
/* CheckMark
(UBYTE *)"Test Menu-Strip" ,
NULL,
1* Screen (kommt noch)
NULL,
/* BitMap
100, 50,
/* Min Width, Height
640, 200,
1* Max Width, Height
CUSTOMSCREEN,
/* Type
}I
USHORT PenIData[) =
{
OxOO,
OxOl,
OxOO,
OxOO,
OxOO,
OxFO,
OxOO.
OxOO.
};
USHORT PenIIData[]
{
OxOO,
Ox01,
OxOl,
OxOO,
OxOO,
OxFO,
OxFO,
OxOO,
};
1, 1,
8, 8,
1,
310
*/
*1
*1
*1
*1
*/
*1
*1
3.8 MENUS
ABACUS
&PenIData[O] ,
2, 0,
NULL
};
struct Image PenIIImage
{
1, 1,
8, 8,
2,
&PenIIData[O) ,
2, 0,
NULL
};
311
3. INTUITION AND C
AMIGA
};
",
NULL
" , NULL
" , NOLL
" , NULL
" , NULL
" , NOLL
" , NOLL
(OBYTE *)"
" , NOLL
(OBYTE *)"
" , NOLL
};
312
3.8
ABACUS
MENUS
ITEMENABLED I HIGHCOMP,
0,
(APTR)&PenIIlmage,
NULL,
0,
NULL,
o
};
&PenII,
102, 2,
10, 10,
ITEMENABLED I HIGHCOMP,
0,
(APTR)&Penllmage,
NULL,
0,
NULL,
o
};
NULL,
90, 98,
50, 10,
(APTR)&CoI8Text,
NULL,
0,
NULL,
o
};
&Color8,
90, 86,
50, 10,
(APTR)&Col?Text,
NULL,
0,
NULL,
o
};
313
3.
INTUITION AND
&Color7,
90, 74,
50, 10,
ITEMTEXT I ITEMENABLED I HIGHBOX,
0,
(APTR)&Co16Text,
NULL,
0,
NULL,
o
);
&Color6.
90, 62,
50, 10,
ITEMTEXT I ITEMENABLED I HIGHBOX,
0,
(APTR)&Co15Text,
NULL,
0,
NULL,
o
);
&ColorS,
90, 50,
50, 10,
ITEMTEXT I ITEMENABLED I HIGHBOX,
0,
(APTR)&Co14Text,
NULL,
0,
NULL,
};
&Color4,
90, 38,
50, 10,
ITEMTEXT I ITEMENABLED I HIGHBOX,
0,
(APTR)&Co13Text,
NULL,
0,
NULL,
};
314
3.8 MENUS
ABACUS
&Color3,
90, 26,
50, 10,
ITEMTEXT I ITEMENABLED I HIGHBOX,
0,
(APTR)&Co12Text,
NULL,
0,
NULL,
};
struct Menultem Color1
{
&Color2,
90, 14,
50, 10,
ITEMTEXT I ITEMENABLED I HIGHBOX,
0,
(APTR)&CollText,
NULL,
0,
NULL,
t;
struct Menultem Col orO
{
&Color1,
90, 2,
50, 10,
ITEMTEXT I ITEMENABLED I HIGHBOX,
0,
(APTR)&ColOText,
NULL,
0,
NULL,
);
NULL,
2, 14,
100, 10,
ITEMTEXT I ITEMENABLED I HIGHCOMP,
0,
(APTR)&ColorText,
NULL,
0,
&ColorO,
o
};
315
3.
INTUITION AND
(APTR)&PenText,
NULL,
0,
&PenI,
a
};
NULL,
140, 22,
100, 10,
(APTR)&IFFText,
NULL,
Ox49,
NULL,
};
(APTR)&ASCIIText,
NULL,
Ox41,
NULL,
o
};
&ASCII,
140, 2,
100, 10,
(APTR)&TextText,
NULL,
Ox54,
NULL,
316
3.8
ABACUS
MENUS
};
NULL,
140, 22,
100, 10,
ITEMTEXT
ITEMENABLED
HIGHCOMP
COMMSEQ,
HIGHCOMP
COMMSEQ,
HIGHCOMP
COMMSEQ,
COMMSEQ,
0,
(APTR) &IFFText,
NULL,
Ox46,
NULL,
};
&SaveIFF,
140, 12,
100, 10,
ITEMTEXT
ITEMENABLED
0,
(APTR)&ASCIIText,
NULL,
Ox43,
NULL,
};
&SaveASCII,
140, 2,
100, 10,
ITEMTEXT I ITEMENABLED
0,
(APTR)&Te~tText,
NULL,
Ox51,
NULL,
};
NULL,
1, 40,
170, 10,
ITEMTEXT
ITEMENABLED
HIGHCOMP
0,
(APTR)&ProgramendText,
NULL,
Ox45,
NULL,
317
3.
INTUITION AND
o
};
struct Menultem Delete
{
&Programend,
1, 26,
170, 10,
ITEMTEXT I ITEMENABLED I HIGHIMAGE I COMMSEQ,
0,
(APTR}&DeleteText,
(APTR)&DeleteTextII,
Ox44,
NULL,
};
&Delete,
1, 14,
170, 10,
ITEMTEXT I HIGHCOMP I COMMSEQ,
0,
(APTR}&SaveText,
NULL,
Ox53,
&SaverText ,
};
&Save,
1, 2,
170, 10,
ITEMTEXT I ITEMENABLED I HIGHCOMP,
0,
(APTR) &LoadText ,
NULL,
0,
&Text,
o
};
NULL,
220, 0,
90, 10,
MENUENABLED,
(BYTE *) "Graphics",
&Pen
};
318
ABACUS
3.8 MENUS
&Graphic,
100, 0,
70, 10,
MENUENABLED,
(BYTE *) "Design",
NULL
};
MENUENABLED,
(BYTE *) "File",
&Load
};
main ()
{
ULONG MessageClass;
USHORT code;
struct Message *GetMsg();
FOREVER
{
Wait(lL
continue;
FirstWindow->UserPort->mp_SigBit);
MessageClass = message->Class;
code = message->Code;
ReplyMsg(message);
switch (MessageClass)
{
case CLOSEWINDOW
Close_All () ;
exit (TRUE) ;
break;
/***********************************************
319
3.
INTUITION AND
AMIGA
* =========================================== *
*
*
Author: Date:
Comments:
Wgb
functioning!
---------- ----------
16.10.1987
*
*
*
*
*
*
*
*
***********************************************1
Open_All 0
(
void
*OpenLibrary () ;
struct Window *OpenWindow();
struct Screen *OpenScreen();
if (! (IntuitionBase = (struct IntuitionBase *)
OpenLibrary(nintuition.libraryn, OL)
{
FirstNewWindow.Screen
FirstScreen;
SetMenuStrip(FirstWindow, &File);
1***************************************
*
*
* Function: Close all
*
* =================================== *
*
*
* Author: Date:
Comments:
*
*
---------- ---------*
* Wgb
10/16/1987 Window, Screen *
and Intuition
*
*
*
*
320
3.8 MENUS
ABACUS
***************************************/
Close_All ()
{
if
if
if
if
(FirstWindow)
(FirstWindow)
(FirstScreen)
(IntuitionBase)
ClearMenuStrip(FirstWindow);
CloseWindow(FirstWindow);
CloseScreen(FirstScreen);
CloseLibrary(IntuitionBase);
3.8.3
Wait(lL
continue;
FirstWindow->UserPort->mp_SigBit);
MessageClass = message->Class;
Code = message->Code;
ReplyMsg(message);
switch (MessageClass)
(
case CLOSEWINDOW
Close_AII();
exit (TRUE) ;
break;
case MENUPICK
Menu_Analysis(Code);
break;
321
3.
INTUITION AND
AMIGA
ItemAddress(&File, Menunumber);
switch (Menu)
(
case 0 : /* Load */
break;
case 1 : /* Save */
322
ABACUS
3.8 MENUS
break;
case 2 : /* Delete */
Delete();
break;
case 3 : /* Program end */
Ende = FALSE;
break;
The last two items finish a process. Only the first two must be read for
now:
/*3.8.3.d.load_save*/
case 0 : /* Load */
switch (SubItem)
{
case 0 : /* Text */
Load(TEXT);
break;
case 1 : /* ASCII */
Load (ASCII) ;
break;
case 2 : /* IFF */
Load(IFF);
break;
break;
case 1 : /* Save */
switch (Subltem)
{
case 0 : /* Text */
Save (TEXT) ;
break;
case 1 : /* ASCII */
Save (ASCII) ;
break;
case 2 : /* IFF */
Save (IFF) ;
break;
break;
All submenu accesses call the same function. They pass parameters (in
this case, self-defined flags). TEXT tells the Load () or Save ()
structures that it is waiting for the command sequences needed to write
out/load normal text. ASCII would strip out any formatting and IFF
would save/load data in Interchange File Format.
The second menu requires an alternate method of reading. We have no
submenus, only a main menu where many features interact with one
323
3.
INTUITION AND
AMIGA
another. Only the text width is incompatible. Assuming that the program uses one variable for the text type and one for character width, the
following function makes sense:
/*3.S.3.e.textstyle*/
switch (MenuItem)
(
case 0 : /* Normal */
TextStyle = NULL;
break:
case 1 : /* Italics */
TextStyle ~= ITALIC:
break:
case 2 : /* Bold */
TextStyle ~= BOLD
break:
case 3 : /* Inverse */
TextStyle ~= INVERS;
break;
case 4 : /* SO Characters */
TextFont = SOL;
break;
case 5 : /* 60 Characters *1
TextFont = 60L;
break:
The third menu lets us return to simpler reading. The Pen item is fIrst;
the PenNr variable contains the number of the selected pen. The
variable ColorNr contains the color number after selecting that color:
/*3.S.3.f.brush_color*/
switch (Menultem)
{
case 0 : 1* Brush *1
PenNr = Sub Item + 1;
break;
case 1 : 1* Color *1
ColNr = SubItem;
break;
324
3.8 MENUS
ABACUS
3.8.4
325
3.
INTUITION AND
AMIGA
/* alternate command-key */
/* no SubItem list for SubItems */
NULL,
NULL,
OxFFFF
I;
struct IntuiText IText2 = {
3,6, JAM2,
/* front and back text pens and drawmode
/* XY origin relative to container TopLeft
20,1,
NULL,
1* font pointer or NULL for defaults
1* pointer to text
1* next IntuiText structure
NULL
.,
*1
*1
*1
*1
*1
I:
struct MenuItem SubItem7 = {
&SubItem8,
34,52,
I;
struct IntuiText IText3 = I
3,5, JAM2,
1* front and back text pens and drawmode
20,1,
/* XY origin relative to container TopLeft
NULL,
1* font pointer or NULL for defaults
II ,
/* pointer to text
NULL
1* next IntuiText structure
*/
*1
*1
*/
*/
I;
struct MenuItem SubItem6
&SubItem7,
34,42,
68,10,
1* hit box width and height
CHECKIT+ITEMTEXT+ITEMENABLED+HIGHBOX,
1* Item flags
0,
1* each bit mutually-excludes a same-level Item
(APTR) &IText3 , 1* Item render (IntuiText or Image or NULL)
NULL,
1* Select render
NULL,
1* alternate command-key
NULL,
1* no SubItem list for SubItems
OxFFFF
1* filled in by Intuition for drag selections
*1
*1
*1
*1
*1
*1
*1
*1
NULL
1* next IntuiText structure
*1
*1
*1
*1
*1
I;
I;
I;
326
*1
*1
*1
*1
*1
*1
*1
*1
*/
ABACUS
3.8 MENUS
*1
*/
*/
*1
*/
Ji
struct IntuiText IText6 = {
3,2,JAM2,
1* front and back text pens and drawmode
20,1,
/* XY origin relative to container TopLeft
NULL,
1* font pointer or NULL for defaults
1* pointer to text
",
NULL
/* next IntuiText structure
I;
struct MenuItem SubItem3
&SubItem4,
34,12,
*/
*/
*/
*/
*/
Ii
*/
*1
*1
*1
*1
I;
struct IntuiText IText8 = {
3, 0, JAM2,
/* front and back text pens and drawmode *1
20,1,
1* XY origin relative to container TopLeft *1
327
3.
INTUITION AND
NULL,
NULL
I;
struct Menultem Sublteml - t
&Subltem2,
34,-8,
I;
struct IntuiText IText9 - {
3, 1, COMPLEMENT,
1* front and back teKt pens and drawmode
1,1,
1* XY origin relative to container TopLeft
NULL,
1* font pointer or NULL for defaults
"Color",
1* pointer to text
NULL
1* next IntuiText structure
I;
*1
*1
*1
*1
*1
Ii
struct IntuiText IText10 - f
3, 1, COMPLEMENT,
1* front and back teKt pens and drawmode
1,1,
1* XY origin relative to container TopLeft
NULL,
1* font pointer or NULL for defaults
"!!!",
1* pointer to teKt
NULL
1* neKt IntuiTeKt structure
I;
*1
*1
*1
*1
*1
I;
struct IntuiText IText11 - f
3, 1, COMPLEMENT,
1* front and back text pens and drawmode
1,1,
1* XY origin relative to container TopLeft
NULL,
1* font pointer or NULL for defaults
"111",
1* pointer to text
NULL
1* next IntuiText structure
I;
328
*1
*1
*1
*1
*1
3.8
ABACUS
MENUS
};
*1
*/
*1
*/
};
};
&MenuItem1
/* Menu flags */
};
*/
*/
*/
*/
*/
};
};
329
3. INTUITION AND C
AMIGA
3, 1, COMPLEMENT,
20,1,
NULL,
"SO Character-,
NULL
/;
struct MenuItem MenuItem7 &MenuItemS,
0,44,
140,10,
CHECKIT+ITEMTEXT+COMMSEQ+ITEMENABLED+HIGHCOMP+CHECKED,
1* Item flags *1
0,
1* each bit mutually-excludes a same-level Item *1
(APTR)&IText14, 1* Item render (IntuiText or Image or NULL) *1
NULL,
1* Select render *1
"8",
1* alternate command-key *1
NULL,
1* SubItem list *1
OxFFFF
1* filled in by Intuition for drag selections *1
);
*1
*1
*1
*1
*1
);
140,10,
CHECKIT+ITEMTEXT+COMMSEQ+MENUTOGGLE+ITEMENABLED+HIGHCOMP,
1* Item flags
0,
1* each bit mutually-excludes a same-level Item
(APTR)&IText15, 1* Item render (IntuiText or Image or NULL)
NULL,
1* Select render
1* alternate command-key
"V",
NULL,
1* SubItern list
OxFFFF
1* filled in by Intuition for drag selections
};
*1
*1
*1
*1
*1
*1
*1
*1
*1
*/
*1
*1
);
140,10,
CHECKIT+ITEMTEXT+COMMSEQ+MENUTOGGLE+ITEMENABLED+HIGHCOMP,
/* Item flags
0,
1* each bit mutually-excludes a same-level Item
(APTR)&ITextI6, 1* Item render (IntuiText or Image or NULL)
NULL,
/* Select render
"Btt,
1* alternate command-key
NULL,
/* SubItern list
OxFFFF
1* filled in by Intuition for drag selections
*1
*1
*1
*1
*1
*1
*1
);
330
3.8
ABACUS
20,1,
NULL,
"Italic",
NULL
MENUS
*1
*1
*1
*1
140,10,
CHECKIT+ITEMTEXT+COMMSEQ+MENUTOGGLE+ITEMENABLED+HIGHCOMP,
1* Item flags
0,
1* each bit mutually-excludes a same-level Item
(APTR)&IText17, 1* Item render (IntuiText or Image or NULL)
NULL,
1* Select render
"I",
1* alternate command-key
NULL,
1* Subltem list
OxFFFF
1* filled in by Intuition for drag selections
*1
*1
*1
*1
*1
*1
*1
};
*1
*1
*1
*1
*1
140,10,
CHECKIT+ITEMTEXT+COMMSEQ+MENUTOGGLE+ITEMENABLED+HIGHCOMP+CHECKED,
1* Item flags *1
0,
1* each bit mutually-excludes a same-level Item *1
(APTR)&IText18, 1* Item render (IntuiText or Image or NULL) *1
NULL,
1* Select render *1
"N",
1* alternate command-key *1
NULL,
1* Subltem list *1
OxFFFF
1* filled in by Intuition for drag selections *1
MENU ENABLED,
"Style",
&MenuItern3
J;
struct Menultem Menultem12 - I
NULL,
0,33,
331
3.
INTUITION AND
AMIGA
NULL,
liE",
NULL,
OxFFFF
1* Select render *1
1* alternate command-key *1
1* SubItem list *1
1* filled in by Intuition for drag selections *1
);
);
*1
*1
*1
*1
*1
);
);
*1
*1
*1
*1
*1
);
332
3.8
ABACUS
MENUS
};
struct IntuiText IText23 = (
3,1,COMPLEMENT,
1* front and back text pens and drawmode
1,1,
/* XY origin relative to container TopLeft
NULL,
/* font pointer or NULL for defaults
"Text",
1* pointer to text
NULL
1* next IntuiText structure
*1
*/
*1
*1
*/
I;
struct Menultem SubItem11 = (
&Subltem12,
122,-8,
81,10,
*1
*1
*/
*1
*1
*1
*1
*1
*/
I;
struct IntuiText IText24 .= (
3, 1, COMPLEMENT,
/* front and back text pens and drawmode
1,1,
/* XY origin relative to container TopLeft
NULL,
1* font pointer or NULL for defaults
"Save",
1* pointer to text *1
NULL
/* next IntuiText structure
Ii
struct Menultem MenuItem10 = f
&Menultemll,
0,11,
*1
*1
*/
*1
I;
struct IntuiText IText25 - (
3,1, COMPLEMENT,
1* front and back text pens and drawmode
1,1,
1* XY origin relative to container TopLeft
NULL,
1* font pointer or NULL for defaults
"IFF",
/* pointer to text
NULL
1* next IntuiText structure
*1
*1
*/
*/
*/
);
,
;
NULL,
"I",
NULL,
OxFFFF
1* Select render */
/* alternate command-key */
/* no Subltem list for Subltems */
333
3.
INTUITION AND
1,1,
NULL,
"ASCII",
NULL
HAH,
NULL,
OxFFFF
/* alternate command-key */
/* no Subltem list for Subltems
/* filled in by Intuition for drag selections
*1
*1
J;
struct IntuiText IText27 - f
3, 1, COMPLEMENT,
/* front and back text pens and drawmode
1,1,
/* XY origin relative to container TopLeft
NULL,
/* font pointer or NULL for defaults
"Text",
/* pointer to text
NULL
1* next IntuiText structure
I;
struct Menultem Sublteml4 - (
&Subltem15,
122,-8,
*/
*/
*/
*1
*1
'*
'*
I;
'*
'*
*/
*/
*/
*/
*/
I:
struct Menultem Menultem9 &MenultemlO,
0,0,
'*
'*
I;
'*
'*
*/
*/
*/
*'
*/
*1
*/
*/
*1
57,0,
MENUENABLED,
334
ABACUS
3.8 MENUS
"Datei",
&MenuItem9
I;
3.8.4.4
Note:
3.
INTUITION AND
AMIGA
336
ABACUS
3.9
3.9.1
ConsoleWriteMsg = CreateStdIO(ConsoleWritePort);
if (!ConsoleWriteMsg)
(
337
3.
INTUITION AND
AMIGA
Once these two data directions are enabled, the device can be opened.
Two fields must be initialized in the IOStdRequest structure. This
is unusual when opening a device, but the console device needs this
initialization:
ConsoleWriteMsg->io_Data
ConsoleWriteMsg->io_Length
conDevErr
(APTR) FirstWindow;
sizeof(*FirstWindow);
if (conDevErr)
(
printf("OpenDevice error!\n");
Close_All();
exit (FALSE) ;
I;
ConsoleReadMsg->io_Device
ConsoleReadMsg->io_Unit
= ConsoleWriteMsg->io_Device;
= ConsoleWriteMsg->io_Unit;
338
ABACUS
3.9.2
* Function:
* =================================== *
comments:
* Author: Date:
*
*
*
---------24.10.1987 improved L&O
* Wgb
*
pointer to IOStdReq
* StdReq
*
Address of Text buffer
*
* buffer
Length of Text buffer
* length
*
***************************************/
----------
339
3.
INTUITION AND
3.9.3
AMIGA
*
*
*
*
*
====:=~===~~~s====================
Author:
Wgb
* StdReq
* Zchar
Date:
23.10.1987
comments:
Basic L&D
*
*
'"
'"
'*"
'"
***************************************1
ConWrite(StdReq, Text, Length)
struct IOStdReq *StdReq:
char
Text[):
int
Length;
StdReq->io_Command
StdReq->io_Data
StdReq->io_Length
0010 (StdReq) ;
= CMD_WRITE;
= (APTR)Text;
= Length;
This function transfers the characters at the start of the buffer when
called. We can also display a string terminated by a null byte.
Here you use the length descriptor -IL. To test this, have the
intuition .library open a window and create a loop which
checks for a clicked close gadget. A console.device window opens
featuring complete input and output, resulting in a full window editor.
Check it out!
340
ABACUS
3.9.4
OxOF
ESC
OxlB
CSI
Ox9B
The last two characters mentioned above are very important. CSI
introduces a control sequence; <Esc> performs a similar task:
Label
Character Sequence Comments
Returns to original status
RESET
Esc Ox63
INSERT [N] CHARACTERS
Inserts [N] characters. If no [N]
CSI [N] Ox40
is given, N = 1
CURSOR UP [N] LINES
Moves cursor [N] lines up. If no
CSI [N] Ox41
[N] is given, N =1
CURSOR OOWN (N] LINES
Moves cursor [N] lines down. If
CSI [N] Ox42
no rNJ is ~iven, N = 1
CURSOR FORWARD
Moves cursor [N] characters to
CSI [N] Ox44
[N] CHARACfERS to the right.
If no [N] is given, N = 1
341
3. INTUITION AND C
AMIGA
Character Sequence
Label
CURSOR BACKWARD
CSI [N] Ox44
Comments
Moves cursor [N] characters to
[N] CHARACTERS to the right.
If no [N] is given, N = 1
Moves the cursor to line [N]. If
no fNl is given N = 1
CURSOR PRECEDING
CSI [N] Ox46
342
ABACUS
We suggest that you create an array from which you can recall a
sequence when you need it. This is because all of the command
sequences in the console.device consist of multiple characters. The
routine for reading the characters is sensitive to differences between
letters, numbers and control characters. Control characters are divided
into other groups. Here you need additional routines to read cursor keys,
function keys and the control sequences (which return to the user)
directed by the CSI. Here is an example of important control characters
and a command table:
fdefine
fdefine
fdefine
fdefine
#define
fdefine
#define
SPACE
BACKSPACE
LINEFEED
RETURN
CSI
DEVICE_STATUS_REPORT
WINDOW_STATUS_REQUEST
Ox20
Ox08
OxOA
OxOD
Ox9B
Ox36, Ox6E
Ox30, Ox20, Ox?1
fdefine
#define
#define
#define
CURSOR_UP
CURSOR DOWN
CURSOR_Shift_UP
CURSOR_Shift_DOWN
Ox41
Ox42
Ox54
Ox53
UBYTE Specialcharacter[]
{
SPACE,
BACKSPACE,
LINEFEED,
RETURN,
CSI, DEVICE_STATUS_REPORT,
CSI, WINDOW_STATUS_REQUEST
};
3.9.5
343
3.
INTUITION AND
AMIGA
handle the entire window. We need a reading loop that takes data from
our data buffer:
if (GetMsg(consoleReadPort
{
switch (Buffer[O])
{
1* Selection *1
QueueRead(consoleReadMsg, &Buffer[mode], 1L);
}
present, select the first character, select a new character, etc. The routine
treats the character as a letter or normal character that doesn't need
special handling. Then the following lines come into play:
default
ConWriteChr(consoleWriteMsg, &Buffer[O]);
Coords = FALSE;
WindowBuffer[y] [x-1] = Buffer[O];
if (WindowBuffer[y] [79] < x)
WindowBuffer[y] [79] = Xl
break;
The small processing routine above gets the first character. Then the
coordinate flag changes to FALSE, so the program knows that the
actual coordinates are no longer known. The characters are then
transferred to the window buffer. This stores all of the characters
presently in the window. Next the new character is tested to see if it
exceeds the current line length. If so, the new line length is transferred.
These methods were chosen for the following purposes: The window
buffer clears at the beginning, so that it contains only spaces (0x20).
When you enter <Return> in a line, this should be sent to DOS so the
eLI command can be executed. If all the spaces are transferred to DOS,
then the eLI cannot recognize where the command sequence ends. You
can't send a command because the eLI command word and arguments
cannot be transmitted with the added characters.
Imagine a line of command data. The line begins with the frrst character
that must be examined. Then the line in which the cursor is found
should be transmitted:
case RETURN
: ConWriteChr(consoleWriteMsg, &Buffer[O])
ConWriteChr(consoleWriteMsg, &Specialcharacter[2]);
Coords = FALSE;
if (WindowBuffer[yj (79) != 0)
{
break;
344
= NULL;
= Ox20;
ABACUS
The second character reading routine processes the graphic first. This
means that the routine requires cursor placement at the beginning of the
line and in the next line. The coordinate flag is set again. A test
examines whether characters were entered in the line. If so, a null ends
the line so the ExecuteO function of DOS can be called. After that a
space ends the line.
The last character needing special attention is the <Backspace> key.
Neither the console.device task nor the buffer correction should execute:
case BACKSPACE: ConWriteChr(consoleWriteMsg,
&Buffer[O]);
ConWrite(consoleWriteMsg, &Specialcharacter[O], 2);
WindowBuffer[y][x-l] = SPACE;
Coords = FALSE;
break;
: mode += 1;
break
This read loop assigns a number that increments by one every time
further readings occur. From the number you can recognize how many
characters must still be processed. Multiple control sequences may be in
the buffer, because the CSI controls the function keys and cursor keys.
We will look at the same reader:
/*********************************
*
*
*
*
*
*
*********************************/
else if (mode)
{
/* Check */
mode = 0;
QueueRead(consoleReadMsg, &Buffer[mode], lL);
}
The ELSE belongs to the IF statement that tests the console buffer for
a character from the device. As long as there is a message there, this
gets the data first from the buffer because the console.device only stores
the last 32 characters received. If more characters exist, the routine
simply ignores them. This is why the variable mode is used; to enter
the new character in the next location of our buffer.
Let's look at the handling of the first control sequence, which reports
the cursor position:
345
3.
INTUITION AND
AMIGA
/*********************************
* Control Sequence :
*
*
*
*
*
*
*
*
*********************************1
if (Buffer [mode-1] == 82)
{
Coords = TRUE;
x = 0; y = 0; i
1;
y *= 10;
y += Buffer[i]
- Ox30;
i ++;
}
++;
while (Buffer[i] < Ox3A & Ox2F < Buffer[i])
i
x *= 10;
x += Buffer[i] - Ox30;
i ++;
}
This program section goes through one character after another, and
calculates the X and Y coordinates of the cursor. The DEVICE STATUS
REPORT has the fonnat (note the ending R):
CSI Row; Line R
Next the routine evaluates the buffer character for character. The
calculation of the first value concludes when the program encounters a
semicolon. Then the X value can be calculated. It is also important that
the coordinate flag be reset. Later a test follows that sends the set flag
to the console as a report, that should include the coordinates in the data
direction. This could provide us with an endless communication system
consisting of the cursor coordinates only.
The next test shows that a control sequence is present (but not the
DEVICE STATUS REPORT). It is flrst checked to see if the cursor
keys were pressed. The console.device allows the use of <Shift> and
cursor keys. A <Shift>ed cursor key scrolls the entire window, instead
of simply changing the cursor position. This scrolling must also occur
in the text buffer.
Look at the following routine:
346
ABACUS
else
switch (Buffer[mode-l])
{
/*********************************
* Control Sequence :
*
* SHIFTED CURSOR MOVE
*
*
*
*
*
*********************************/
case CURSOR Shift UP
Move_Down () ;
break;
Move_Up() ;
break;
/*********************************
*
*
* Control Sequence :
*
*
*********************************/
case CURSOR_DOWN
if (y == 1) Move_Down();
break;
if (y == Ymax) Move_Up();
break;
The last two statements end with normal cursor key movements. If the
cursor has reached the top or bottom border of the screen, the screen
scrolls up or down by one line.
Two functions handle the scrolling of the window buffer. These
functions look like this:
/***************************************
*
* Functions: Text buffer processing
* ===================================
*
comments:
* Author: Date:
*
* Wgb
*
*
*
*
y
x
---------- ---------11/2/1987
*
*
*
*
*
*
*
*
*
*
***************************************1
347
3.
INTUITION AND
AMIGA
int y;
for(y=O; y<80; y ++)
Clear_Line(y);
Clear_Line (y)
int y;
{
int x;
for(x=O; x<79; x ++)
WindowBuffer[yl [xl = Ox20;
WindowBuffer[yl [78] = NULL;
f* Line end mark *1
WindowBuffer[y] [79] = NULL; f* Number of entered
characters *1
}
Move_Up 0
{
int x, y;
for(y=l; y<80; y ++)
for (x=O; x<80; x ++)
WindowBuffer[y-l] [xl
Clear_Line (79) ;
= WindowBuffer[y] [xl;
Move_Down ( )
{
int x, Yi
for(y=79; y>O; y --I
for(x=O; x<80; X ++)
WindowBuffer[y] [xl = WindowBuffer[y-l] [xl;
Clear_Line(O);
}
This function initializes the buffer at the beginning of the code. The
number of characters entered per line is set at zero, just as a zero
indicates the end of a line of text.
Clear_Line()
348
ABACUS
/*********************************
Control Sequence :
*********************************/
ConWrite(consoleWriteMsg, &Buffer[O], mode);
Coords = FALSE;
}
You can enter all of the additional control sequence functions, if you
want to. For example, you won't find a description of simple function
key assignment. It's actually very simple. Assuming that you want
these assignments permanent within the program, then you simply
establish a pointer array and add the desired texts. Here is an example for
our CLI editor:
UBYTE *Functionkeys[]
{
"dir"+RETURN,
"cd df1:"+RETURN,
"cd sys: "+RETURN,
"list"+RETURN,
"makedir ram:c"+RETURN+"copy sys:c ram:c"+RETORN,
"execute make "
};
The test for the function keys must identify these through "_" and the
number calculated. Then the pointer can be assigned to ConWr i teO;:
ConWrite(consoleWriteMasg, Functionkeys[Number], -1L);
3.9.6
349
3.
INTUITION AND
AMIGA
Set all of the reading elements, all of the routines to open and close,
and the text buffer manipulator (remember to define the important
variables and arrays) Now we have a neweLl editor!
Note:
The window sizing gadgets described earlier in this book can be added to
this editor. Here is a complete listing:
I*******~********************************
*
*
~-============
* Author:
* -----* Wgb
Date:
Comments:
10/20/1987
*
*
*
*
*
*
*
*
****************************************1
#include <exec/types.h>
#include <intuition/intuition.h>
struct IntuitionBase *IntuitionBase;
struct Window
*FirstWindow;
struct IntuiMessage *message;
struct IOStdReq *consoleWriteMsg, *conso1eReadMsg,
*CreateStdIO () ;
struct MsgPort *consoleWritePort. *conso1eReadPort.
*CreatePort () ;
struct NewWindow ConsoleWindow =
{
0, 0,
640, 101,
2, 3,
1*
1*
1*
1*
LeftEdge, TopEdge
Width, Height
DetailPen, BlockPen
IDCMP Flags
CLOSEWINDOW
GADGETUP,
WINDOWDEPTH
1* Flags
WINDOWSIZING I
WINDOWDRAG I
WINDOWCLOSE I
ACTIVATE I
SMART_REFRESH,
NULL,
1* First Gadget
NULL,
1* CheckMark
(UBYTE *) "Wgb Prod. presents BECKERshell",
NULL,
1* Screen
NULL,
1* BitMap
640, 50,
1* Min Width, Height
640, 200,
1* Max Width, Height
WBENCHSCREEN,
1* Type
};
350
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
*1
ABACUS
fldefine
#define
#define
#define
#define
#define
#define
SPACE
BACKSPACE
LINEFEED
RETURN
CSI
DEVICE_STATUS_REPORT
WINDOW_STATUS_REQUEST
Ox20
Ox08
OxOa
OxOd
Ox9b
Ox36, Ox6e
Ox30, Ox20, Ox71
#define
#define
#define
#define
CURSOR_UP
CURSOR_DOWN
CURSOR_Shift_UP
CURSOR_Shift_DOWN
Ox41
Ox42
Ox54
Ox53
UBYTE Specialcharacter[]
{
SPACE,
BACKSPACE,
LINEFEED,
RETURN,
CSI, DEVICE_STATUS~REPORT,
CSI, WINDOW_STATUS_REQUEST
};
SHORT conDevErr = FALSE;
UBYTE WindowBuffer[80] [80];
UBYTE Buffer[80];
VOID mainO
{
ULONG MessageClass;
USHORT code;
int i;
int x, y;
int Xmax = 77, Ymax
int mode = 0;
BOOL Coords;
= 11;
0;
Clear_Buffer 0 ;
ConWrite(consoleWriteMsg, &Specialcharacter[4], 3);
QueueRead(consoleReadMsg, &Buffer[Oj, 1L);
FOREVER
{
MessageClass
= message->C1ass;
351
3. INTUITION AND C
AMIGA
code = message->Code:
ReplyMsg(message):
switch (MessageClass)
{
if (GetMsg(consoleReadPort
{
switch (Buffer[D])
(
case BACKSPACE
ConWriteChr(consoleWriteMsg,
&Buffer[D]) :
ConWrite(consoleWriteMsg,
&Specialcharacter[D], 2):
Coords
break:
case RETURN
FALSE:
ConWriteChr(consoleWriteMsg,
&Buffer[D]) :
ConWriteChr(consoleWriteMsg,
&Specialcharacter[2]):
Coords = FALSE:
if (WindowBuffer[y] [79]
!=
0)
Dx2D:
break:
case CST
mode += I:
break;
default
ConWriteChr(consoleWriteMsg,
&Buffer[D]) ;
Coords = FALSE:
WindowBuffer[y] [x-I]
= Buffer[D);
1*********************************
*
*
352
*
*
ABACUS
sequence
*
*
*
*
*********************************1
else i f (mode)
{
1*********************************
*
*
*
*
*
Control Sequence :
*
*
*
*
*********************************1
if (Buffer [mode-1]
==
82)
Coords = TRUE;
x = 0; y = 0; i
1;
*= 10;
Y += Buffer[i] - Ox30;
i ++;
}
i ++;
else
switch (Buffer[mode-1])
1*********************************
*
*
*
*
*
Control Sequence :
SHIFTED CURSOR MOVE
*
*
*
*
*********************************1
case CURSOR- Shift- UP
case CURSOR- Shift- DOWN
Move_Down () ;
break;
Move_Up() ;
break;
1*********************************
**
*
Control Sequence :
*
*
*
353
3.
INTUITION AND
AMIGA
*
*
*********************************/
case CURSOR UP
if (y == 1) Move_Down!);
case CURSOR_DOWN
i f (y == Ymax)
break;
break;
/*********************************
*
*
*
*
*
Control Sequence :
*
*
*********************************/
ConWrite(consoleWriteMsg, &Buffer[O], mode);
Coords = FALSE;
/*********************************
*
*
*
*
* Console Device :
*********************************/
mode = 0;
QueueRead(consoleReadMsg, &Buffer[mode], 1L);
}
/*********************************
* Console Device
*
*
*
* New cursor*
* position
*
*
*
*********************************/
if (Coords
==
FALSE)
354
= TRUE;
ABACUS
/***************************************
*-
** Author:
Date:
comments:
* ----* Wgb
10/20/1987
* no parameter
*
*
*
*
*
*
*
*
*
*
***************************************/
consoleWriteMsg = CreateStdIO(consoleWritePort);
if (!consoleWriteMsg)
(
355
3.
INTUITION AND
AMIGA
consoleReadMsg = CreateStdIO(consoleReadPort);
if (!consoleReadMsg)
{
consoleWriteMsg->io_Data
(APTR) FirstWindow:
consoleWriteMsg->io_Length = sizeof(*FirstWindow):
conDevErr = OpenDevice ("console. device", OL, consoleWriteMsg,
OL);
if (conDevErr)
{
= consoleWriteMsg->io_Device;
consoleReadMsg->io_Unit
consoleWriteMsg->io_Unit;
1***************************************
*
*
*
*
Comments:
* Author: Date:
---------- ---------*
*
Wgb
*
*
*
No Parameter
20.10.1987
**
*
*
*
*
*
*
***************************************1
i f (!conDevErr)
CloseDevice(consoleWriteMsg):
356
ABACUS
if (consoleWriteMsg)
DeleteStdIO(consoleWriteMsg);
if (consoleReadMsg)
DeleteStdIO(consoleReadMsg);
if (consoleWritePort)
DeletePort(consoleWritePort);
if (consoleReadPort)
DeletePort(consoleReadPort);
if (FirstWindow)
CloseWindow(FirstWindow);
if (IntuitionBase)
CloseLibrary(IntuitionBase);
1***************************************
*
*
* Function: Output with the ConsoleDev*
*======
** Author: Date:
Comments:
* ------ ---------- ---------23.10.1987
* Wgb
* request Message Port
*
Number of the character
* Zchar
*
*
*
*
*
***************************************1
ConWriteChr(request, Zchar)
struct IOStdReq *request;
char
Zchar[];
ConWrite(request, Zchar, 1);
}
1*******************************
ConWriteString(request, Text)
struct IOStdReq *request;
char
Text[];
ConWrite(request, Text, -1);
}
********************************1
357
3.
INTUITION AND
AMIGA
request->io_Command = CMD_WRlTE;
request->io_Data
= (APTR)Text;
request->io_Length = Length;
DoIO(request):
}
1***************************************
*
*
*
*
*
Comments:
* Author: Date:
*
---------- -----* Wgb
24.10.1987
*
*
*
*
request
whereto
Length
*
*
*
*
*
*
*
***************************************1
QueueRead(request, whereto, Length)
struct IOStdReq *request;
char
*whereto;
ULONG
Length;
request->io_Command = CMD_READ;
request->io_Data
= (APTR)whereto;
request->io_Length = Length;
SendIO(request);
}
1************************************************
int ConMayGetChr(conso1ePort, request, whereto)
struct Port
*conso1ePort:
struct IOStdReq *request:
char
*whereto;
register temp:
358
ABACUS
if (GetMsg(consolePort) == NULL)
return(FALSE);
temp = *whereto;
QueueRead(request, whereto, lL);
return (temp) ;
}
register temp;
while (GetMsg(consolePort) == NULL)
WaitPort(consolePort);
temp = *whereto;
QueueRead(request, whereto, lL);
return (temp);
}
*************************************************1
1***************************************
*
*
*
*
*
*
* Author: Date:
Comments:
*
---------- ---------11/2/1987
* Wgb
*
* y Number of the line
* x Number of the column
*
*
*
*
*
*
***************************************1
Clear_Buffer 0
{
int y;
for(y=O; y<80; y ++)
Clear_Line(y);
Clear_Line(y)
int y;
{
int x;
for(x=O; x<79; x ++)
359
3.
INTUITION AND
AMIGA
Move_Up()
{
int x, y;
for(y=l; y<80; y ++)
for(x=O; x<80; x ++)
WindowBuffer[y-l] [x] = WindowBuffer[y] [x];
Clear_Line (79) ;
}
Move_Down ()
{
int x, y;
for(y=79; y>O; y --)
for(x=O; x<80; x ++)
WindowBuffer[y] [x) = WindowBuffer[y-l] [x];
Clear_Line (0);
}
360
3.10
ABACUS
3.10
3.10.1
OxOO
Ox04
Ox08
OxOC
OxlO
Ox14
Ox18
OxIC
Ox20
};
00
04
08
12
16
20
24
28
32
UBYTE
ULONG
UBYTE
UBYTE
UBYTE
ULONG
UBYTE
UBYTE
*km_LoKeyMapTypes;
*km_LoKeyMap;
*km_LoCapsable;
*km_LoRepeatable;
*km_HiKeyMapTypes;
*km_HiKeyMap;
*km_HiCapsable;
*km_HiRepeatable;
361
3.
INTUITION AND
AMIGA
You can see that the KeyMap structure actually has two separate
sections-a Lo section and a Hi section.
The Lo section contains infonnation about characters with RAWKEY
codes from OxOO to Ox3F. The Hi section contains infonnation about
characters with RAWKEY codes from Ox40 to Ox6. This division exists
to keep the keyboard open for more keys which can be entered into the
Hi section. This makes enhanced keyboards possible.
Examine the RAWKEY diagram below, with which you can get the code
for any key:
. . .
, .
. ...
3D
lE
IF
2D
2E
2F
lD
lE
lF
4A
- . '"
1'1
J~
18
, Ho.e
lE
20
2E
....
3A
01
I'qUp -
3D
. ....
JF
4A
2F
OC
lEnd'IPqOn
10
IE,
IF
"
1-:----'--=+-"::'001'" ;
OF
lC
r 43
Figure 3.13
Let's look at each table:
km_LoKeyMapTypes
km_HiKeyMapTypes
These two tables contain one byte for every key. This byte lists the flag
infonnation described below. Unfortunately it's almost impossible to
362
ABACUS
list the combinations for every key. These combinations include nonnal
keys, <Shift>ed keys, <Ctrl> combinations and <Alt> combinations.
Because the key combinations from two qualifiers, the <Shift>, <Alt>
and the <Ctrl> key, are considered, only four characters can be used per
key. You can test to see which qualifier releases which characters. Or
you could decide upon an entire string for the key, but then you should
take into consideration that only two arrangements are allowable for the
key.
km_LoKeyMap
km_HiKeyMap
The ULONG values of these tables contain the characters that should
appear when a key is pressed. Since the ULONG value consists of four
bytes, each byte has the character code belonging to one of the four
combinations. This is because the tables handle the character as a
string. Then the ULONG value represents a pointer to the string.
km_LoCapsable
km_HiCapsable
These two tables work much like the Capsable tables. Here again we
have a bit for each RAWKEY code. A set bit indicates key repetition
according to values set by Preferences. An unset bit disables key repeat
3.10.2
363
3.
INTUITION AND
= ConWriteRequest->io_Error;
i f (Error)
return(FALSE);
else
re.turn (TRUE);
364
ABACUS
if (Error)
return(FALSE);
else
return(TRUE);
= ConWriteRequest->io_Error;
if (Error)
return(FALSE);
else
return(TRUE);
This last function lets you address two more functions named
CD ASKDEFAULTKEYMAPandCD SETDEFAULTKEYMAP.Both
work exactly as the names suggest. These functions let us control the
default KeyMap (the KeyMap made available when the console.device
opens).
This KeyMap contains all of the characters divided into the groups Lo
and Hi. The Lo group is handled as follows:
Any keys pressed with the <Shift> key create their shifted ASCII
codes.
Any keys pressed with the <Alt> key create their ASCII codes
with the highest bit set (Ox80).
365
3.
INTUITION AND
AMIGA
Any keys pressed with the <Alt> and <Shift> keys create their
shifted ASCII codes with the highest bit set (Ox80).
Any keys pressed with the <Ctrl> key create their ASCII codes
with bits 5 and 6 deleted.
Tran::!Iat~ val!J~
<Backspace> OxOS
<Enter>
OxOD
Ox7F
<Del>
Keys with one qualifier
withQ!Jt with
Ox20
OxAO
OxOD
OxOA
OxlB Ox9B
Ox2D
OxFF
K~y
<Space>
<Return>
<Esc>
Qualifier
Alt
Ctrl
Alt
Alt
Keys of High-KeyMap
366
K~y
Vi.lI!J~
<Tab>
Ox09
Up
Down
Forward
Backward
Ox9B "A"
Ox9B "B"
Ox9B "C"
Ox9B "D"
Ox9B "T"
Ox9B "S"
Ox9B "@"
Ox9B" A"
Fl
F2
F3
F4
F5
F6
F7
FS
F9
FlO
Ox9B "0-"
Ox9B "1-"
Ox9B "2-"
Ox9B "3-"
Ox9B "4-"
Ox9B "5-"
Ox9B "6_"
Ox9B "7_"
Ox9B "S-"
Ox9B "9_"
Ox9B "10-"
Ox9B "11-"
Ox9B "12-"
Ox9B "13-"
Ox9B "14-"
Ox9B "15-"
Ox9B "16-"
Ox9B "17-"
Ox9B II IS- "
Ox9B "19- II
HELP
Ox9B "?_"
ABACUS
3.10.3
lCeyMap
Types
We first need the two table types. These contain, as explained above, a
check to see if it is a string or four single characters, and the qualifiers
which access every key. The following flags are possible:
KeyMap Types:
Nam~
:YaIJJ~
:D~S~[h21jQn
KC_NOQUAL
KC VANILLA
OL
7L
No qualifier
Standard value with <Ctrl>
KCF SHIFT
KCF ALT
KCF CONTROL
OxOlL
Ox02L
Ox04L
KCF DOWNUP
KCF DEAD
Ox08L
Ox20L
KCF_STRING
Ox40L
Byte 1
Byte 2 Byte 3
Shift
Alt
Ctrl
Shift+Alt Alt Shift
Ctrl+Alt Ctrl Alt
Ctrl+Shift Ctrl Shift
Shift+Alt Alt Shift
(etrl also accessible)
Poiater to string
Byte 4
without
without
without
without
without
without
without
without
367
3.
INTUITION AND
KeyMap
AMIGA
Caps able
Like a typewriter, the Amiga keyboard also has a <CapsLock> key that
acts as a continuous <Shift>. Because the keyboard has a keyboard
processor, it isn't just a matter of simulating the continuous <Shift>.
The processor executes its own form of <Shift>. The <CapsLock> key
tests to see which keys are usable and which are nOL This method has
the advantage that punctuation remains unshifted, and can be entered as
normal without having to release the <CapsLock> key.
We have 8 bytes with 64 bits for all of the keys. Each of these bits
represents a key:
Bit 0 Byte 0 RAWCODE OxOO, Bit 1 Byte 0 RAWCODE OxOl
Bit 0 Byte 0 RAWCODE Ox08 Bit 7 Byte 7 RAWCODE Ox4f.
Set all of the bits for those keys which you want <Shift>ed.
Repeatabl.e
The same method available for the Capsable table is used for the
Repeatable table. Each set bit indicates that the key should be
repeated according to the repetition time and frequency set in
Preferences.
Many keys can be excluded from the repeat function. That's important if
a key doesn't have to repeat, or if repeating could cause trouble. Usually
the <Return> key does not repeaL
368
3.11
ABACUS
3.11
MEMORY MANAGEMENT
Memory Management
Some programs may require access to a certain area of memory. The
simplest memory forms are the variables. Here you control what is
needed for program formation. Arrays are based upon these variable
forms. But you may have heard quite a bit about how limiting these can
be.
Here's where the problem arises. The program could need a variable
array double the size of the number expected for program execution.
This could have a dimension of 1000*1000 for the color values of a
high resolution graphic. Our compiler reserves a doubled variable in the
finished program 100000 times. You can imagine how much memory
that requires.
We would like to show you a short example which accesses memory
areas from the system and frees them after use. Three methods of
memory organization are demonstrated. The example itself is chosen
from Intuition. We will try to assign four text buffers for the
corresponding string gadgets.
3.11.1
Arrangement
Comments
Copy of the Kickstart ROM
Third copy of Chip RAM
Fast RAM
Ox08000000
Ox20000000 8 MByte
OxAOOOOOOO CIAs
OXCOOOOOOO 512K expansion (Amiga 500 & 2000 only)
OxC8000000 Unused
(Amiga 500 & 2000 only)
OxDCOOOOOO Real time clock
OxDFOOOOOO Custom chips
OxEOOOOOOO Unused
OxE8000000 Expansion slots
OxFOOOOOOO ROM module
0xF8000000 Kickstart ROM copy
OxFCOOOOOO Kickstart ROM
369
3.
INTUITION AND
AMIGA
The entire memory is divided into two regions. The first 512K is chip
RAM, which means it can be addressed from the custom chips. The
second region consists of fast RAM or ROM, and can be addressed from
the 68000.
The word "dynamic" comes from the fact that the 8MB fast RAM
doesn't exists in the basic Amiga: Memory can be expanded
dynamically.
When we want our program to request an area of memory from the
system, then we must specify whether it should be chip RAM or fast
RAM. If we give no specification, fast RAM is used first. If this
doesn't succeed, the memory is is taken from chip RAM.
Note:
3.11.2
The flags MEMF FAST and MEMF CHIP describe the attributes of the
memory area. MEMF_PUBLIC indicates memory usable for different
tasks. The MEMF_PUBLIC flag is currently unimplemented. However~
you should use it anyway to maintain compatibility with later Amiga
systems. MEMF_CLEAR clears the area of memory (Le., fills the area
with null bytes)., which means it should be filled with zeros. The
memory area must be a minimum of eight bytes long.
The following program reserves memory for four text buffers:
/***************************************
*
* Memory Management : AllocateMemory
* ===================================
*
370
*
*
*
*
ABACUS
* Author: Date:
*
---------10/16/1987
* Wgb
*
comments:
----------
*
*
*
*
***************************************/
iinclude <exec/types.h>
iinclude <exec/memory.h>
tdefine MemoryType MEMF CHIP
MEMF CLEAR
FileBuffer
AllocMem(30L, MemoryType);
i f (!FileBuffer)
{
DiskBuffer
= AllocMem(512L,
MemoryType);
i f (!DiskBuffer)
{
printf("Memory reserved!\n");
FreeMemory();
371
3.
INTUITION AND
exit(TRUE);
}
FreeMemory ()
{
if
if
if
if
(UndoBuffer)
(FileBuffer)
(DiskBuffer)
(SuffBuffer)
FreeMem(UndoBuffer,
FreeMem(FileBuffer,
FreeMem(DiskBuffer,
FreeMem(SuffBuffer,
512L);
30L);
512L);
10L);
Program
Description
3.11.3
Oxoo
OxOE
372
00
14
ABACUS
OxlO
Ox18
};
16
24
union
ULONG meu_Reqs;
APTR meu_Addr;
}
Oxoo
Ox04
me_Un;
04 ULONG me_Length;
Ox08
};
08
DefInition:
me_un me_Un
me_Reqs me_Un.meu_Reqs
me_Addr me_Un.meu_Addr
MemList
DO
AllocEntry(Entry);
-222
AO
FreeEntry(Entry);
-228
AO
373
3.
INTUITION AND
AMIGA
*
*
: MemoryEntries *
* ================================== *
* Memory management
*
*
Comments:
*
* Author: Date:
---------- ---------*
*
10/16/1987 only for test *
* Wgb
purposes
*
*
*
*
**************************************/
#include <exec/types.h>
#include <exec/memory.h>
#define MemoryType MEMF_FAST
struct MemList *MemoryPtr, *AllocEntry();
APTR UndoData, FileData, DiskData, SuffData;
struct Memoryneeds
{
Memory.Head.ml_NumEntries
= 5:
Memory.Head.ml_me[O].me_Length = 0;
Memory.UndoBuffer.me_Reqs
= MemoryType;
Memory.UndoBuffer.me_Length
512L;
Memory.FileBuffer.me_Reqs
= MemoryType;
Memory.FileBuffer.me_Length = 30L;
Memory.DiskBuffer.me_Reqs
= MemoryType;
Memory.DiskBuffer.me_Length
512L;
Memory.SuffBuffer.me_Reqs
MemoryType;
Memory.SuffBuffer.me_Length
10L:
MemoryPtr
else
374
ABACUS
UndoData = MemoryPtr->ml_me[l).me_Addr;
FileData = MemoryPtr->ml me[2) .me Addr;
DiskData = Memoryptr->ml=me[31.me=Addr;
SuffData = MemoryPtr->ml_me[4].me_Addr;
}
3.11.4
= AllocRemember(RememberKey,
-396
AO
Size, Flags);
DO
Dl
FreeRemember(RememberKey, ReallyForget);
-408
AO
DO
OxOO
Ox04
Ox08
OxOC
};
00
04
08
12
375
3. INTUITION AND C
Structure
description
* Memory Management
: Remember
==========================~=======
* Author:
*
* Wgb
Date:
16.10.1987
Comments:
only for test
purposes
*
*
*
*
*
*
**************************************1
#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#define MemoryType MEMF_CHIP
MEMF_CLEAR
376
3.11
ABACUS
MEMORY MANAGEMENT
exit(FALSE);
}
UndoBuffer
MemoryType) ;
FileBuffer
MemoryType);
DiskBuffer
MemoryType);
SuffBuffer
MemoryType);
AllocRemember(&RememberPtr, 512L,
AllocRemember(&RememberPtr,
30L,
AllocRemember(&RememberPtr, 512L,
AllocRemember(&RememberPtr,
10L,
printf("Memory reserved!\n"};
FreeRemember(RememberPtr, TRUE);
printf ("Memory free again! \nn) ;
CloseLibrary(IntuitionBase);
}
377
4.
Operating
System.
Programming
ABACUS
4.
Operating System
Programming
This chapter contains a large, real-world application written in C. We've
included this listing for two reasons:
I.}
2.}
In addition to the normal capabilities that any editor has, our editor
includes a few extra features for creating program source code in general.
This means that you can use the editor for writing source code in most
programming languages, not just in C.
The editor was compiled with the Aztec C compiler. However, the
source code is flexible enough that it should compile with any
compiler. When you find special compiler functions or utility program
calls, the editor code is written so that other compilers can use this.
4.1
381
AMIGA
fast;
2.)
flexible;
3.) programmable;
4.)
user-friendly.
Fast
Let's begin with the first requirement The editor should be fast. Placing
the source text in memory plays a major role in speed. The more
organized the memory, the faster text can be manipulated. You can
allocate editing memory with a given size, and load the text into that
memory range as a series of blocks. The advantage to this is a
minimum of memory management. In addition, source files load and
save quickly.
Buffers
Problems arise when you try inserting characters in the text. If you
insert a character in a large text, the editor becomes unbearably slow
because a relatively large memory area must be moved. It would be
better if you could copy the line containing the entry into a buffer and
insert the character there. Keeping this buffer small speeds up the
memory movement. The buffer is then copied into the text if the user
moves the cursor out of the line. This method works well enough if
you have spare memory available and want to edit a short text.
However, it can take a long time to shift the edited text, especially for
files longer than 30K.
There is also a disadvantage during scrolling. To move from one line to
the next you must search for the end of the line. This takes somewhat
longer than computing the beginning of the next line.
Arrays offer another option for placing text in memory. You can
determine the maximum number of characters per line and the
maximum number of lines, then define an array with the corresponding
dimensions: line field [maxline] [maxcolumn]. That way you
have fast access to individual lines and characters within a line.
Characters can be inserted directly in the text. This method of insertion
is fast because you only move the characters of the current line. This
method also has disadvantages:
382
If you want to insert new lines, you must move the entire text.
The array size is preset. If you want a larger array, you must first
change the size of the array, thereby deleting its contents.
Not all of the lines use the maximum line length. Trial and error
has shown that only a few lines reach maximum length, while
most lines only use a third to one half of the allowable line
width.
ABACUS
Linked lists
Because of these disadvantages you can choose another method and store
the lines as linked lists. Then available memory limits the maximum
number of lines instead of an array, and changing the pointer moves
you to the next line. The lines have no set length so short lines require
little memory. The resulting memory management is complicated as
you'll see in the development stage, but the bottom line is a good
compromise between speed and memory.
After you know how your lines are constructed, you must consider from
where you'll get the memory for each line. The operating system can
supply the memory at any time. You can utilize this memory by
dividing blocks of memory into smaller units (the operating system
cannot produce these smaller units). It is better if you allocate a block
of a predetermined size (e.g., 5K) from the operating system and divide
this up yourself, according to the present need. Write the functions so
you don't need to think about the "why" later when you want to add a
new line to the program. We'll go into more detail when. we discuss
memory management.
User-friendly
Now for a few ideas to make the program more user-friendly. An Undo
function will remove the changes just made to a line. To create the
Undo function you must store the original text in a buffer, delete the
changed line and restore the line's original contents from the buffer.
A feature that has always bothered us when working with editors and
word processors is when the program cannot sense text changes. When
leaving an editor it usually asks, "Are you sure that you have saved the
text?" whether or not changes have been made since the last save. The
editor would be a friendlier program if it only asked this question after
the text has been changed since the last save. To determine whether the
text was changed, a flag must be set whenever a change is made. We
mention this now because if you forget to set the flag in a few places in
the code, the program won't work. The error will be hard to find if you
don't know about this flag.
The editor should support multiple windows. This is important when
doing a lot of work if you want to move blocks of text between
different source codes. When you only have one window, this results in
a lengthy load and save. But you can't just open more windows. Each
window requires its own editing memory, its own cursor and other
things. This means that you cannot globally save all of the important
information about the text, like number of lines, cursor position, etc.
Instead it must be saved to a structure for which exactly one structure
exists for each editor window. There is still a pointer to the actual
structure, whose window is still active. This means more work for you
at the beginning of the development stage, but once you've
implemented a second window, providing for the others becomes easier.
An interesting capability of the Amiga is the option of assigning a
character string to any key. You can, for example, specify that the
Amiga sends a "ae" or ''\344'' (octal code for a) every time you press
383
AMIGA
Tabs
Your editor should be able to insert true tabs instead of just blank
spaces. Structured programming involves inserting spaces to indent
lines, which wastes memory. If you own an Aztec C compiler, you
could use the Z editor which inserts true tabs. However, Z is a very
difficult editor to use and learn. It would be better if you could set the
tabs anywhere you want This is difficult to program, but this tab
system makes the editor more flexible.
Auto indent
Folding
Programmable We now tum to the programmability of the editor. All of the editor's
functions should be accessible from simple commands. You can enter
multiple commands in one line by placing semicolons between each
command sequence. In addition, command sequences can be repeated by
entering a number before a sequence (either single commands or
multiple commands). So far this editor seems to do everything that ED
does. We're going to take this editor a few steps further.
First we allow the use of variables which can provide numbers for
cursor X/y coordinates, strings or command sequences. If you place a
numeric variable before a command, this command executes the number
of times given in the variable. In addition you can have expanded
384
ABACUS
Data structures Once you have the complete list of ideas you must now think about the
data structures which make up the editor. Data structures correspond to
the important things which you must think about before you begin
programming. All of the Amiga's data structures accessed by the
operating system are of similar design; you only think about the lists.
This makes manipulation of objects easier. The following line is the
first data structure in your editor:
struct Zline
{
The two pointers later construct the linked list. You need two pointers
because you want to move to either the previous line or the next line.
The Amiga also has functions for doubled linked lists, which you can
also use for your lines. If (flags & 128) is unequal to zero, the line
appears in the buffer because the user changed this. This flag is needed
because you must display the buffer if you move to a line where this
flag is seL We will define more flags as the need arises.
Line length
The len variable specifies the line length including the CR, LF or
CR/LF that indicates the end of the line. By this method you can have a
line without an end of line character if the line is longer than the
screen's width. Instead of this line continuing off to the right of the
screen, the line ''wraps'' and text appears on the next screen line without
being split by a CR, LF or CR/LF. Word processors handle lines in
this way.
The line itself directly follows len. Because of the changing lengths,
you cannot include it in the structure. The length of the structure
including the line appears as follows:
EntireLength
4.
AMIGA
Memory blocks The next structure configures the memory blocks containing the text
lines. A linked list connects the blocks of memory. The code needs a
pointer to a list of all the available memory segments in this block, the
length of the block, and the number of unused bytes. The structure
looks like the following:
struct Memoryblock
{
The first two variables insert the memory blocks in a double linked list
The length variable gives the block size without the memory block
structure. This is the equivalent of the number of free bytes present in
an unused block (the length of a memorysection structure). Free
states how many bytes are free in this block. Now the problem arises of
where these free bytes are. These blocks can exist in any location, and
are rarely in sequence. When a line is deleted, a free section of memory
must not lie on another piece of free memory. Because every line is at
least 10 bytes long (sizeof(struct line)!), you have 10 bytes in
which you can construct a list of all of the free memory pieces in a
block. Each element of this list has the following construction:
struct Memorysection
{
Management
The first two elements are chosen with the operating system list in
mind. They are handled as a double linked list for a set of management
functions in the Exec library. Because you use the same chain, you
can also use these functions for your lists without having to write your
own. The element len contains the length of the entire memory
section, including the Memorysection structure.
Next we need an FList structure, which has the same design as the
list header of an operating system list:
struct FList
{
The structure should be familiar to you from the Exec lists (see the
Appendix for an explanation of Exec . library). It is often said that
386
4.1
ABACUS
head points to the fIrst element of the list, tailpred to the last
element of the list, and t ail is equal zero. This arrangement
simplifIes insertion and deletion in the lists.
This may sound like a lot of effort just to manage a line display. We
could specify the memory for each line from the operating system, but
this tends not to work too well. Remember that a text can consist of
1000 or more lines. Every time you change a line, you must release the
old memory and allocate new memory. Once you've spent some time
editing one text, a lot of gaps remain in memory.
When you want a new line, the program should fIrst search for a
memory section that has exactly the desired length. If it cannot find
such a piece, take the memory from the largest memory section
available. Using the largest section as a pattern, be sure that no smaller
memory sections are reduced without needing reduction. For example,
you need 14 bytes and the next largest memory piece has 16 bytes. If
you distribute this, you get two memory pieces, one 14 bytes long and
the other 2 bytes. These two bytes are leftovers. They can't be placed in
the list of free memory pieces because you need at least 10 bytes for the
Memorysection structure.
When you have no more room, allocate a new memory block and insert
it in the block list.
That brings us to the last structure, the Edi tor structure. One exists
for every editor window, and extracts all the important data needed for
the window:
struct Editor
{
Two more structures join the Edi tor structure, which represent the
list heads:
387
4.
AMIGA
struct BList
{
struct ZList
{
struct'Zline *head;
struct Zline *tail;
struct Zline *tailpred;
};
388
4.2
ABACUS
4.2
DEVELOPMENT
STAGES
Development Stages
We want to begin developing the modules from which we can create the
editor. The following source code opens the libraries which let the
Amiga access operating system functions. The Amiga displays a
message if the access is successful. Remember that you can only call
functions of a library if you have opened it with OpenLibrary. It is
also good practice to close all opened libraries.
/* <Editor.c> Version 0.0 */
/************
* Includes:
************/
finclude <exec/types.h>
finclude <intuition/intuition.h>
/***********
* Defines:
***********/
#define REV 33L
/**********************
* External Functions:
**********************/
struct Library *OpenLibrary();
void CloseLibrary();
/*********************
* Global Variables:
*********************/
NULL;
/*****************
* Main program:
*****************/
389
AMIGA
main ()
{
if ( ! (IntuitionBase =
OpenLibrary(nintuition.library",REV)
goto Ende;
if ( !(GfxBase
goto Ende;
= OpenLibrary("graphics.library",REV)
if ( ! (DosBase = OpenLibrary(ndos.library",REV)
goto Ende;
printf(nEverything is open\n");
Ende:
if (DosBase) CloseLibrary(DosBase);
if (GfxBase) CloseLibrary(GfxBase);
if (IntuitionBase) CloseLibrary(IntuitionBase);
Compiling takes some time, since the compiler needs to read the
include files. This long delay also bothered the developers of the Aztec
C compiler, so they added an option which precompiles include files.
This precompilation links the files faster than with normal includes.
For this reason, write a prelist file that contains only the #incl ude
instructions of your source text. The edi tor. prelist file located
in the pre directory, looks like the following in our example:
<pre/editor.prelist>
#include <exec/types.h>
#include <intuition/intuition.h>
Make a directory named PRE and save this file into it. To compile this
file you must inform the compiler that it should create a file using the
precompiled includes. The Aztec C compiler uses the +H option to
declare this:
cc +Hpre/editor.pre pre/editor.prelist
390
ABACUS
This file can be combined with the +I option when compiling the
program:
cc +Ipre/Editor.pre src/Editor.c
The src (source) directory should contain the file Editor.c. The
compiler should now work faster. The make utility program offers
another possibility for simplifying the process. The make utility is
used when a program consists of multiple modules that are linked
together. It is better to divide large programs into smaller program
modules. These modules are then compiled and put together with the
linker. When modules are changed, the programmer must only compile
the edited modules instead of the entire program.
Compiler and linker module control is handled by the make utility.
You enter make, which reads the makefile. The makefile
contains the modules your program consists of, and how these should
be compiled and linked. If you call make, it checks the system date and
time for any changes to sections of your program. It then automatically
recompiles edited modules and links the compiled modules into a
finished program.
You use the makefile to tell the make utility which files of your
program depend on which other files. The object file of one module
depends on the source text, and the finished program depends on the
object files of all of the modules. Because of this dependence and the
date and time when the file was created, make tests for changes and
takes the appropriate steps to create the program. It is therefore
important that the time and the date are correctly set in your Amiga.
To make this somewhat clearer, let's look at how make would be used
to compile your editor. For this we type the names of the files which
comprise your editor:
Editor
src/Editor.o
src/Editor.c
pre/Editor.pre
pre/Editor.prelist
The include files also belong in this category, but they already appear in
pre/Editor.pre. The finished editor program depends on the object
module src/Editor.o. The libraries also playa role, but you don't
change them. You need the dependency of each file to configure the
makefile in case you make any changes in the process of program
development
The object module src/Editor.o depends on the source text
src/Editor.c and on the precompiled includes pre/Editor.pre;
these depend on pre/Editor.prelist, which contains the names
of the includes that should be precompiled. When you want to
document the dependence for the make utility, enter the filenames in
391
the first column of a line, followed by a colon. Following that enter the
filenames on which this file depends. For this first editor the dependence
looks like the following:
Editor:
sre/Editor.o
sre/Editor.o:
sre/Editor.e pre/Editor.pre
pre/Editor.pre: pre/Editor.prelist
The files scr /Edi tor.c and pre/Edi tor .prelist depend on
other smaller files. So that make knows how to compile or link all of
these fIles, you must enter the instructions in the make fIle. Type
these directly under the line that describes the dependence. The
commands cannot begin in the first column, because this is reserved for
the dependents. You should also have at least one line of instructions
after each one. You can enter any instructions behind each dependent.
The make utility executes these until it either encounters a dependent
or the end of the make fIle.
Linking
Now enter the instructions that link your editor created together. To
make the finished program from the object modules, the following
command must be executed:
In sre/Editor.o -Ie
-0
Editor
To create the precompiled include you add two instructions. When you
create these, as you did above, the C compiler creates an object file,
although this wouldn't be necessary because you are only interested in
the precompiled includes. The assembler is also called, which uses
additional time. Combine the call of the assmebler with the -A option.
The assembler fIle which the compiler created is written to the RAM
disk and then deleted:
ee -A +Hpre/Editor.pre pre/Editor.prelist -0 ram:Ed.asm
delete ram:Ed.asm
Your makefile for the creation of the editor should look like the
following, yours may differ slighlty depending on the structure of your
directories (i.e is the pre directory in the src directory):
Editor: sre/Editor.o
In sre/Editor.o -Ie
-0
Editor
392
4.2
ABACUS
DEVELOPMENT STAGES
Make creates the file whose name is found first in the makefile, in
our case Ed ito r. If we want to create another file (e.g.,
src/Edi tor. 0) state the name when calling make from the eLI:
make src/Editor.o
Let's use make just a bit more. Because our program consists of
modules, we can create the makefile before we create all of the
modules. First we want the object module, from which our editor is
made, to be defined in a variable:
OBJ=src/Editor.o
-0
Debug: $ (OBJ)
In -w $(OBJ) -lc
Editor
-0
Editor
The make utility determines that the file Debug doesn't exist and
directs all of the steps to the creation of file specified in the
makefile.
393
AMIGA
how a file with the same name and a different extension (.0) is created
from a file with another extension (.c):
.c.o:
/* Call compiler
c to 0*1
The difference for the above from defined dependent is that the file that
the RAM disk creates has the name Editor.preasm when the file
pre/Editor.pre is be created. Now you can remove the instructions
that follow your dependents for the Edit 0 r . pre file.
The next rule that we want to define states how an object file is created
from a source text. Here you should ensure that your program contains
only a main module in which the mainO function is defined, if it
consists of multiple modules. It allows the C compiler to save some
memory by setting the +B flag for all other modules. While main
begins with a jump to the initialization routine, the other modules do
not require this jump; the initialization routine must only be at the
beginning of main. We will define the rule so that it can create all of
the modules out of the main module:
.c.O:
cc +B +Ipre/Editor.pre -0 $@ src/$*.c
394
ABACUS
<make file>
.c.o:
cc +B +Ipre/Editor.pre -0 $@ src/$*.c
.prelist.pre:
cc -A -0 ram:$*.preasm +H$@ pre/$*.prelist
delete ram:$*.preasm
OBJ=sre/Editor.o
Editor: $ (OBJ)
In $(OBJ) -lc
-0
Debug: $ (OBJ)
In -w $(OBJ) -Ie
Editor
-0
Editor
pre/Editor.pre: pre/Editor.prelist
src/Editor.o: sre/Editor.e pre/Editor.pre
cc +Ipre/Editor.pre src/Editor.c
Here is another file for the editor that will be used in developing our
editor. The file defines the editor's data structures:
<sre/Editor.h>
/************
* Includes:
************1
#include <exec/types.h>
/*******************
* Data structures:
*******************/
struet Zline
(
This file contains only the definitions for lines that you will change.
The makefile must also be changed accordingly:
395
AMIGA
<makefile>
.c.o:
cc +B +Ipre/Editor.pre -0 $@ src/$*.c
.prelist.pre:
cc -A -0 ram:$*.preasm +H$@ pre/$*.prelist
delete ram:$*.preasm
OBJ=src/Editor.o
Editor: $ (OBJ)
In $(OBJ) -lc
-0
Debug: $ (OBJ)
In -w $(OBJ) -lc
Editor
-0
Editor
There is also a rule to make an object file from a * . c file, but because
we have given an extra instruction, the rule is ignored. To conclude, the
editor itself is created after the object file exists:
In src/Editor.o -lc
396
-0
Editor
ABACUS
4.2
DEVELOPMENT
STAGES
With this you have not only finished the first version of your program,
but you also have a stable base on which you can build the additional
programming of the editor. In large programming projects the make
utility and the makefile can save a great deal of time and effort. Be
sure that you understand how the make utility functions or see your C
manual for more information on the make utility that came with your
compiler.
397
4.3
Step by Step
This section describes the step by step construction of our editor
program from the preceding example Editor. c source text. On the
optional diskette each version of the editor is contained in its own
directory, you may wish to do the same with your editor.
4.3.1
Opening a Window
First the editor should open a window. You could copy one of the
example programs from Chapter 2 and edit it to suit your needs.
Because you want to be able to open more windows, you must write
your own routines to open a window and add this to the Editor
structure. You need a function that closes a window again and releases
the Editor structure.
Next let's consider how to open a window. This function should fll"St
try to obtain memory for the Editor structure. If it succeeds, a
window opens and the pointer to this window is entered in the structure.
Then the structure must be initialized, especially for the memory block
and the line lists. The function for closing the window closes the
window then releases the memory occupied by the Edi tor structure.
That would theoretically be all you need. However, if you did it !his
way, you would have to make some changes when you wanted to add
more windows. Every window has a message port through which it
sends its messages to the operating system. Because the
IntuiMessage structure also determines which window receives the
message, the message port will be enough for our purposes. Using the
message port saves you memory.
Your own
MessagePort
398
You must also make it clear to Intuition that you want a single
message port for all of your windows. The program must clear the
IDCMP flags in the Newwindow structure before calling
OpenWindow. Because the window should contain no input, it
receives no message port from Intuition. If a window opens, you enter
the address of your message port, which you initialized from
CreatePort, in the UserPort field of the Window structure. Then
call ModifyIDCMP and define the input that you want to get. Because
Intuition has already found a message port for the window, it doesn't
use a new one. You must test for the closing of the window and set the
UserPort field of the window structure to zero before you call
Closewindow, so your message port will be closed.
4.3
ABACUS
Key display
STEP BY STEP
The next step of our editor will be to display the pressed key. The
operating system has two possibilities: VANILLAKEY and RAWKEY.
While you get the ASCII code of the corrseponding key with
VANILLAKEY, you get scan codes with RAWKEY. RAWKEY returns
which key was pressed, and not which letter the key corresponds to.
Work with RAWKEY in your editor because we can determine whether
the function or cursor keys were pressed. These are not passed by
VANILLAKEY because these keys don't send an ASCII code.
First complete the structure that you need for your editor, this is entered
in Editor.h:
<src/Editor.h>
1************
* Version 1A
* Includes:
************1
iinclude <exec/types.h>
iinclude <intuition/intuition.h>
1***********
* Defines:
*
***********1
ide fine MAXWIDTH 80
1*******************
**
Data structures:
*
*******************1
struct Zline
{
struct Memorysection
{
399
4.
AMIGA
struct FList
{
struct Memoryblock
{
struct BList
{
struct Editor
{
struct EList
{
The structures in the above code have already been described in Section
4.1, except for the last one. The EList structure manages all of the
editor windows, just as the BList structure manages all of the
memory blocks and the ZList structure manages all of the lines. Next
follows the source text of the editor:
<src/Editor.c>
/* Editor.c VIA */
/************
*
400
Includes:
4.3
ABACUS
STEP BY ST E P
************/
#include <exec/types.h>
#include <intuition/intuition.h>
#include "src/Editor.h"
/***********
* Defines:
***********1
1**********************
**
External Functions:
**********************1
1*********************
**
Global Variables:
**********************1
struct Library *IntuitionBase
NULL;
= NULL,
*GfxBase
= NULL,
*DosBase
100,40,440,156,
AUTOFRONTPEN,AUTOBACKPEN,
REFRESHWINDOW I MOUSEBUTTONS I RAWKEY 1 CLOSEWINDOW,
WINDOWSIZING I WINDOWDRAG I WINDOWDEPTH
WINDOWCLOSE
SIZEBBOTTOM
I SIMPLE_REFRESH I ACTIVATE,
NULL, NULL,
(UBYTE *) "Editor",
NULL, NULL,
100,40,640,200,
WBENCHSCREEN
};
= NULL;
1***************
** Functions:
*
*
401
4.
AMIGA
***************/
/***********************************
**
OpenEdi tor ()
************************************/
struct Editor *OpenEditor()
{
ed->window = wd;
/* UserPort established: */
wd->UserPort = edUserPort;
ModifyIDCMP(wd,flags);
/* Parameter initialization: */
NewList(&(ed->block;
NewList(&(ed->zlines;
ed->num lines = 0;
ed->actual = NULL;
ed->top = NULL;
ed->toppos = 0;
ed->xpos = 1;
ed->ypos = 1;
ed->changed = 0;
ed->insert = 1;
}
else
{
freeed) ;
ed = NULL;
newEdWindow.IDCMPFlags
return (ed);
flags;
/***************************
* CloseEditor(ed)
402
4.3
ABACUS
STEP BY STEP
* ed
Editor structure.
***************************/
void CloseEditor(ed)
struct Editor *ed:
{
/* Close window: */
ed->window->UserPort = NULL:
CloseWindow(ed->window):
free (ed) :
/*****************
* Main program:
*****************/
main 0
{
signal
while (imsg
= GetMsg(edUserPort
class
imsg->Class;
code
imsg->Code;
qualifier = imsg->Qualifier;
403
iaddress
= imsg->IAddress;
ReplyMsg(imsg);
1* Event processing: *1
switch (class)
(
case RAWKEY:
printf("RawKey-Event Number %3d code: Code
Qualifer = $%4x. \n", n++, code, qualifier);
break;
= $%4x,
case CLOSEWINDOW:
running = FALSE;
break;
default:
printf(IINot a processable event: %lx\n",class);
} 1* of case *1
1* of while (GetMsg( *1
} while (running);
Ende:
404
To write the program to run on all compilers, you should use the
variable types from <exec/types.h> and not the normal C
4.3
ABACUS
STEP BY ST E P
You should also declare all of the functions that you use in your
program, if only as void. Otherwise you could easily overlook
a function, and the program could stop because you needed a
32-bit pointer but only had a 16-bit pointer available. There is
also no problem if you must compute or return with a 32-bit
integer instead of with a 16-bit integer.
C!oseWindow(xxx);
C!oseLibrary(yyy);
exit (FALSE) ;
Compile and link the program. Run it and press a couple of keys. The
program returns a message when you press a key, and one when you
release a key. When you press one key and hold it down, something
unexpected happens: Once a certain amount of time passes (set by
KeyRepeatDelay), the program displays the same message multiple
times. This continues until you release the key. The only difference
between these messages is that the ninth bit of the qualifier is set,
which means that this key transmits the key repeat function. The editor
allows key repeat with both VANILLAKEY and RAWKEY modes.
405
4.
4.3.2
From
RAWKEY
AMIGA
to ASCII
and selecting the pointer from the io Device field of the IOStdReq
structure. Then you can call RawKeyConvert as a normal function.
In addition to a pointer to an InputEvent structure, you must assign
a pointer to a buffer whose length and a pointer to a KeyMap structure
has been set to zero, because the standard keyboard table should be used
for the conversion. You must fill the Inputevent structure with the
RAWKEY code and the qualifier from the IntuiMessage structure
before the call. To allow ASCII output instead of RAWKEY output, the
followings changes are necessary:
Define the maximum length of keyboard input as 128 characters:
#define MAXINPUTLEN 128L
Now two global variables appear that we already mentioned. We set the
ie_Class field to RAWKEY in the InputEvent structure so that
you don't have to do this explicitly in the program:
struct IOStdReq ioStdReq;
UBYTE inputBuffer[MAXINPUTLEN];
UWORD inputLen = 0;
struct InputEvent inputEvent =
{
0,
IECLASS RAWKEY, 0,
0,0
};
406
4.3
ABACUS
STEP BY STEP
After you open the libraries in the main program, select the pointer to
the console device:
if ( !(OpenDevice{"console.device",-lL,&oiStdReq,OL)
ConsoleDevice = ioStdReq.oi Device;
else
goto Ende;
inputBuffer[l) = 0;
printf{"%3d> Length = %d: ",n++,l);
for (1 = 0; inputBuffer[l); 1++)
printf{"%2x", (UWORD)inputBuffer[l);
if (inputBuffer[O] >= 32 && inputBuffer[O) <= 127)
printf(" %s",inputBuffer);
printf ("\n");
break;
407
Type it in, compile, link, run and examine. While testing the normal
keys don't give any unusual results, the function keys return three or
four characters at a time. You must bear this in mind with your
keyboard check. This is also the reason that VANI LLAKEY returns no
function key information. The IntuiMessage can only return one
character, not three at once. When you examine all of the special
characters and compare them with the codes supplied in the
documentation (see the AmigaOOS manual), you may find that some
codes may not match. There may be some errors in your output.
The complete source text for the section of the editor described above
can be found in the VO .1 directory on the optional disk for this book.
The complete listing for readers who do not have the optional disk is
presented in Appendix A. If you own an Aztec C compiler you can
compile this by changing to the VO.1 directory and entering make
Edi tor. Before this you must ensure that the make utility exists in a
place accessibe to the CLI. For example, you could copy make to the
RAM disk:
copy make to ram:
path ram: add
va.l
make Editor
The single difference from the paths discussed here is that the pre
directory is elsewhere on the disk, in the main editor directory. Because
pre is used for all of the versions of the editor, you should make sure
that the file :pre/Editor.pre exists for each new version of the
editor. Delete the file pre/Editor.pre before you call make for
each new version.
4.3.3
~ernory ~anagernent
You must have a method of saving and joining lines together before
you can load and display a file. You need a function for this which
provides a new Zline structure, into which you can copy a string and
the current line. The function header should look like the following:
struct ZLine *newZline(len)
len;
UWORD
len represents the length of the string, including the end of line
character. Because you can only use memory pieces of even lengths, the
length must appear in the function as an even number. Because this
happens often, create a corresponding define:
408
ABACUS
4.3
STEP BY STEP
Before you implement the function, consider how this should operate. If
no memory block is found in the memory list and the text memory is
also empty, a new block with 10K appears and a line is selected.
Otherwise a search begins for an available line. If this happens, a
garbage collection executes and the memory is released before a second
search executes. If this also happens, not enough memory blocks exist
for further line storage. The program uses a new memory block of only
SK. That is why you should work with two different block sizes, so
that only one memory block will be needed for texts less than 10K in
length. The following flowplan explains our method:
If no memory block exists in the block list
select a new block (length = 10K)
select line
done
else
select line
if none is found
execute garbage collection
select line
if none is found
select new block (5K)
select line
done
for the block. The memory block must then be inserted in the memory
block list. For this you need a pointer to the current Editor structure:
struct Editor *actualEditor;
409
This function searches all the memory blocks of the current editor for a
memory piece that can accept the line. The line length is given in
Line, which must be an even value within the function. It searches for
a memory block. then determines which block is large enough to accept
the text. Since you have not added memory blocks, we can distribute .
the memory pieces in a more optimal way:
First try to find a memory piece that has the exact length of the text If
this doesn't happen, search for the largest memory piece available. This
avoids making a small piece smaller. If a memory piece is so small that
after the new line is cut from it, not enough memory exists for the
memory piece structure, and at least two bytes remain, the search was
unsuccessful. Because the rest must be at least as large, you can insert
another line there.
Remember that each memory block consists of two things: The lines
that lie in it, and the free memory pieces from which room for new
lines is taken.
The following function is the last accessed from newZline:
struct Memoryblock *garbageCollection(len)
UWORD len;
This function reorganizes the free memory inside of the memory blocks
so that only one memory piece exists with the maximum size per
memory block. Each memory piece needs ten bytes of management
information (sizeof(Memorysection. Multiple memory pieces
in a memory block allocate a little free memory. Garbage collection
executes memory block by memory block, until a memory block with
a free memory piece the size of len bytes is found, or until the end of
the memory block list occurs. If the search was successful, a pointer to
the corresponding memory block returns (otherwise, zero is returned).
The pointer can eventually be used to keep the GetZline function
from searching through every memory block.
All of these functions appear in a module called Memory.c. The entire
module is in the following listing:
<src/Memory.c>
/************
**
Includes:
************/
#include
#include
#include
#include
410
<exec/types.h>
<intuition/intuition.h>
<exec/memory.h>
"src/Editor.h"
4.3
ABACUS
STEP BY STEP
1***********
*
* Defines:
************1
idefine BIGBLOCK (200L-sizeof(struct Memoryblock
#define BLOCKSIZE (lOOL-sizeof(struct Memoryblock
1**********************
** External
Functions:
***********************1
struct Memoryblock *AllocMem();
void NewList(),AddTail(),Remove(),FreeMem();
1*********************
** External
Variables:
**********************1
extern struct Editor *actualEditor;
1***************
*
*
*
*
***************1
*
*
Functions:
1*******************************************
**
freeMemoryblock(blk)
*,
* blk Memoryblock.
********************************************1
A
void freeMemoryblock(blk)
struct Memoryblock *blk;
(
Remove (blk);
FreeMem(blk,blk->Iength + sizeof(struct Memoryblock:
1**********************************************
**
newerBlock(len):
**
*
**
*
411
4.
AMIGA
C FOR
ADVANCED PROGRAMMERS
**********************************************/
struct Memoryblock *newerBlock(len)
ULONG
len;
{
* searchMemorysection(block,len)
* Searches for memory piece with len length in the block block
* and returns the pointer or zero in case
* of no room.
* block A Memoryblock.
* len = Length of line (even).
************************************************/
struct Memorysection *searchMemorysection(block,len)
struct Memoryblock
*block;
UWORD
len;
(
NULL;
st->succ)
412
0)
ABACUS
4.3
STEP BY STEP
return (fnd);
1********************************************
**
**
ConvertSpstToZline(blk,st,len)
**
*********************************************1
struct Zline *ConvertSpstToZline(blk,st,len)
struct Memoryblock
*blk;
register struct Memorysection
*st;
register UWORD
len;
{
register UWORD 1;
register struct Zline *z;
if (st->len
==
(1
= EVENLEN(len)))
else
{
1* .distribute piece: *1
z = (struct Zline *)
UBYTE *)st) + sizeof (struct
+= sizeof (struct Zline)) );
st->1en
1;
t
blk->free
1;
Memorysection) + st->len - (1
z->flags = 0;
z->len' = len;
return (z);
1***********************************************
**
**
getZline (len)
413
4.
AMIGA
C FOR
ADVANCED PROGRAMMERS
register awORD I;
register struct Memoryblock *blk,*fblk
register struct Memorysection *st,*fst
=
=
NULL;
NULL;
EVENLEN(len);
= blk-
else
if (st->len> fst)? fst->len :
return (ConvertSpstToZline(fblk,fst,len;
else
1* no piece found: *1
return (NULL);
1*********************************************
* optimalBlock(block)
* block
Memoryblock.
**********************************************1
void optimalBlock
(blk)
register struct Memoryblock *blk;
{
= blk->freeliste.head;
stl->succ; stl
stl->succ)
st2->succ)
414
ABACUS
4.3
STEP BY ST E P
1****************************************
**
garbageCollection(len)
**
*
*
*
*
**
len
= number
*****************************************1
z-
>succ)
if z >= ptr) && (z < end
i f z < fz) II (fz = NULL
fz = z;
i f (fz)
i f (fz != ptr)
{
z = fz;
st = (struct Memorysection *)ptr;
415
4.
AMIGA
spst = *st;
1* pointer correction: *1
fz = (struct Zline *)st;
fz->succ->pred = fz;
fz->pred->succ = fz;
if (actualEditor->actual == z)
actualEditor->actual = fz;
if (actualEditor->top == z)
actualEditor->top = fz;
st = (struct Memorysection *)ptr;
st->succ = spst.succ;
st->pred = spst.pred;
st->len = spst.len;
spst.succ->pred = st;
spst.pred->succ = st;
else
1*****************************************
**
newZline (len)
**
*
*
*
len
= number
of characters.
*****************************************1
register
register
register
register
416
ABACUS
4.3
STEP BY STEP
if (actualEditor->block.head->succ)
1* blocks already exist: *1
if .(z = getZline(len))
return (z);
else
1* Garbage-collection:line not directly usable *1
if (blk = garbageCollection(l - EVENLEN(len)))
if (st = searchMemorysection(blk,l))
return (ConvertSpstToZline(blk,st,len));
else
1* may not actually be encountered 111 *1
return (NULL);
else
1* no reults for Garbage-Collection ->pick new block *1
if (blk = newerBlock(BLOCKSIZE))
(
AddTail(&(actualEditor->block),blk);
return (getZline(len));
}
else
return (NULL);
else
AddTail(&(actualEditor->block),blk);
return (getZline(len));
}
else
return (NULL);
Some functions appear in the above code that haven't yet been
described. This is because some jobs can be done better with functions,
and you don't have to know all the details on every function. Here are a
couple of words for the additional functions:
void freeHemoryblock(blk)
struct Memoryblock *blk;
This function searches the given block for a memory piece that either
has the length len, or for the largest existing memory piece. Either a
pointer to the found memory piece or null returns, which means none
of sufficient length could be found. Be very careful that blk - >f ree
contains the sum of the lengths of the individual memory pieces of this
block, so that a simple check determines if enough space is available. If
the length could not be found, only the larger memory pieces are of
interest: those (sifeof(struct Memorysection) +2) bytes larger
417
than len, because the found memory piece must be distributed between
a line and a memory piece. When no more room exists for the
structures, no distribution occurs.
The following line may be difficult to understand:
if 1 >= minl) && (1 > fnd)? fnd->len : 0)
For those who have trouble with the above line, here is a small tip. If
fnd equals zero, no memory piece was found and cannot be tested to
see if 1 is greater than the length of the memory pieces already found
(fnd). In any case, the second comparison should be true. The
expression fnd) ? fnd->len : 0) equals zero if fnd is zero.
Otherwise fnd ->len (the length of the memory piece fnd). Because
1 is always positive, 1 is always greater than 0, letting you bypass
the special case when fnd is still zero.
struct Zline *ConvertSpstToZline(blk,st,len)
struct Memoryblock
*blk;
register struct Memorysection
*st;
register UWORD
len;
When you want to use a new line, you must convert a memory piece
into a line, or at least take a line from a memory piece. The function
mentioned earlier lowers the amount of work required for this
conversion. If the memory piece was as long as the line, it is removed
from the list of all of the memory pieces. Otherwise the line is removed
from the end of the memory piece with the corresponding length. In
each case the number of free bytes of the block belonging to the
memory piece decreases. It then returns a pointer to the line.
The following function appends memory pieces that lie directly behind
one another:
void optimalBlock (blk)
struct Memoryblock *blk;
You'll always need this function when you lengthen a memory piece or
insert a new memory piece in the list. You must be sure that no two
memory pieces are directly behind one another, because this costs you
10 bytes (sizeof(struct memorypiece.
The following flowplan describes the garbageCollection
function:
FOR all blocks
are the~e at least 2 memory pieces in this block?
move all lines in this block to their beginning
join all memory pieces into one
is there enough room in this block?
return the pointer to the block
else: return zero
418
4.3
ABACUS
4.3.4
STEP BY STEP
**
Includes:
************/
#include <exec/types.h>
#include <intuition/intuition.h>
ilinclude "src/Editor.h"
/***********
**
Defines:
************/
/**********************
**
External Functions:
**********************/
**
External variables:
**********************/
extern struct Editor *actualEditor;
/***************
* Functions: *
***************/
void print ()
{
z->succ)
419
4.
AMIGA
UBYTE 5(80);
1 = strlen (5);
i f (s[1-1) != , ')
(
s[l++) = 13;
s[l) = 0;
if (z
newZline(l
strncpy(z+l,s,l);
AddTai1(&(actua1Editor->zlines),z):
else
printf ("Error in new line!!! \n");
else
printf("Error with gets!!!\n");
void Test 0
{
register UBYTE c;
printf(">");
while c = getchar()
!= 27)
case 'a':
input ();
break;
case 'p':
print ();
break;
case 'b':
print blocks () ;
break;
420
*1
= b1k-
ABACUS
4.3
STEP BY STEP
printf (">");
The print function displays the entire text that you have entered. The
print_blocks function specifies the starting address,line lengths
and the number of free bytes of all of the memory blocks currently in
the editor. inputO expects line input that is added to the end of the list
of all lines. Otherwise, as you do later in the real editor, a null byte
represents the end of line, with which the line forms the output of all of
the lines as simply as possible. There the printf function expects a
pointer to a string as a parameter, which ends with a null byte.
These functions are called from the Test function which waits for line
input from a user. You can add a line by pressing <8> (Append), print
the text by pressing <p> (print), and read a list of all of the memory
blocks by pressing <b> (Blocks). After you enter a letter you must
press the <RetUrn> key, because we still use the CLI window for the
input routine and it is line oriented (input will be sent only after
pressing <Return. The <Return> is not used in the switch case.
Now you must change your program so that the Te s t function is
automatically called. Change the Editor module as follows. First
define the external functions:
void Test () ;
void freeMemoryblock();
And in the main program add the Test function before the main loop
and before the wai t function:
Test ();
Finally, change the makefile so that the two new modules are also
compiled. To do this, expand the line in which OBJ is defined:
QBJ=src/Editor.o src/Memory.o src/Test.o
421
4.
AMIGA
At the end of the make file insert the following two lines:
src/Memory.o: src/Memory.c src/Editor.h pre/Editor. pre
src/Test.o: src/Test.c src/Editor.h pre/Editor.pre
The last change that you should make is set the BIGBLOCK and
BLOCKSIZE defines to smaller values so that you don't have to
enter 10K of text later before you get a second memory block. Change
10K into 120 bytes and 5K into 60 bytes. This also lets you check
what happens when you want to insert a line that is longer than an
entire empty block.
Compile, link and test. When inserting a line that is larger than the
block, a new block is used, even though the line cannot fit there.
Because you set the block size to 5K for later, the following happens: if
only 22 bytes are free in a block, a line with 21 or 22 characters fits
there; or if you insert one with less than 10 characters, then there must
be room for a Memorysection structure.
4.3.5
Deleting Lines
Until now you have only tested the functions for inserting a new line.
To be able to test the functions optimalBlock and garbage
Collection, you need a function that can delete any line. This
function first removes the line from the list, searches for the block
where it lies and converts the line into a memory piece. Then the block
is optimized. The function for the editor looks like this:
void deleteZline
(line)
register struct Zline *line;
{
,
register struct Memoryblock *blk,*fblk
register struct Memorysection *st;
= NULL;
fblk = blk;
break;
422
ABACUS
4.3
STEP BY STEP
i f (fblk)
{
If the line was the last line in the memory block, this is deleted. Before
you write a few additional functions for testing, a note about
ConvertSpst ToLine. Because it was easy to program, we created
the function so that the line ends if the line to be inserted is smaller
than the memory piece. This means that the lines in a block extend
from back to front.
You have already determined that the garbage collection
moves all the lines to the beginning of the block. It's not too late to fIx
this back-to-front problem. Change the ConvertSpstToLine
function so that the lines extend from front to back:
struct Zline *ConvertSpstToZline(blk,st,len)
struct Memoryblock
*blk;
register struct Memorysection
*st;
register UWORD
len;
{
register UWORD 1;
register struct Zline *z;
z = (struct Zline *)st;
if (st->len -= (1 = EVENLEN(len)
1* Found piece fits exactly: *1
Remove(st);
else
{
1* distribute piece: *1
UBYTE *)st) += (1 += sizeof (struct Zline;
( st->succ '"' struct Memorysection *)z)->succ )->pred
( st->pred = struct Memorysection *) z) ->pred ) ->succ
st->len = struct Memorysection *)z)->len - 1;
}
blk->free
-=
= st;
= st;
1;
z->flags = 0;
z->len z len;
retum (z);
}
423
4.
AMIGA
C FOR
ADVANCED PROGRAMMERS
-0
ram:sp.asm
The created assembler code can be found on the RAM disk under the
name sp.asm. Because the assembler code contains the C commands as
comments. it's not too difficult to find the place. Search for the label
_ConvertSpstToZline, then cursor down until you see the above
lines as comments. Directly under that you should fmd the assembler
code for these lines. This code looks like this:
;
= st;
move.l (a3),aO
;a3 = z
;a2 = st;
move.l aO, (a2)
move.l a2,4(aO)
( st->pred = struct Memorysection *)z)->pred )->suoc
move.14(a3),aO
move.l aO,4(a2)
move.l a2, (aO)
= st;
(st->succ
Notice how the assembler code looks is similar for both functions. You
can improve it. For example, the following section from the
garbageCollection function moves a piece of memory:
/* push line down after ptr: *1
for (n = (EVENLEN(fz->len) + sizeof(struct Zline
;
;
;
1;
n; n--)
move.l dS,aO
move.l #O,dO
move.b 9(aO),dO
add.w #l,dO
belr.l #O,dO
add.w #10,dO
move.w dO,d6
lsr.w #l,d6
bra .74
;dS = fz
;d6 = n
;a2 = ptr
.73
;
.71
sub.w #l,d6
.74
tst.w d6
bne .73
.72
;
Here you can improve the loop labeled. 73. You can replace the five
instructions directly after label. 73 with one (MOVE.W (An)+ ,(Am)+) if
you place the variable f z in an address register instead of in a data
register You can append the three instructions after label .71 together
424
4.3
ABACUS
STEP BY STEP
fz = NULL;
fst = NULL;
425
4.
AMIGA
else
{
1* Memorysection: *1
printf(fOMemory section (%d)\n",fst->len);
ptr += sizeof(struct Memorysection) + fst->len;
else
{
1* Zline *1
printf(fOLine: %s\n",fz+l);
ptr += sizeof(struct Zline) + EVENLEN(fz->len);
printf ("\n");
void delete_line()
{
printf(fODelete: %s\n",z+l);
deleteZline (z) ;
ABACUS
4.3
STEP BY ST E P
Compile the program and examine the individual modules. Add new
lines, delete others and constantly examine the memory arrangement.
Maybe you will find some things that can be improved. The complete
source text and the compiled editor can be found in directory VO. 2 of
the optional disk for this book.
4.3.6
427
Window
sizing
SMART_REFRESH
SUPER BITMAP
428
There is another problem with text output in your window. You do not
want to write in the border of your window. The GIMMEZEROZERO
ABACUS
4.3
STEP BY STEP
flag provides you with your own RastPort for input inside of the
window. The window border has its own RastPort as well, so that you
cannot write in the border. But this also requires more memory, and the
output is slower because you must manage two RastPorts from
Intuition. You want the simplest, more efficient way, so clipping
makes sure that your text does not run into the screen border.
Two functions exist for the actual text output. One comes from the
graphics library and is called Text; the second comes from
Intuition and is called PrintIText. You assign Text a pointer to
the RastPort, the text and the length. PrintIText needs one pointer
to the RastPort, one pointer to the IntuiText structure and X and Y
coordinates. You can specify the text's writing mode, color, position
and the font parameters. The text to which the IntuiText structure
points must end with a null byte.
At first glance the Intuition functions appear to be more elegant than
the graphic functions, which look like sloppy programming. The
elegance of Intuition functions come with a price-time. Which special
functions do we need to output text in our editor? We need to determine
the output position (Le., Move from graphics library). We
want to display control codes in red and change the text color back
(SetAPen from graphics library). We also want reserved C
words to appear in bold print (SetSoftStyle). Those are all the
functions we need from graphics library. We chose the more
primitive functions because they execute faster than normal operating
system functions.
How can we make sure that text doesn't overwrite the window's border?
Simple. We first establish the total size of the window within the
borders, measured by character height and width. Then we determine
when no more characters should be displayed. We place the width and
height of the window in the Editor structure, because this value can
be shared by multiple windows. Since these two values change as the
window size changes. we must reset the window using the NEWSIZE
flag listed in the I DCMP flags. Every time a corresponding report
returns, the program recalculates window height and width. A
SIMPLE_REFRESH window returns a REFRESHWINDOW report, and
redisplays the contents of the window.
The right and bottom borders still remain. These borders don't belong to
either the window border or the text (the window width is not divisible
by the letter width). The same goes for the height. The following
illustration shows exactly how we solved this problem:
429
fll
I4-CN-.!
ch
1.
4-
Basel ine
yoff
_ _ _ _u
yru
Figure 4.1
xrl
xoff
xrr
The letters are not proportional to make it easier to view. The letter
above the window shows an example of the height and width of the
characters (cw =character width, ch = character height). Because we
don't use any proportional character sets in this example, these values
remain the same for all letters. The window width in characters (wcw =
WindowCharacterWidth) comes from subtracting the width of the
window (window->Width) from the width of the left border
(Window->BorderLeft) and right border (window>BorderRight), then dividing the result by the width of one
character (cw).
The height is calculated in much the same way:
wch
(window->height - window->BorderTop
- window->BorderBottom) I chi
The width of the left and top screen borders specify the top left position
of the window. We save the two values to the Edi tor structure as
xoff (X offset) and yoff (y offset). The same goes for cw and ch.
which can change from window to window if you change the character
set using Preferences, then open another window. A pointer to the
RastPort of the window belongs in our Editor structure so that we
don't always have to access the window structure. We know that
graphic output on the Amiga is through the RastPorts.
You know what to do about the borders. The editor may never need
this, but it will keep graphic garbage to a minimum. Each time the
program receives a REFRESHWINDOW report it deletes the contents of
430
ABACUS
4.3
STEP BY STEP
both borders. We need additional information for this (e.g., the border
positions). The above illustration listed additional coordinates with xrl
(X, Border, left comer), xrr (X, border, right corner), and yru (y,
border, bottom corner). We need the top edge of the border, which is
given in the drawing by Y. Y provides itself in part, so no calculations
are needed yet. Display the text line by line and count the Y
coordinates. If either the window fills up or no more text is available,
the Y coordinate of the line appears directly beneath the last text line.
This corresponds exactly to the top corner of the border.
There are more details given in the program. When positioning the text,
be careful that the text always appears relative to its baseline (see
illustration above). We must increase the Y position to match the
distance of the baseline from the top edge of the letter before calling
Move.
Now combine the individual functions that handle text output into one
function:
void printAll ()
This function displays the entire text. Next the program tests the top
line in the window as long as the line appears, until the window fiUs or
the end of the text occurs. The printLine function passes control to
a pointer to the line and the current Y position. Then the two borders
are deleted, provided they are greater than zero.
The next function displays the line left-justified at the Y position:
void printLine(line,y)
This is not as easy as it sounds. Our editor works with true tabs, which
would appear as little graphic boxes. When we let the output run from
the console.device, the tabs are displayed correctly. The console. device
uses too much memory overhead, which makes the output slower and
won't let us start anything. We must convert the tabs ourselves.
Another function performs this task:
void convertLineForPrint(text,length,width,buffer)
This function expects a pointer to the text (not the Line structure) and
its length. You must assign a pointer to a buffer in which the converted
lines are written, because spaces fill in the buffer to the end of the line.
This function doesn't display a visible CR/LF, because the CR or LF
should not appear at the end of a line (for aesthetic reasons, if for
nothing else). If the line doesn't end with a CR, LF or CR/LF, the
function fills the rest of the line with dots (CHR$ (183) ) This also
applies to lines that don't completely fit in the window because they are
too long. A point at the end of a line means that the lines continues,
and multiple points mean that the line doesn't conclude with a normal
end of line character.
431
4.
AMIGA
We can now convert and display the line using the Text graphic
function. Because a line can include spaces, or because the line is
narrower than the window ,the line could be indented for fonnatting
reasons. This formatting becomes inefficient. Displaying spaces
requires the same amount of time as displaying a letter or number. A
faster method would be to replace any larger number of spaces by
drawing a rectangle the same size as the spaces to be displayed. The
following function perfonns this task:
void printAt(buffer,length,x,y)
Folding refers to the ability to fold text into areas in memory where the
line wouldn't nonnally be visible. Usually you would search through
hundreds of lines to find a routine. Folding allows you to move
routines into their own areas, marked by the following string:
I*#FOLD: Routine_Name *1
MAXHEIGHT 50
FGPEN 1L
BGPEN OL
CTRLPEN 3L
CONTROLCODE(c) (c)&127)<32)
struct Editor
{
432
4.3
ABACUS
STEP BY STEP
*zptr = fz;
break:
require the addition of zprt and n (UWORD). Add to the above lines
the following:
433
*zptr = NULL;
break;
The following two new functions test for one line before or after the
current line. These may seem unnecessary, but the folding extension
will need them later:
1*********************************************
* nextLine (line)
* line
line structure.
*********************************************1
if (z
line->succ)
i f (! (z->succ) )
NULL;
return (z);
else
return (NULL);
1*******************************************
**
prevLine (line)
*
*
* line
line structure
*
*
*******************************************1
434
4.3
ABACUS
STEP BY STEP
if (z
line->pred)
i f (! (z->pred
z = NULL;
return (z);
}
else
return (NULL);
register
register
register
register
register
register
register
flags = newEdWindow.IDCMPFlags;
newEdWindow.IDCMPFlags = NULL;
ed->window = wd;
ed->rp = (rp = wd->RPort);
435
/* zlinesptr-Array initialization: */
for (n = 0, zptr = ed->zlinesptr; n < MAXHEIGHT; n++,
zptr++)
*zptr = NULL;
/* Tab initialization: */
ptr = ed->tabstring;
'*ptr++ = 1;
for (n = 1; n < MAXWIDTH; n++)
i f (n % 3)
*ptr++ = 1;
else
*ptr++ '" 0;
ed->wch = 0;
initWindowSize(ed);
else
(
free(ed);
ed = NULL;
newEdWindow.IDCMPFlags
return (ed);
flags;
Tab initialization gives the user a couple of predefined tabs. Two new
flags must be inserted in the routine which checks the report types in
the main program:
case NEWSIZE:
initWindowSize(actualEditor);
print ();
break;
case REFRESHWINDOW:
BeginRefresh(actualEditor->window);
printAll () ;
EndRefresh(actualEditor->window,TRUE);
break;
* Includes:
*************/
#include <exec/types.h>
#include <intuition/intuition.h>
436
43
ABACUS
STEP BY STEP
#include "src/Editor.h"
/***********
* Defines:
************/
Itdefine CR 13
#define LF 10
#define TAB 9
/**********************
**
External Functions:
**********************/
void SetAPen(),RectFill(),Text(),Move();
struct Zline *nextLine();
/*********************
**
External Variables:
*********************/
**
Functions: *
****************/
/**********************************
* initWindowSize(ed)
*
**
ed
Editor structure.
**********************************1
void initWindowSize
(ed)
register struct Editor *ed;
{
register
register
register
register
w
rp
= ed->window;
= ed->rp;
ed->xoff
ed->yoff
(UWORD)w->BorderLeft;
(UWORD)w->BorderTop;
437
4.
ed->cw
ed->ch
AMIGA
(rp->TxWidth)? rp->TxWidth : 8;
(rp->TxHeight)? rp->TxHeight : 8;
=
=
=
ed->xoff + ed->wcw*ed->cw;
w->Width - w->BorderRight - 1;
w->Height - w->BorderBottom - I:
rp->TxBaseline;
1****************************************
**
convertLineForPrint(line,len,buf)
*
**
line" Zline.
* len = the length.
* w : maximum wiidth.
* buf "buffer, in which the converted line
*
is stored.
****************************************/
438
ABACUS
4.3
tab
STEP BY STEP
line + len - 1;
CR)
i f (*tab =
{
len--;
lastchar
I.
else i f (*tab
LF)
len--;
lastchar = I ' ;
if len) && (*--tab
len--;
else
lastchar
else
lastchar
CR
183;
183;
/* Zline convert: */
tab = actualEditor->tabstring;
while len--) && (1 < w
i f *buf = *line++) = TAB)
cD
{
*buf++ =
1++;
';
tab++;
buf++;
tah++;
1++;
(len >= 0)
lastchar = 183:
* printAt(buf,len,x,y)
**Prive
*
*
*
*
*************************************/
void printAt
(buf,len,x,y)
439
4.
AMIGA
C FOR
ADVANCED PROGRAMMERS
WORD
{
1* output spaces: *1
x2
x-I;
while *buf =
buf++;
x2 += cw;
SetAPen(rp,BGPEN);
RectFill(rp, (ULONG)x, (ULONG)y, (ULONG)x2, (ULONG)y2);
SetAPen(rp,FGPEN);
x = ++x2;
}
else
{
SetAPen(rp,CTRLPEN);
while (CONTROLCODE(*buf) && len--)
*buf++ 1= 64;
1 = buf - ptr;
Text (rp,ptr,l);
SetAPen(rp,FGPEN);
else
{
x += cw*l;
1************************************
* printLine(line,y)
440
ABACUS
4.3
STEP BY STEP
************************************1
void printLine
(line,.y)
register struct Zline *line;
register UWORD
y;
{
= actualEditor-
1**********************************
* printAll
**********************************/
void printAll ()
{
ch
= actualEdi tor->ch;
count
Ip
actualEditor->yoff;
= actualEditor->wch;
actualEditor->rp:
Now here are a few comments about the above functions and the special
additions:
'
Now rewrite the print function so that it supplies the values to the
pointer established in the zlinesptr array:
void print ()
{
442
ABACUS
4.3
STEP BY STEP
n++, z++)
printf("Line %d an Adresse %61x.\n",n,*z);
Finally the actual test program needs a change. After inserting and
erasing lines, zlinesptr must be re-initialized. Furthennore, the
printAll function must be called by the command p (print). The
corresponding branch of the switch case application looks like the
following:
case 'a':
input ();
init zlinesptr();
break;
case 'p':
print ();
printAll () ;
break;
case 'b':
print blocks () :
break;
case
151:
print memory () ;
break;
case 'd':
delete_line ();
init zlinesptr();
break;
Next add the following line and object definition (OBJ= ) to the
make file.
src/Output.o: src/Output.c src/Editor.h pre/Editor.pre
443
incorrectly. Remove the increment (++) and process this separatelythe C compiler creates a more efficient object code. Compile the
following test program to create its assembler source text:
main 0
{
= string;
for (n = 0, ptr
*ptr = 0;
= string;
ptr~
cc test.c -a -t
You get the assmebler source text with the name Test.asm, which
looks like the following using our Aztec C compiler:
main 0
;
;
public main
main:
- link as,it.2
movem.l .3,-(sp)
register char *ptr;
char string[ID];
register short n;
;
;
;
*ptr++ = 0;
move.l a2,aO
add.l In, a2
clr.b (aD)
.4
add.w In,d4
.1
cmp.w 1n0,d4
bIt .6
.5
;
for (n
move.l itO,d4
lea -10(a5),aO
move.l aD, a2
bra .11
.10
;
ptr
= string;
*ptr
clr.b (a2,
444
= 0,
= 0;
4.3
ABACUS
STEP BY ST E P
.8
add.w #1,d4
add.l #1,a2
.11
cmp.w #10,d4
bIt .10
.9
,
.12
movem.l (sp)+,.3
unlk as
rts
.2 equ -10
.3 reg d4/a2
public .begin
dseg
end
Look at the two loop bodies in particular, for the first loop from
label. 6 to label. 7, and for the second loop from label. 1 0 to
label. 11. You see yourself that the first loop needs four commands
while the second has three commands. Therefore it is especially
important that these commands be run through as often as the loop
counter allows. This command becomes more powerful the more often
you execute the loop.
It is worthwhile to examine the assembler source text. There the
compiler manufacturer tries to make their product as efficient as
possible. The loop counter procedure is discussed later in this book.
4.3.7
The Cursor
The editor should let the user move the cursor to any point in a source
text for easy editing. The following section shows how to expand the
editor for cursor movement using either cursor keys or mouse. When
the cursor reaches the bottom or top border of the window, the window
contents must scroll to allow access to the next or previous lines in the
window. This vertical cursor movement is important, since almost
every source text has more lines than can fit into just one window.
Horizontal scrolling must be available as well. When the user reduces
the size of the editor window, lines that are wider than the reduced
window must also be accessible through scrolling.
Folding
445
4.
AMIGA
with the zlinesptr array, an array is needed that contains the line
number for every field that is visible in the editor window.
The editor specifies the cursor's line position through the zlinesptr
array. In addition to the true line position, the zlinesnr array needs
the window position. This also allows testing without the
implementation of folding, before including the folding routine.
Tabs
Tabs present the next problem. The editor Z (included with the Aztec C
compiler) forces the cursor to jump over multiple columns if it lands
on a tab. Cursor movement depends on the character on which the
cursor is found. This is why the cursor stands on the last character of a
line instead of behind a line. The cursor position should be fully
selectable, even though it causes a few problems with character input
(e.g., what happens if you overwrite a tab with another character).
Scrolling
The fold ending mark appears at the end of the text the programmer
wants folded, and looks like this:
446
4.3
ABACUS
STEP BY STEP
I*#ENDFD*I
Both marker strings must appear at the beginning of a line (no leading
spaces). You may have noticed that the marks begin with the two
characters that normally start comments in C (! *). This is necessary
because the C compiler must skip these lines. Later we'll show you
how to change the marks so that the programmer can use the folding
technique in other languages.
The words fFOLD and fENDFD must be entered in capital letters. The
editor will not fold lines using lower case lettering (e.g., fFold or
:ftendfd are unacceptable to the editor).
The colon following the word fFOLD lets you add comments or
function names. This can tell the user the purpose of the program text
within the fold. A completed fold displays the line as seen above and
the commentary. For example:
I*#FOLD: InnerRoutine *1
Fold levels
To read a fold, move the cursor onto the starting mark. Press
<Ctrlxf> (f as in "fold"). The editor displays the contents of the fold.
You can have multiple levels of folding (inserting folds within folds).
When creating folds, the editor defines the level of each set of lines and
the depth of the fold in which the lines lie. The first line in a text lies
at level zero. A fold starting mark increments the level by one, and a
fold ending mark decrements the level by one.
The following example displays the fold level on the left margin. The
lines on the right correspond to the lines within the fold level:
Line 1
1
1
Line 3
I*#FOLD: Line 4 *1
Line 5
I*#ENDFD Line 6*1
Line 7
I*#ENDFD Line B *1
Line 9
I*#FOLD: Line 10 *1
I*#FOLD: Line 11 *1
Line 12 I*#ENDFD Line 13 *1
Line 14
I*#ENDFD Line 15 *1
Line 16
-
o I*#FOLD: Line 2 *1
2
2
1
o
o
1
2
2
1
The Edi tor structure defines two variables named minfold and
maxfold. Only the lines whose levels lie between minfold and
maxfold are displayed in the window.
<Ctrl><f> enters the next fold level in two ways:
447
4.
AMIGA
o
o
o
o
o
o
o
1
1
1
1
o
o
1
1
1
1
1
1
448
You see only the lines whose fold level is between minfold
and maxfold. or equal to zero. Press <Ctrl><f> while the
cursor is in line_l. Most of the text unfolds. revealing the
following:
Line 1
/*#FOLD: Line 2 *1
Line 3
/*#FOLD: Line 4 */
Line 7
I*#ENDFD Line 8 */
Line 9
/*#FOLD: Line 10 */
/*#FOLD: LIne~ll */
Line 14
/*#ENDFD Line 15 */
Line 16
-
ABACUS
4.3
STEP BY STEP
These need no arguments, and return either lRUE (the cursor moves) or
FALSE (the cursor remains stationary, or reaches the end ofa line). We
have no use for this return value at the moment. However, if you wish
to add more capabilities to the editor later on, knowing whether or not
the cursor actually moved can be useful. This knowledge can be used in
conjunction with interrupt criteria for loops.
These cursor movement functions call other functions that scroll the
editor window contents in the corresponding direction:
scrol11eft, scrollRight, scrollDown, scroll Up
Cursor display Cursor movement is useless without displaying a visible cursor. The
Cursor function displays the cursor in COMPLEMENT mode:
void Cursor ()
When we call this function before the Wait function in the main loop.
the cursor appears when the editor is inactive. When the program is
busy, the cursor disappears and cannot be used.
Now that you know the important functions for the new module, add
these items to the Edi tor. h file:
ide fine FOLDPEN 21
idefine Z1INESPTR(n) aktuellerEditor->zeilenptr[n]
#define Z1INESNR(n) aktuellerEditor->zeilennr[nl
449
FOLDPEN is the color in which the fold marks are displayed (usually
black). The ZLINESPTR and ZLINESNR functions should ease access
to the elements of corresponding fields. Instead of:
actualEditor->linesptr[nl
write:
ZLINESPTR(n)
The last two defines belong to the line structure flags in the data
structure Line. When the flag ZLF FSE is set, it handles the line as a
fold mark. It doesn't matter whetheilt is handled as a fold start or fold
end mark (FSE = Fold Start End). ZLF FOLD specifies the fold level
of the line (for non-folded lines ZLF FOLD=O). Be careful that the fold
start marks themselves don't belong to the fold. The maximum fold
level is 63.
The expanded Edit 0 r structure looks like this:
struct Editor
(
Ii
This contains the numbers of all of the lines that are visible in the
window (see also zlinesptr).
450
4.3
ABACUS
STEP BY STEP
leftpos
When the window scrolls to the left, the first column of the window
changes to the next column up. leftpos specifies the number of the
column directly to the left of the visible window. leftpos has a
default value of zero, which means that the first visible column in the
window has the number one.
wdy
This specifies the line position of the cursor with a value between zero
and (actualEditor->wch-l).
xscr, yscr
Specifies the minimum and maximum fold levels that determine which
folds appear on the screen and which don'L
The following listing for the Cursor module contains all of the
previously described cursor functions:
<src/Cursor.c>
/************
*
*
Includes:
************/
#include <exec/types.h>
#include <intuition/intuition.h>
#include "src/Editor.h"
/***********
** Defines:
************1
fdefine
#define
#define
#define
#define
#define
#define
CSI Ox9B
CUU 'A'
CUD 'B'
CUF 'C'
CUB '0'
SU 'S'
SO 'T'
fdefine CFOLD 6
#define CENOF 5
/**********************
*
451
4.
AMIGA
C FOR
ADVANCED PROGRAMMERS
* External Functions:
***********************/
void SetDrMd(),RectFill(),printAlI();
struct Zline *nextLine(),*prevLine();
1*********************
* External Variables:
**********************/
extern struct Editor *actuaIEditor;
extern UWORD SplitDec;
/***************
**
Functions: *
****************1
*
1************************************
**
**
*
*
*
*
restoreZlinenptr:
Restore the zlinesptr/nr field
for all lines in window plus
the next line.
The first line must be
correct!
*************************************1
void restoreZlinenptr()
{
register UWORD n;
register struct Zline **zptr,*z;
register UWORD *pnr;
UWORD znr;
for (n
*zptr++
*pnr++
(z
= nextLine(z,&znr));
znr;
1**********************************
** Cursor:
*
***********************************/
void Cursor 0
{
4Sl
4.3
ABACUS
STEP BY STEP
(actuaIEditor->xpos - actuaIEditor->leftpos - 1)
*(cw = actuaIEditor->cw) + actuaIEditor->xoff;
y = actuaIEditor->wdy
*(ch : actuaIEditor->ch) + actuaIEditor->yoff;
=
* scroIIRight(num):
* nurn
= number of characters.
*******************************1
void scroIIRight(nurn)
register UWORD
num;
{
-=
num;
1* Scrolling: *1
ScrollRaster(actuaIEditor->rp,
-num * (LONG) actualEditor->cw, OL,
(LONG)actualEditor->xoff, (LONG)actualEditor->yoff,
(LONG)actualEditor->xscr, (LONG)actuaIEditor->yscr);
1* now list printAII(): */
wcw : actualEditor->wcw;
actuaIEditor->wcw = num + 1;
SplitDec = 1;
y
actualEditor->yoff;
actualEditor->ch;
count = actualEditor->wch;
ch
1* OUtput line: */
SplitDec = 0;
/* Display right border beside last character */
453
4.
AMIGA
C FOR
ADVANCED PROGRAMMERS
leftpos = actuaIEditor->leftpos;
xoff
= actuaIEditor->xoff;
actuaIEditor->wcw
= 2;
actuaIEditor->leftpos += (y = wcw - 2);
actuaIEditor->xoff
+= actuaIEditor->cw * y;
y
count
actuaIEditor->yoff;
actua1Editor->wch;
1* OUtput line: *1
for (z = actua1Editor->z1inesptr; count-- && (*z != NULL);
z++, y += ch)
printLine(*z,y);
actuaIEditor->wcw
= wcw;
actuaIEditor->leftpos= leftpos;
actuaIEditor->xoff = xoff;
1*******************************
**
scrollLeft(num):
*******************************1
void scrollLeft(num)
register UWORD num;
{
1* Scrolling: */
ScroIIRaster(actuaIEditor->rp,
num * (LONG)actuaIEditor->cw,OL,
(LONG)actuaIEditor->xoff, (LONG)actualEditor->yoff,
(LONG)actuaIEditor->xscr, (LONG)actua1Editor->yscr);
1* now list printAII(): */
wcw
actuaIEditor->wcw;
1eftpos = actuaIEditor->1eftpos;
xoff
= actuaIEditor->xoff;
actuaIEditor->wcw
= num + 1;
actuaIEditor->leftpos += (y = wcw - num - 1);
actuaIEditor->xoff
+= actuaIEditor->cw * y;
y
actuaIEditor->yoff;
actuaIEditor->ch;
count = actuaIEditor->wch;
ch
454
4.3
ABACUS
STEP BY STEP
1* Output line: *1
for (z = actualEditor->zlinesptr; count-- && (*z != NULL);
z++, y += ch)
printLine(*z,y);
actualEditor->wcw
= wcw;
actualEditor->leftpos= leftpos;
actualEditor->xoff = xoff;
1*******************************
**
**
scrollUp(num):
**
*******************************1
>= web)
z = ZLlNESPTR(n);
znr = ZLINESNR(n);
while (++n < nurn)
z = nextLine(z,&znr);
}
else
{
z = ZLINESPTR(n - 1);
znr = ZLINESNR(n - 1);
4SS
4.
AMIGA
1* Scrolling: *1
ScrollRaster(actualEditor->rp,
OL,num * (LONG)actualEditor->ch,
(LONG)actualEditor->xoff, (LONG)actualEditor->yoff,
(LONG)actualEditor->xscr, (LONG)actualEditor->yscr);
1* zlinesptr/nr recalculated: *1
zptr = actualEditor->zlinesptr;
zold = &(ZLlNESPTR(num;
pnr = actualEditor->zlinesnr;
oldnr = &(ZLINESNR(num;
for (n = 0; n <= wch - num; n++)
{
*zptr++ = *zold++;
*pnr++ = *oldnr++;
z = *--zold;
zold = zptr - 1;
znr = *--oldnr;
oldnr = pnr - 1;
while (n <= wch)
1*
*1
*zptr++ = (z = nextLine(z,&znr;
*pnr++ = znr;
n++;
printLine(*zold,y);
actualEditor->toppos += num;
1*******************************
* scrollDown(num):
*
*
*
*
456
ABACUS
4.3
STEP BY ST E P
z = prevLine(z,&znr);
/* z points to first line after bottom windo edge. */
for (n = 0, zptr = &(ZLlNESPTR(wch, pnr =
&(ZLlNESNR(wch;
n <= wch; n++)
*zptr-- = z;
*pnr-- = znr;
z = prevLine(z,&znr);
/* Now redisplay the window: */
printAll();
}
else
{
/* Scrolling: */
ScrollRaster(actualEditor->rp,
OL,-num * (LONG)actualEditor->ch,
(LONG)actualEditor->xoff, (LONG)actualEditor->yoff,
(LONG)actualEditor->xscr, (LONG)actualEditor->yscr);
/* zlinesptr recalculated: */
zptr = &(ZLlNESPTR(wch;
zold = &(ZLINESPTR(wch - num;
pnr = &(ZLlNESNR(wch;
oldnr = &(ZLINESNR(wch - num;
for (n = 0; n <= wch - num; n++)
{
*zptr-- = *zold--;
*pnr- = *oldnr--;
for (z = *++zold, znr = *++oldnr, n = num; n; n-)
{
*zptr-- = (z = prevLine(z,&znr;
*pnr-- = znr;
/* Output new line: */
for (n = num, y = actualEditor->yoff; (n && (*zold !=
NULL) );
**
**
cursorLeft:
*****************************************/
457
4.
BOOL cursorLeft ()
{
if (actuaIEditor->xpos > 1)
{
actualEditor->xpos--;
if (actuaIEditor->xpos <= actuaIEditor->leftpos)
scrollRightUWORD)1);
retum (TRUE);
}
else
return (FALSE);
1*****************************************
**
cursorRight:
*
* moce cursor one position
* right, False returned if cursor,
* is in last column.
******************************************1
BOOt cursorRight ()
{
actualEditor->xpos++;
if (actuaIEditor->xpos
>= (actuaIEditor->leftpos + actuaIEditor->wcw
scrollLeft UWORD) 1);
return (TRUE);
}
else
return
(FALSE);
1*****************************************
cursorUp:
* Move
* False
*
*
*****************************************1
BOOL cursorUp ()
{
UWORD help;
if (actuaIEditor->wdy)
actuaIEditor->wdy--;
else
if (prevLine(ZLlNESPTR(O),&help
scroIIDownUWORD)1);
else
retum (FALSE);
458
4.3
ABACUS
actuaIEditor->ypos
STEP BY STEP
ZLINESNR(actuaIEditor->wdy);
return (TRUE);
1******************************************
* cursorDown:
*
*
*
*
*******************************************1
BOOL cursorDown()
{
if (ZLlNESPTR(actuaIEditor->wdy + 1
{
scrolIUpUWORD)l);
actualEditor->wdy--;
actuaIEditor->ypos
ZLlNESNR(actuaIEditor->wdy);
return (TRUE);
else
return (FALSE);
1**********************************
**
halfpageUp:
***********************************1
BOOL halfpageUp()
{
= ZLlNESNR(actualEditor->wdy);
return (TRUE);
459
4.
AMIGA
else
return (FALSE);
1**********************************
* halfPageDown:
**********************************1
BOOL halfPageDown()
{
= nextLine(z,&help)
break;
i f (n)
{
scrollUp (n) ;
actualEditor->ypos
ZLINESNR(actualEditor->wdy);
return (TRUE);
else
return (FALSE);
/*******************************
* handleKeys(buf,len):
*
*
*
buf
len
= number
*******************************1
void handleKeys(buf,len)
register UBYTE *buf;
register WORD
len;
{
first = *buf;
460
4.3
ABACUS
STEP BY STEP
OOf++;
len--;
if first
len--;
switch (*buf++)
{
case CUU:
cursorUpO;
break;
case CUD:
cursorDown();
break;
case CUF:
cursorRight () ;
break;
case CUB:
cursorLeft () ;
break;
case SU:
halfPageDown 0;
break;
case SD:
halfPageUp() ;
break;
default:
buf--;
len++;
break;
)
else if first
== CENDF)
&& (actualEditor->maxfold
actualEditor->maxfold--;
if (actuaIEditor->minfold > actualEditor->maxfold)
actuaIEditor->minfold = actualEditor->maxfold;
if (ZLINESPTR(O)->flags & ZLF_FOLD) > actualEditor>maxfold)
461
4.
AMIGA
ZLINESPTR(O) = prevLine(ZLlNESPTR(O),&(ZLlNESNR(O);
actualEditor->wdy = 0;
restoreZlinenptr();
printAll () ;
actualEditor->ypos = ZLINESNR(actualEditor->wdy);
else
putchar (first);
462
To test the vertical scrolling for more than one line, the
halfPageUp and halfPageDown functions scroll the text a
4.3
ABACUS
STEP BY STEP
sense.
Now we can expand the other modules. Two new functions must be
declared in the main Edi tor module:
void handleKeys(),Cursor();
if z = z->succ) ->succ)
i f z->flags & ZLF FOLD) < minfold)
{
1* Following lines lie outside of fold *1
z = NULL;
break;
else
*pnr += 1;
else
{
1* no following line */
z = NULL;
break;
}
463
return (z);
i f z = z->pred) ->pred)
z = NULL;
break;
else
*pnr += 1;
else
{
1* no previous line *1
z = NULL;
break;
}
while z->flags
else
&
= NULL;
return (z);
The OpenEdi tor function initializes the other elements of the editor:
ed->leftpos
ed->wdy
ed->minfold
ed->maxfold
= 0;
=
=
=
0;
0;
0;
1* zlinesptr/nr-Array initialization: */
for (n = 0, zptr = ed->zlinesptr, pnr a ed->zlinesnr;
n < MAXHEIGHT: n++, zptr++, pnr++)
*zptr = NULL;
*pnr = 1;
The main program now requires two new variables in place of SHORT
1:
464
4.3
ABACUS
STEP BY STEP
ULONG mouseX,mouseY
The main loop also requires some changes. Here is the current listing of
the main loop:
d>
(
1* Set cursor
Cursor();
signal
*f
Wait(IL edUserPort->mp_SigBit);
GetMsg(edUserPort
= imsg->Class;
class
= imsg->Code;
code
qualifier = imsg->Qualifier;
iaddress
= imsg->IAddress;
mouseX
imsg->MouseX;
mouseY
= imsg->MouseY;
ReplyMsg (imsg);
1* Event processing: *1
switch (class)
{
case RAWKEY:
i f (! (code & IECODE_UP_PREFIX
(
inputEvent.ie Code
= code;
inputEvent.ie-Qualifier = qualifier;
if inputLen-= RawKeyConvert (
&inputEvent,inputBuffer,MAXINPUTLEN,NULL)
) >= 0)
handleKeys(inputBuffer,inputLen);
break;
case MOUSEBUTTONS:
{
else
y
= (mouseY
- actualEditor->yoff)
1 actualEditor->ch;
465
if
y < actuaIEditor->wch)
&& (actuaIEditor->zlinesptr[y]
actualEditor->xpos ~ x;
actuaIEditor->wdy = y;
actuaIEditor->ypos = actuaIEditor->zlinesnr[y];
case NEWSIZE:
initWindowSize(actualEditor);
} 1* of case *1
} 1* of while (GetMsg( *1
while (running);
The MOUSEBUTTONS check lets the user place the mouse on the
window by clicking on that screen character, as with most word
processors.
The memory module remains unchanged. However, the output
module must be modified to display fold markers in alternate colors.
First, a printLine needs a new global variable to make sure the last
column is not displayed:
1*********************
* Global
*
Variables:
*********************1
UWORD SplitDec = 0;
466
ABACUS
*zptr++ = (z = nextLine{z,&znr;
*pnr++ = znr;
ed->wch
newh;
actualEditor->leftpos;
tab++;
i f (skip)
skip--;
else
{
1++;
*buf++ =
';
467
tab++;
if (skip)
skip--;
else
{
1++;
buf++;
is replaced by:
SetAPen(rp,fgpen);
if z
{
468
= line->succ)->succ)
4.3
ABACUS
STEP BY STEP
FGPEN;
else
(
SetAPen(actuaIEditor->rp,FOLDPEN);
pen = FOLDPEN;
printAt(buf,w - SplitDec,actuaIEditor->xoff,y,pen);
/* Old write color first again: */
if (pen != FGPEN)
SetAPen(actuaIEditor->rp,FGPEN);
else
printAt(buf,w - SplitDec,actuaIEditor->xoff,y,FGPEN);
Changes must still be made to the test. c module before we can test
the new extensions. The line number displayed by the print function
comes from the zlinesnr array:
void print ()
{
The number of lines should be adapted for the input and delete line
functions, by adding the following for input:
actuaIEditor->num_lines++;
andfor delete_line:
actuaIEditor->num_lines--;
The ini t zlinesptr function must determine the fold level of all
of the lineS:
void init zlinesptr()
= actuaIEditor->zlines.head;
z->succ; z
= z->succ)
469
z->flags
i f (z->len >: 8)
{
fold++i
z->flags 1= ZLF_FSEi
}
fold--;
z->flags 1= ZLF_FSEi
for (n = 0, z = actualEditor->zlines.head,
zptr = actualEditor->zlinesptr,
pnr = actualEditor->zlinesnr, znr = 1;
n < MAXHEIGHTi n++)
*zptr++ = Zi
*pnr++ = znri
z = nextLine(z,&Znr)i
The fold mark test looks rather "wild," but it works faster than the
string compare function from the standard library. This happens because
four characters are constantly compared with one another (ULONG =4
bytes). Currently it works in our test module because we can allow
ourselves some leeway in test programming. Later, if we allow any
other strings as fold marks, we use the strcmp or strncmp
functions. because there is no guarantee that the corresponding character
string is exactly eight characters in length.
A printAll call at the end of the function test ensures that the
window contents are updated. Compile and link this version of the
editor.to test this version of the editor. We will use the Amiga's ability
to redirect input from a file. The optionl diskette contains a Test Text
file to do this. The file contains a linefeed, an a, and another linefeed at
the end of each line. Using redirection, this file can be loaded into our
test version of the editor using the append (a) command. An escape
character and a linefeed are located at the end of the file to let the test
editor know when we are finished appending text. Please see Apendix A
for complete information on altering a standard text file suitable for
redirection into our test editor.
You'll find the TestText source, the editor source text and compiled
editor in the VO. 4 directory of the optional disk.
Mter you have created a test file, enter the following to start the editor
and use the TestText source as redirected input. see the optional
diskette or Appendix A for the TestText listing:
470
4.3
ABACUS
STEP BY STEP
The end of Test Text contains an escape code, which indicates the end
of the source file. You can now examine the editor and some of its
features, including folding.
Debugging
This loads a file named. dbini t that contains only one instruction
(al). This lets you begin a new task while in the debugger. Start the
editor as explained above. The debugger notices that a new task is in
operation. It checks to see if the program contains a symbol table, and
loads the symbol table if it exists.
Mter all loading ends, the debugger displays the frrst instruction:
jrnp
.begin
link
a5,i-2
movea.l actualEditor,aO
tst.w 252 (aD)
beq.s
cursorUp+18
movea.l :actualEditor,aO
471
cursorUp+l2
-cursorUp+l6
-cursorUp+18
-cursorUp+lc
-cursorUp+20
-cursorUp+24
:=cursorUp+28
cursorUp+2a
-cursorUp+2c
-cursorUp+2e
-cursorUp+32
-cursorUp+36
:=cursorUp+38
cursorUp+3a
:=cursorUp+3c
cursorUp+3e
:=cursorUp+40
cursorUp+44
-cursorUp+46
-cursorUp+4a
-cursorUp+4c
-cursorUp+4e
:=cursorUp+S2
_cursorUp+S6
cursorUp+Sc
:=cursorup+Se
subq.w
bra.s
pea
movea.l
move.l
jsr
addq.w
tst.l
beq.s
move.w
jsr
addq. w
bra.s
moveq
unlk
rts
movea.l
moveq
move.w
as!.l
movea.l
adda.l
movea.l
move.w
moveq
bra.s
#l,2S2(aO)
cursorUp+40
::2 (as)
actualEditor,aO
ca(aO),-(a7)
~revLine
#S,a7
dO
_cursorUp+3a
#l,-(a7)
scrollDown
i2, a7
cursorUp+40
iO,do
as
_actualEditor,aO
#0, dO
2S2(aO),dO
#l,dO
dO,al
actualEditor,al
-actualEditor,a6
Ica(al),2S0(a6)
#l,dO
_cursorUp+3c
About the arguments in the above line: The pd (Print Decimal) forces
the debugger to display the contents of the given memory location in
decimal notation). The memory location corresponds to the Y position
of the current editor, 250 bytes after the address to which the current
editor points. The g means that the debugger should restart the editor.
We don't want to stop the editor, but instead want to have the Y
472
4.3
ABACUS
STEP BY STEP
Continue to enter u until you find the UNLK instruction. It will appear
at address _ cursorDown+66. Set the breakpoint as before:
bs cursorDown+66 ;pd 250+*actualEditor;q
This concludes the preparations. Press <g:> (GO) to start the editor.
Everything runs as usual, until you press the <Cursor up> key. Then
the debugger window comes to the foreground and activates. The cursor
does not appear in the debugger window following a question mark.
One catch: The editor is no longer in the foreground, nor is it active.
Reduce the size of the debugger window so that it doesn't block the
editor window but is still large enough to clearly display the Y
position. Enlarge the editor window so that you can see the entire
debugger window. Now reactivate the editor window by either clicking
on the window border or the cursor (clicking anywhere else moves the
cursor to another position).
Every time you press the <Cursor up> or <Cursor down> key, you
must reactivate the editor window (this is tolerable, but not very
elegant). Move the cursor up a couple lines onto folds, and see if the
position displayed is true. Enter the fold and scroll around a little. Once
you have done some scrolling, return to the beginning of the text and
see if this has the line number one. You'll find an error in the
calculation of the Y position.
With some logical thought we can determine how the error occurred.
Everything runs properly, as long as we remain in the window without
accessing any folds. This is fine, since line numbers are stored in the
zlinesnr array. As soon as this array changes, however, an error
occurs. The next Line function (which calculates the line numbers)
initializes the line numbers, returning zero. Two options are available
for seeking errors:
1.)
List and read the source text until you find the error;
2.)
Let's start by searching the source. Load the cursor. c module into
an editor and look at the program locations which affects folding. You
won't see any obvious errors there-we didn't. Since the problem seems
to occur when scrolling, search for the scrollUp and scrollDown
functions. They don't seem to contain any errors, either. Before we
continue testing, look at the prevLine function, which is accessed by
the scrollDown function. Notice that in this function the line
473
4.
AMIGA
4.3.8
Text Input
The editor should be able to handle text entry and editing. Functions
listed in this section will give the editor capabilities of entering,
inserting and deleting characters and lines in a source text. Let's begin
with the "how" of writing these functions.
Insert mode
Overwrite
mode
When the editor is in overwrite mode, any characters entered from the
keyboard are placed in the text. If any characters already exist in the
same line, the new characters replace the old. The cursor moves one
character to the right for each new character.
Control characters playa special role when inserting, particularly CR
(carriage return = 13), LF (linefeed = 10), BS (Backspace = 8), DEL
(Delete = 127), and TAB (9). CR and LF create a new line at the current
cursor position. BS and DEL delete characters. TAB inserts multiple
spaces.
Tabs
Tabs require special attention. Since the editor's main purpose is for
entering program text, it doesn't really matter whether tabs are spaces or
not. The Aztec C compiler treats tabs as blank spaces. Consider the
following:
When we copy a line to the buffer, the tabs change into spaces,
just as we did in the convertLineForPrint function.
When the line transfers from the buffer back into the program
line, the spaces change back into tabs.
474
ABACUS
4.3
STEP BY STE P
multiple spaces (the editor views these as true spaces, not tabs) or userspecified tabs allowed. A flag named actualEditor->tabs solves
this problem. When this flag is set to one, spaces convert to tabs.
When it is set to zero, tabs are treated as normal control characters
Ctrl><i for input and output (i.e., no conversion).
Now we must write the buffer back into the line structure. This occurs
when the cursor moves out of the current line. The function needed
executes when a message is processed within the editor's main loop.
This function writes the buffer contents back to the line if the cursor
exits this line. A variable named bufypos either contains the number
of the line found in the buffer, or contains zero if no lines exist in the
buffer. The variable actual points to the line structure of the line
found in the buffer. In addition, the ZLF USED flag in the line
structure must be set when copying a line In the buffer. This flag
determines whether a line from the output appears in the buffer.
Now we can begin to create the specifics of the new Edit. c module:
void getLineForEdit (line, znr)
register struct Zline *line;
register UWORD
znr;
This function copies the line line to the buffer and sets all of the
corresponding variables. The convertLineForPrint function
performs line conversion. The actualEditor->leftpos must
bave been preset to zero, allowing the conversion of all characters and
the return to original values later. This function requires some
revision-the converted line's length is needed for placing in
actualEditor->buflen, and we must know lastchar (from
actualEditor->buflastchar). For character deletion, the last
character in the buffer must be replaced by lastchar, so that the line
displays correctly on the screen.
BOOL saveLine(line)
struct Zline *line;
Save the line found in the buffer to the line structure, which points to
actualEditor->act ual. Eventually a new line must be used in
case the length of the original line differs from the new line. In case the
old or the changed line represents a fold marker, the levels of all of the
following lines must correspond to the old or changed line. If the line
cannot be rewritten, FALSE returns.
void savelfCursorMoved()
The main loop of the editor calls this function after processing each
message. This function determines whether the cursor has left the actual
input line, and. if so, calls the saveLine function.
The following functions control text processing. Since these are
implemented as individual functions, the implementation of a command
language can be added fairly easily, because individual instructions only
475
When a line is found in the buffer, this removes changes and displays
the original line.
BOOL deleteChar ()
Erases the character under the cursor and moves the rest of the line one
character to the left (enabled by pressing the <Delete> key).
BOOL backspaceCbar ()
Erases the character to the left of the cursor and moves the rest of the
line one character to the left provided insert mode is enabled. If insert
mode is disabled, a space appears to the left of the cursor, and the cursor
moves one character to the left.
BOOL insertCbar(c)
UBYTE c;
Erases the line in which the cursor currently lies. You must be careful
if the cursor lines in a fold at the end of a text. If you try to delete all of
the lines, other folded lines still exist. In this case this function reduces
minfold until the line found appears in the top line of the window.
The function tries to leave the cursor at the current position, scrolling
up the boltom section of the window's contents after the line is deleted.
476
ABACUS
All of the functions are called from the handleKeys function, which
appeared in Section 4.3.7. The source text of our new module looks
like this:
<src/Edit.c;>
/************
**
Includes:
*************/
#include <exec/types.h>
#include <intuition/intuition.h>
#include "sre/Editor.h tl
/***********
**
Defines:
***********/
#define CR 13
#define LF 10
#define TAB 9
**
External Functions:
**********************/
UWORD convertLineForPrint(),strncmp(),recalcTopOfWindow();
void deleteZline(),Insert(),printAII(), restoreZlinenptr();
void DisplayBeep(),printLine(),AddHead(),scroIIRight(),
scrol!Up() ;
void ScroIIRaster();
struct Zline *newZline(),*nextLine(),*prevLine();
BOOL cursorLeft(),cursorRight(),cursorDown(), eursorHome();
1*********************
** External
Variables:
*********************/
* Global variables:
*********************1
UBYTE foldStart[]
UBYTE foldEnde[]
"/*IIFOLD:*/";
"/*IIENDFD*/";
477
4.
AMIGA
C FOR
ADVANCED PROGRAMMERS
1***************
* Functions:
***************1
1********************************
*
* getLineForEdit(line,znr)
*
* Copy line from editor to the
* the buffer.
**
*
line 1\ Zline.
znr : line number of the line.
********************************1
1* get leftpos: *1
leftpos = actualEditor->leftpos;
actualEditor->leftpos = 0;
1* convert line: *1
actualEditor->buflen =
convertLineForPrint(line+l,line->len,MAXWIDTH + 1,
actualEditor->buffer,&actualEditor->buflastchar);
1* Zline as "used"
: *1
line->flags 1= ZLF USED;
actualEditor->actual = line;
actualEditor->bufypos : znr;
1* establish Leftpos: *1
actualEditor->leftpos = leftpos;
1*******************************
*
*
cursorOnText:
*
*
*
*
*******************************1
void cursorOnText()
{
zptr--i
478
4.3
ABACUS
STEP BY ST E P
1--;
}
actua1Editor->wdy = 1;
actua1Editor->ypos = ZLINESNR(l);
1*************************
**
deconvertLine():
* Deconver line.
**
*
**************************1
UWORD deconvertLine(buf, buffer,buf1en)
UBYTE
*buf, *buffer;
UWORD
buf1en;
{
1* deconvert line: *1
pI = buffer;
p2 = buf;
tab= actua1Editor->tabstring;
1
= buflen;
if (actualEditor->tabs)
1* Spaces converted to tabs: *1
while (1)
{
i f *p2 = *pl++)
")
i f (fb =
fb =
NULL)
p2;
else
i f (fb)
fb = NULL;
*1
*fb = TAB;
fb++;
p2 = fb;
else
p2++;
1--;
else
while (1)
{
479
*p2 = *pl;
pl++;
p2++;
1--;
C (UWORD)
.)
II (* (p2-l)
(p2 - buf);
1****************************************
* getFoldInc(line)
*
* line
result line.
****************************************1
getFoldInc(line)
struct Zline
*line;
UWORD
pl++;
1-;
1 = Start,
-1
End and
0 no fold marker.
*/
*1
*/
**
480
saveLine(line)
0)
0)
=0
TAB)
4.3
ABACUS
STEP BY S T E P
* Save line.
* line
old line-Structure.
*******************************1
BOOL saveLine(line)
struct Zline *line;
{
IE'
==
/* +2 for CR and
= NULL;
NULL)
/* deconvert line: */
p2 = buf + (len = deconvertLine(buf,actualEditor->buffer,
actualEditor->buflen));
/* firm place, or line ends with CR or LF, */
/* and increases len correspondingly. *1
p2 = buf + len;
if (1 = line->len)
{
pI = (UBYTE *) (line + 1
i f (*pl == CR)
+ line->len - 1;
len++;
*p2 = CR;
}
else if (*pl
==
LF)
len++;
if 1 > 1) && (*--pl
==
CR)
len++;
*p2++ = CR;
*p2
LF;
old = line;
if (line = newZline(len
{
Insert (&actualEditor->zlines,line,old);
line->f1ags = old->flags & -ZLF USED;
deleteZline (old) ;
/* now possible to convert pointer: */
for (1 = 0, zptr = actualEditor->zlinesptr;
1 <= actualEditor->wch; 1++, zptr++)
if (*zptr == old)
{
481
4.
AMIGA
*zptr = line;
break;
else
return (FALSE);
else
{
line->len
= len;
line->flags &= -ZLF_USED;
1* write line : *1
pI = (UBYTE *) (line + 1);
p2 = buf;
1
= len;
while
(1)
*pl = *p2;
pl++;
p2++;
1-;
1* reconstruct folding: *1
1* Was old or is new line on fold mark? *1
= getFoldlnc(line);
if line->flags & ZLF FSE) I I 1)
(1)
1:
*1
line->flags 1= ZLF_FSE;
else
line->flags &= -ZLF_FSE;
(old->flags &
old->flags+l) &
i f (1)
{
old->flags
old = old->succ;
restoreZlinenptr();
printAll () ;
482
4.3
AB.4.CUS
STEP BY STEP
cursorOnText () ;
1* line output: *1
1* First calculate Ypos:
*1
for (1 = 0, zptr = actualEditor->zlinesptr;
1 <z actualEditor->wch; 1++, zptr++)
if (*zptr == line)
{
printLine(line,actualEditor->yoff + actualEditor->ch*l);
break;
1* Delete reference: *1
actualEditor->actual = NULL;
actualEditor->buflen
= 0;
actualEditor->bufypos = 0;
1* Text was changed! *1
actualEditor->changed = 1;
return (TRUE);
1***************************************
**
savelfCursorMoved:
**
****************************************1
void savelfCursorMoved()
{
**
undoLine:
*
*
*******************************************1
void undoLine ()
{
actualEditor->actual = NULL;
actualEditor->bufypos = 0;
483
4.
AMIGA
actualEditor->buflen : 0;
z->flags &=
~ZLF_USED;
1* line output: *1
printLine(z,actualEditor->yoff
+ actualEditor->ch*actualEditor->wdy):
1***************************************************
* getBufferPointer(pptr,pinc,prest):
**
***************************************************1
/* first line: *1
register struct Zline *z;
if (z
= newZlineUWORD)
* UBYTE *) (z + 1 = LF;
AddHead(&actualEditor->zlines,z);
ZLlNESPTR(O) = z;
ZLINESNR(O) = (actualEditor->num lines
1);
getLineForEdit(ZLINESPTR(O),ZLINESNR(O:
}
else
return (FALSE);
else
484
4.3
ABACUS
STEP BY STEP
inc = actuaIEditor->xpos - 1;
ptr = actuaIEditor->buffer + inc;
rest= actuaIEditor->buflen - inc;
if (rest < 0)
{
actuaIEditor->buflen -= rest;
while (rest)
{
*p =
p++;
I;
rest++;
else
return (FALSE);
*pptr = ptr;
*pinc = inc;
*prest= rest;
return (TRUE);
1************************************
**
deleteChar:
*************************************1
BOOL deleteChar()
{
UBYTE *ptr;
UWORD inc,rest;
register UBYTE *pl,*p2;
register UWORD 1;
if (! getBufferPointer(&ptr,&inc,&rest))
return (FALSE);
i f (rest)
{
pI = ptr + 1;
p2 = ptr;
1
= --rest;
while (1)
{
*p2 *p1;
p1++;
p2++;
1-;
485
4.
*p2
AMIGA
= actua1Editor->buflastchar;
1* Lenght - 1 *1
actua1Editor->buflen--;
printLine (actua1Editor->actua1, actua1Editor->yoff
+ actua1Editor->ch*actua1Editor->wdy);
return (TRUE);
else
return (FALSE);
/******************************
**
backspaceChar:
*******************************1
BOOL backspaceChar()
{
UBYTE *ptr;
UWORD inc, rest;
register UBYTE *pl,*p2;
register UWORD 1;
if (! getBufferPointer(&ptr,&inc,&rest))
return (FALSE);
i f (inc)
{
if (actua1Editor->insert)
{
= ptr;
pl
p2
--ptr;
rest;
while (1)
=
1
{
*p2
*pl;
pl++;
p2++;
1--;
*p2
= actua1Editor->buf1astchar;
1* Length - 1 *1
actua1Editor->buflen--;
inc--;
else
{
*--ptr =
inc--;
rest++;
cursorLeft () ;
486
';
4.3
ABACUS
STEP BY STEP
printLine(actualEditor->actual,actualEditor->yoff
+ actualEditor->ch*actualEditor->wdy);
return (TRUE);
}
else
return (FALSE);
1******************************************
* insertChar (c)
**
**
c= character.
******************************************1
UBYTE *ptr;
WORD inc, rest;
WORD xadd = 1;
if (! getBufferPointer(&ptr,&inc,&rest
return (FALSE);
if c
==
if (actualEditor->insert)
{
1* Insert spaces: *1
register UBYTE *p1,*p2;
register UWORD num,l;
p1
= actualEditor->tabstring + actualEditor->xpos - 1;
= MAXWIDTH
num = 1;
- actualEditor->xpos;
xadd++;
1-;
num++;
ptr + rest;
pl + num;
1
= rest;
while (1)
=
=
*--p2 = *--pl;
1-;
487
4.
AMIGA
C FOR
ADVANCED PROGRAMMERS
1* Insert spaces: *1
pI = ptr;
1
= num;
while (1)
{
*pl = ':
pl++;
1-;
actualEditor->buflen += num;
rest += num;
}
else
{
xadd++;
max-i
}
else
{
ptr + rest;
pI:
1
rest;
while (1)
{
=
=
*p2 = *--pl;
p2--;
1-:
*ptr
c:
1* Length + 1 *1
actualEditor->buflen++:
rest++:
else
*ptr '" c;
i f (rest = 0)
{
actualEditor->buflen++;
488
4.3
ABACUS
rest
STEP BY STEP
1;
/* move cursor: */
while (xadd)
{
if (! cursorRight()) break;
xadd--;
/* output line: */
print Line (actualEditor->actual,
actualEditor->yoff + actualEditor->ch*actualEditor->wdy);
return (TRUE);
/**************************
**
insertLine(c):
*
*
*
*
* c
**************************/
BOOL insertLine(c)
UBYTE
c;
{
pi
actualEditor->buffer;
1 = inc;
while
(l
TAB) ))
p1++;
489
1-;
i f (1)
autoindent : pI - actualEditor->buffer + 1;
else
1* insert spaces in line: *1
autoindent : I:
else
autoindent : I:
1* deconvert line: *1
p2 : buf + (len = deconvertLine(buf,actualEditor>buffer, inc) );
*p2= c;
len++;
if (z = newZline(len
{
(1)
*pl = *p2;
p1++;
p2++;
1--;
1* line connect: *1
Insert (&actualEditor->zlines,z,actualEditor->actuaI>pred) ;
actualEditor->num lines++;
actualEditor->changed : 1;
1* write rest of buffer line *1
1* remember auto indent! *1
pI : actualEditor->buffer:
1
: autoindent:
while (--1)
pl++;
1* Old contents remain! *1
p2 : ptr;
1 = rest;
while (1)
{
*pl : *p2;
p1++;
p2++;
1-;
}
actuaIEditor->buflen : pI - actuaIEditor->buffer;
490
ABACUS
4.3
STEP BY STEP
*pl = actualEditor->buflastchar;
p1++;
1--;
1* redisplay window: *1
actualEditor->xpos = autoindent;
if (actualEditor->xpos <= actualEditor->leftpos)
{
it
(
ZLlNESPTR(O) = ZLINESPTR(l);
ZLINESNR(O) = ZLINESNR(l);
restoreZlinenptr();
actualEditor->wdy--;
actuaIEditor->ypos = ZLlNESNR(actualEditor->wdy);
printAll () ;
}
else
i f (l)
491
4.
AMIGA
C FOR
ADVANCED PROGRAMMERS
ZLINESPTR(O) = ZLINESPTR(1);
ZLINESNR(O) = ZLINESNR(1);
restoreZlinenptr();
actuaIEditor->wdy--;
actuaIEditor->ypos = ZLINESNR(actuaIEditor->wdy);
printAlI () ;
}
else
(
1* it was scrolled: *1
if (++actuaIEditor->wdy >= actuaIEditor->wch)
(
else
(
actuaIEditor->ypos = ZLINESNR(actuaIEditor->wdy);
printLine(ZLINESPTR(nr),actuaIEditor->yoff
+ actuaIEditor->ch*nr);
nr--;
printLine(ZLINESPTR(nr),actuaIEditor->yoff
+ actuaIEditor->ch*nr);
return (TRUE);
}
else
return (FALSE);
}
else
(
cursorHome 0;
cursorDown 0 ;
492
4.3
ABACUS
STEP BY STEP
return (FALSE);
1****************************
**
**
deleteLine:
delete line
* that the cusor is on.
* FALSE returned if
* it cannot be deleted
*****************************1
BOOL deleteLine()
{
ZLINESPTR(actualEditor->wdy))
actualEditor->actual = NULL;
actualEditor->bufypos = 0;
actualEditor->buflen = 0;
actualEditor->changed = 1;
actualEditor->num_lines--;
1* Reconstruct folding: *1
if (line->flags & ZLF FSE)
{
register struct Zline *z;
1* Fold level for all following lines: *1
i f z = line->su=) ->succ)
(
(z->flags
&
z->flags+l) ,
1 = 0;
if (1)
while (z->succ)
{
493
4.
1* Next
prevnr=
prev =
nextnr=
next =
AMIGA
1* Security check: *1
while prev = NULL) && (next
>minfold) )
1 = 1;
1* Window completely output *1
actualEditor->minfold--;
prevnr=
prev =
nextnr=
next =
ZLINESNR(actualEditor->wdy);
prevLine (line, &prevnr) ;
ZLINESNR(actuaIEditor->wdy);
nextLine (line, &nextnr) ;
if (actualEditor->minfold) continue;
if next = actualEditor~>zlines.head)->succ
next = NULL;
else
nextnr = 1;
NULL)
break;
1* delete line *1
deleteZline(line);
1* pointer to actual line set for recalc: *1
i f (next)
{
ZLlNESPTR(actualEditor->wdy)
ZLINESNR(actuaIEditor->wdy)
= next;
= nextnr;
else
{
ZLlNESPTR(actualEditor->wdy) = prev;
ZLINESNR(actualEditor->wdy) = prevnr;
actualEditor->wdy = recalcTopOfWindow(actualEditor->wdy);
restoreZlinenptr();
printAllO;
cursorOnText 0 ;
}
else
{
1* with scrolling: *1
register UWORD oldwdy;
oldwdy = actualEditor->wdy;
actualEditor->wdy = recalcTopOfWindow(actualEditor->wdy);
494
4.3
ABACUS
STEP BY STEP
restoreZlinenptr();
if (next)
{
else
i f (oldwdy -- actualEditor->wdy)
{
else
printLine(ZLlNESPTR(oldwdy),actualEditor->yoff
+ actualEditor->ch*oldwdy);
cursorOnText () ;
return (TRUE);
}
else
return (FALSE);
}
The character strings which represent the fold marks are defined
the fold mark. The user must remember to add the closing
comment mark after the fold marks.
exist after this, fold levels of all of the following lines must be
decremented.
These increment and decrement rules also apply to the fold end mark. If
a line is a fold mark before as well as after a change, things can be
complicated. However, creating a table of values, which returns the
value by which all successive folds must be increased or decreased,
makes things much simpler. This value depends on the status of a line
(normal line or fold mark). The process works like this: The
getFoldInc function handles a line changed to a fold mark and notes
the return value. A fold mark changed to a normal line forces a
496
4.3
ABACUS
STEP BY STEP
comparison between the fold level of the current line and the fold level
of the next line. In this case we get the negative value relative to
getFoldInc (-I for the start and +1 for the end). Now we add the two
values, and receive the value that must be added to the fold level of all
subsequent lines.
So much for folding. The changed flag from the Editor structure
detennines whether something in the text has been changed. If the user
changes a line with saveLine, the changed flag must be seL
The line is displayed at the end of the function.
Text output in the window is another matter. If you insert a new line,
the window contents are moved out of order. It's possible to use the
printAll function, but that would be too slow. Besides, we want the
editor to allow scrolling. The insertLine function should contain
the following:
When the window contents must be scrolled horizontally, or when the
newly insert line is a fold marker, printAll displays the window
contents. This would not be necessary with horizontal scrolling, but if
we scroll the window contents horizontally and then vertically,
printAll is not much slower. Otherwise it scrolls vertically and the
section of text at the cursor position scrolls down. When the cursor is
on the bottom line, the entire contents of the window scroll up.
497
4.
buf1astchar;
buf1en,bufypos;
tabs
: 1;
skipb1anks : 1;
autoindent : 1;
Now the rest of the line fills with lastchar and the function returns
the length:
*lc .. 1astchar;
return (realLen);
4.3
ABACUS
STEP BY STEP
void printLine
(line,y)
register struct Zline *line;
register UWORD
y;
{
w = MAXWIDTH - aetualEditor->leftpos;
p2 = aetualEditor->buffer + aetualEditor->leftpos;
pl = buf;
while (w)
{
*pl = *p2;
p1++;
p2++;
W--i
else
pl = buf;
/* fill rest with lastchar: */
p2 = buf + (w = actualEditor->wcw);
Ie = actualEditor->buflastehar;
while (pl < p2)
{
*pl = Ie;
p1++;
else
eonvertLineForPrint(line+l,line->len,
w = aetualEditor->wcw,buf,&le);
/* If start/end marker of fold => FOLDPEN */
if (line->flags & ZLF FSE)
{
i f z = line->succ) ->suee)
{
else
pen = FGPEN;
else
499
SetAPen(actualEditor->rp,FOLDPEN);
pen = FOLDPEN;
.}
printAt(buf,w - SplitDec,actuaIEditor->xoff,y,pen);
else
printAt(buf,w - SplitDec,actuaIEditor->xoff,y,FGPEN);
}
else
{
= actuaIEditor->rp;
SetAPen(rp,BGPEN);
RectFill(rp,(ULONG)actualEditor->xoff,
(ULONG)y,
(ULONG)actualEditor->xscr,
(ULONG)y + actuaIEditor->ch - 1);
SetAPen (rp, FGPEN) ;
}
lIP
.. z;
znrp = znr;
if z = prevLine(z,&znr
break;
y-;
if (y)
{
ZLlNESPTR(O) .. zp;
ZLINESNR(O) .. znrp;
}
else
{
500
;: NULL)
4.3
ABACUS
ZLINESPTR(O)
ZLINESNR(O)
STEP BY STEP
z;
= znr;
This function keeps the cursor in the current line when encountering a
fold and when deleting a line. The new line position is returned from
this function, which usually matches the old line position. Now when
encountering a fold, the cursor can be relatively far down in the
window, but not very much text is visible. Then the cursor must be
moved up where the new position accesses this function.
The program segment for fold handling is placed before the
handleKeys function:
1***************************
**
enterFold:
***************************1
void enterFold ()
(
actuaIEditor->minfold = actuaIEditor->maxfold;
ZLINESPTR (0) = z;
ZLINESNR(O)
= ZLINESNR(actuaIEditor->wdy) + 1;
actualEditor->wdy = 0;
actualEditor->wdy = recalcTopOfWindow(actuaIEditor->wdy);
restoreZlinenptr();
printAll () ;
actualEditor->ypos = ZLlNESNR(actualEditor->wdy);
1*****************************
* exitFold:
** Step
*****************************1
501
void exitFoldO
{
actualEditor->maxfold--;
if (actualEditor->minfold > actualEditor->maxfold)
aqtualEditor->minfold = actualEditor->maxfold;
wdy = actualEditor->wdy;
if (ZLINESPTR(wdy) == NULL)
actualEditor->wdy = wdy = 0;
if (ZLlNESPTR(wdy
if (ZLINESPTR(wdy)->flags & ZLF FOLD) > actualEditor>maxfold)
II ZLINESPTR(wdy)->flags & ZLF FOLD) < actualEditor>minfold
ZLlNESPTR(wdy) =
prevLine(ZLlNESPTR(wdy),&(ZLlNESNR(wdy);
actualEditor->wdy = recalcTopOfWindow(wdy);
restoreZlinenptr();
printAllO;
cursorOnText 0 ;
actualEditor->xpos = 1;
if (actualEditor->leftpos)
scrollRight(actualEditor->leftpos);
return (TRUE);
502
CROME 'A'
UNDO '?'
CFOLD 6
CENDF 5
TABMODE 20
WRITEMODE 15
AUTOINDENT 1
DELLINE 2
DEL 127
BS 8
LF 10
CR 13
4.3
ABACUS
STEP BY STEP
first = *buf;
buf++;
len--;
if first == CSI) && (len> 0
(
len--;
switch (*buf++)
{
case CUU:
cursorUpO;
break;
case CUD:
cursorDown 0 ;
break;
case CUF:
cursorRight 0 ;
break;
case CUB:
cursorLeft 0 ;
break;
case SU:
halfPageDown () ;
break;
case SD:
halfPageUp () ;
break;
case' ':
i f (len> 0)
{
len-;
swi tch (*buf++)
(
case CHOME:
cursorHome () :
break;
default:
if (! insertCharUBYTE)CSI
DisplayBeep(NULL):
buf -= 2;
len += 2;
break;
else
goto noCSI:
case UNDO:
if len> 0) && (*buf
{
==
'-'
buf++;
len--;
503
undoLine():
break;
}
1* Else: default *1
default:
noeS!:
1* No command sequence! *1
if (! insertCharUBYTE)CSI
DisplayBeep(NULL);
OOf--:
len++:
break:
1* switch (first) *1
}
else
switch (first,
{
case CFOLD:
enterFold () :
break;
case CENDF:
exi tFold () :
break:
case TABMODE:
actuaIEditor->tabs = !actuaIEditor->tabs:
printAlI () :
break;
case WRITEMODE:
actuaIEditor->insert = !actuaIEditor->insert;
break;
case AUTOINDENT:
actuaIEditor->autoindent= !actuaIEditor->autoindent;
break:
case DELLINE:
if (! deleteLine(
DisplayBeep(NULL);
break:
case DEL:
if (! deleteChar(
DisplayBeep(NULL);
break:
case BS:
i f (! backspaceChar () )
DisplayBeep(NULL);
break;
case LF:
case CR:
if (! insertLine(first
DisplayBeep(NULL);
break;
default:
if (! insertChar(first
DisplayBeep(NULL);
1* switch (first) *1
1* while (len) *1
}
504
ABACUS
4.3
STEP BY STEP
length, some are two and three characters long. This instruction
provides another swi tch case instruction which processes all of the
sequences that begin with CSI ' '. This is currently only CSI ' , 'A',
which is sent through <Shift><Cursor Left>. This could be expanded
later to include user defined sequences.
The second switch case instruction handles control characters that
call their own functions at that moment Later we will access a section
of the command line.
Let's look at an overview of editor commands available so far:
Keys
Cursor keys
Shift Cursor up/down
Shift Cursor left
Help
CtrIA
CtrlB
Ctrl E
CtrlF
CtrIO
Ctrl T
Delete
Backspace
Comments
moves the cursor by one character/line
scrolls text a half page up or down
sets the cursor at the beginning of the line
undo
(delete these)*1
if (actualEditor->actual == line)
actualEditor->actual = NULL;
for (n = 0, zptr = actualEditor->zlinesptr;
n <= actualEditor->wch; n++, zptr++)
if (*zptr == line)
{
50S
*zptr = NULL;
break;
A few changes still need to be made to the main Edi tor. c module.
The additional elements of the Edi tor structure must be initialized in
the OpenEdi tor function:
ed->buflen
= 0;
ed->bufypos
= 0;
ed->buflastchar= , ';
ed->tabs
= 1;
ed->skipblanks = 1;
ed->autoindent = I:
It may surprise you when testing the cursor that the cursor runs on
(keeps going after you release the cursor key) horizontally. This
happens with almost all the functions that move the cursor or insert
text, and when you set the keyboard repeat to very high speed from
Preferences.
This effect can be prevented by checking to see if something has
changed after the program processes a keypress. If this is the case, and if
all of the RAWKEY messages have set the REPEAT flag, the first
message is removed. The REPEAT flag is set from the operating
system when a RAWKEY message is encountered instead of a keyboard
repeat, because the user has pressed the key. Messages that do not set
the REPEAT flag are not deleted because the user may sometimes press
keys faster than the editor can process them.
The current messages are deleted until only one remains. This is
processed during the next execution of the main loop. In principle we
could also delete the first message, but then the cursor movement would
be uneven. The fact that the cursor moves one character farther than you
may want it to move is the price paid for the fast and smooth cursor
movement.
The RAWKEY check looks like the following:
case RAWKEY:
{
inputEvent.ie Code
= code;
inputEvent.ie-Qualifier = qualifier;
if inputLen-= RawKeyConvert(
&inputEvent,inputBuffer,MAXINPUTLEN,NULL)
) >= 0)
handleKeys (inputBuffer, inputLen);
/* prevent running: */
iml = (struct IntuiMessage *)
edUserPort->mp_MsgList.lh_Head;
506
4.3
ABACUS
STEP BY S T E P
1* Message removed: *1
lml = GetMsg(edUserPort);
ReplyMsg (lml);
lml
(struct IntulMessage *)
edUserPort->mp_MsgList.lh_Head;
break;
To compile this version, look for the name of the object file edi t . 0
in the makefile, and add the following line to the end of the
makefile:
sre/Edit.o: sre/Edlt.e sre/Editor.h /pre/Editor.pre
The optional diskette for the book contains the source text in the v 0 . 5
directory. After you compile the new program you have quite a bit to do
because there are many options to test. Start the program using the
Test Text file as described in the last section. An error that we found
in the program during development has been left in the program for
you. You should be able to find it for it yourself.
Debugging
Have you found it? If you want to add characters to the end of a line in
the overwrite mode, the editor does not enter the new characters when
you exit the line again. We looked over the source text and couldn't find
an error anywhere. This is where the debugger helps us. Compile the
editor using make debug. Start the debugger with db and then the
editor, as we have already described (your drive setup may differ):
sys3:bin/db
Editor <TestText
The debugger displays the editor as usual. Now think about how to
proceed further. The error occurs when we want to add a character at the
end of the line using the insertChar function. Set a breakpoint at
this function:
507
4.
AMIGA
bs insert Char
and start the editor by pressing <g> (Go). Activate the editor window
and press <Ctrl><O> to enter overwrite mode. Search for the end of a
line, or for a blank line. Move the cursor there and press a key (e.g.,
<8. The editor stops and the debugger displays the beginning of the
insertChar function. Now you need to find the error! The reason
could be that the length of the buffer was not increased correctly when
you want to add a character. It would be best to move through this
function in single step mode.
First the LINK and MOVEM instructions perform general initialization.
Then register D4 receives the inserted character and treats the character
as a register variable. The local variable -a (A%) is filled with the
value one, which it handles as xadd. The getBufferPointer
function call occurs. Press <t> to bypass this function, not <s>.
Pressing <t> executes a command completely and executes a subroutine
of BSR or JSR with a subroutine call, before program control returns
to the debugger.
Now we look at the values written in the p t r. inc and res t
variables:
p2dx a5-8
-6 (as)
-4 (as)
To display this we must display two words at address -8 (as) and then
a long word, which the debugger calls p2dX. In addition, two decimal
and one hexadecimal long word must appear from the start address. The
values for inc and rest are both zero if the cursor moves to an empty
line. The value of ptr is not of interest, but look where ptr points:
db *(a5-4)
The line in the debugger window consists of an address, and equal sign.
and a 20. ptr points to a string that is made up only of spaces
( $ 20 ) . Let the debugger continue in single step mode until everything
is correct. It follows the testing of the return values and then checks to
see if D4 contains the value 9. D4 is the inserted character. and 9
508
4.3
ABACUS
STEP BY S T E P
movea.l
move.b
c1r.w
beq.s
movea.l
addq.w
move.w
-4(aS),aO
d4, (aO)
-8 (as)
insertChar+1ge
-actualEditor,aO
#1,76(aO)
#1,-8(aS)
actualEditor->buflen++;
rest = 1;
0);
Local
variables
register int
register int
register int
for (al = a,
a,b,c;
al,bl,cl;
a2,b2,c2;
bl ~ b, cl
c; al > 0; al--)
c; a2 > 0; a2--)
for (a2 = a, b2
b, c2
fast ()
{
c; a2 > 0; a2--)
These serve the same purpose. The difference is in the use and
placement of register variables. Compile each to get the following
assembler source texts:
; slowO
; {
public slow
slow:
link as,#.2
movem.l .3,-(sp)
register int a,b,c;
register int al,bl,cl;
register int a2,b2,c2;
;
; for (al = a, bl
-)
move.w d4,d7
move.w dS,a2
move.w d6,a3
bra .7
.6
.4
sub.w #1,d7
.7
.s
510
tst.w d7
bgt .6
= b,
cl = c; al > 0; al-
ABACUS
4.3
; for (a2
= a,
b2
STEP BY STEP
b, c2 = c; a2 > 0; a2--
move.w d4,-2(aS)
move.w dS,-4(aS)
move.w d6,-6(a5)
bra .11
.10
;
.8
sub.w U,-2(a5)
.11
tst.w -2(a5)
bgt .10
.9
;
;}
.12
.2
.3
movem.l (sp)+,.3
unlk as
rts
equ-6
reg d4/d5/d6/d7/a2/a3
; fast ()
;{
public fast
fast:
link as, 13
movem.l .14,-(sp)
; register Int a,b,c;
;
; {
= a,
for (a1
bl
= b,
c1
= c;
a1 > 0; a1-
= c;
a2 > 0; a2--
-)
move.w d4,d7
move.w dS,a2
move.w d6,a3
bra .18
.17
;
.15
sub.w U,d7
.18
tst.w d7
bgt .17
.16
; {
"
for (a2
= a,
b2
= b,
c2
move.w d4,d7
move.w d5,a2
move.w d6,a3
bra .22
.21
;
.19
511
sub.w #l,d7
.22
tst.w d7
bgt .21
.20
;
;}
.23
movem.l (sp) +, .14
unlk as
rts
.13 equ 0
.14 reg d4/dS/d6/d7/a2/a3
dseg
end
4.3.9
Command Line
File access must still be created for our editor to be complete. Since
filenames must be entered, a command line should be included in the
editor, like the ED editor program. In addition, a status line should
exist, which displays the current cursor position and other information.
On to the command line. This should serve a purpose similar to the one
in ED: The command line should be accessible by pressing the <Esc>
key, and should accept two-letter commands. ED'S command set isn't
confIgured in an easy to remember order. For example, look at ED'S
block commands: BS marks the start of a block, but you must enter m
to insert a block, instead of BI.
Our own editor should have commands which have a first letter
indicating the general command grouping. Let's consider which
command groups would be needed by an editor. First priority would be
512
ABACUS
4.3
STEP BY STEP
loading and saving data. We also need commands for cursor control, text
block manipulation, setting mode flags, text deletion, word searching
and exiting the program. Also, commands may be needed if you choose
to extend the editor to a programmable mode. Here are a few commands
we came up with by brainstorming (the ones marked with an asterisk
are implemented in our version of the editor):
ASCII file
commands
AF ["Filename"]
AL ["Filename"]
AS ["Filename"]
Block
commands
BC (Block copy)
BD (Block delete)
BE (Block end)
BF (Block fold)
BH (Block hide)
BM (Block move)
BP (Block paste)
BS (Block start)
BX (Block cut)
Cursor
commands
CB (Cursor bottom)
CD (Cursor down)
CE (Cursor end)
CH (Cursor home)
CL (Cursor left)
CN (Cursor next)
CP (Cursor previous)
CR (Cursor right)
CS (Cursor start)
CT (Cursor top)
CU (Cursor up)
Delete
commands
DB (delete back)
DC (delete char)
DL (delete line)
backspace
delete character
delete line
Exchange
commands
Find
commands
FN ["String"]
FP ["String"]
IF command
IF(condition)assigment
*
*
*
*
*
513
4.
Line
commands
LA
AMIGA
"String"
LI "String"
LJ (line join)
LS (line. split)
M [A-Z] [=instructions]
Macro
command
Quit command Q
Repeat
RI? instruction
command
Set commands Sa
SA
Sb
SB
Si
So
St
ST
eXit command x
Undo
command
exits editor
*
*
*
*
*
*
*
*
*
The editor commands, with the exception of the Set commands, accept
commands entered in either uppercase or lowercase.
This section lists the development process of the most important editor
features, such as saving and loading text. Some of the features already
exist from the previous versions (e.g., cursor movement).
The first item needed is a command line that works independently from
the normal editing functions. A string gadget placed at the lower left
border of the window serves this purpose. Use of a string gadget means
that you can enter text in the gadget immediately, without clicking on
it to activate it. The string gadget becomes active the moment the user
presses the <Ese> key. The main difference between our command line
and ED'S <Esc> commands is that you can edit the text in the string
gadget.
The editor must execute the commands of the command line as soon as
the user presses the <Return> key. The RELVERIFY flag indicates a
pressed <Return> key to the editor. The editor then receives a
514
ABACUS
4.3
STEP BY STEP
charaCtel'.
The cursor then returns to the original line. Two items of interest in
this command:
Info line
1.)
2.)
The info line appears along the bottom border of the window, to the
right of the command line. This line actually contains a minimum
amount of information, to keep the info line small and maximize the
size of the text area:
515
4.
AMIGA
If we limit the maximum width for each number to four digits (two
digits for minfold or maxfold), the maximum is 34 characters. The
info line looks something like this:
X=nnnn Y=nnnn #=nnnn [nn,nnj ICATB
A:
T:
B:
insert I O:Overwrite
changed
autoindent.
tabs.
skipblanks.
516
ABACUS
4.3
STEP BY STEP
Ascii), you must include the following line at the beginning of the
routine:
global _cn_utoa
_cn_utoa:
We cannot use all of the free registers in our assembler program. The
registers that the C compiler uses for reference to local (AS) and global
(A4) variables may be changed, like the register that uses register
variables (D4-D7, A2, A3). However, there are more than enough
registers for us, especially since we only need four segments for our
subroutine. Here is the complete assembler program, which will be
located in Output. c:
1******************************
*
*
cn_utoa(buf,val,len):
Convert UWORD in ASCII.
buf
val
len
= Value
= Maximum
Lenght.
******************************/
517
cn utoa:
; 4(sp)
; 8(sp)
;10(sp)
lea
move. I
moveq
move.w
move.w
lea
bra.s
4(sp),aO
(aO)+,a1
#O,dO
(aO)+,dO
; = Value
(aO),dl
; = maximum Lenght
0(a1,dl.w),aO ; ~ End of Buffers
.in
.lp:
divu
swap
add.b
move.b
clr.w
swap
Buffer,
Value,
= maximum Lenght.
#10, dO
dO
#'O',dO; dO.w
dO,-(aO)
dO
dO
= VAlue
MOD 10
.in:
dbeq
dl, .lp
; length is value = 0
; or String full
beq.s
. i b ; Value = 0 => Rest is
; full of spaces
bra.s
.r ; String full!
.bl:
move.b
#' ',-(aO)
.ib:
dbra
.r:
dl, .bl
rts
#endasm
The program fHIs in the string starting from the end. The number to be
converted divides by ten each time, and the remainder from the division
is written in the string as numerals. The rest of the string fills with
blanks, as long as memory is available.
The values must be displayed properly in the info line. We need the
position at which this display must begin. Add to that the position of
the gadget and the position of the Int ui Text structure (which the
info line contains). The Intuitext structure determines the character
set which keeps the info line as small as possible We recommend
Topaz_80, which permits 80 characters per line. We must also
choose this character set for the output of the values. The
P r i n t I Te x t function allows character set assignment in the
Intui Text structure. This function also requires a null byte at the
end of the string. It would be better to use the text function from the
graphics library, because you can enter the length of the character
string. Consider that the value must be copied to the info line string,
because the border is also redisplayed when the window is redisplayed. If
we display the new value over the old value using PrintIText or
518
ABACUS
4.3
STEP BY STEP
Text in the window, the new output appears the same as the old
because it is always found in the string.
We want to copy or convert the new value (xpos, ypos, etc.) to the
info line, then use Text to display the corresponding section of the
info line. For this we must first set the character set with SetFont.
Then OpenFont must open the character set for access by the main
program. The value display functions look like the following (the
assembler program _cn_utoa should precede these functions in the
Output. c module!):
I*#FOLD: printXpos *1
1*****************************
** printXpos:
** Give Xpos of cursor.
******************************/
void printXpos ()
{
I*#ENDFD*I
I*#FOLD: printYpos *1
1*****************************
* printYpos:
*****************************/
void printYpos ()
{
/*#ENDFD*/
519
4.
AMIGA
C FOR
ADVANCED PROGRAMMERS
/*#FOLD: printNumLines */
/*****************************
* printNumLines:
*
*
*
*
*****************************1
void printNumLines()
{
l*itENDFD*1
l*itFOLD: printFold */
1********************************
* print Fold:
**
********************************1
void printFold ()
{
/*#ENDFD*I
/*#FOLD: printFlags *1
1*****************************
**
*
printFlags:
* At Ypos of cursor.
520
4.3
ABACUS
STEP BY ST E P
*****************************1
void printFlags()
{
I*#ENDFD*I
I*#FOLD: print Info *1
1****************************
**
print Info:
* Gives new
Infoline.
****************************1
void printlnfo()
{
printXpos () ;
printYpos () ;
printNumLines();
printFold () ;
printFlags () ;
}
I*#ENDFD*I
All of these functions operate in the same manner: First the new value
is copied into the string, the character set is changed and the changed
section of the info line is redisplayed. The normal character set returns.
The defines compute the positions of the individual values, and
521
We also need a pointer to the character set used for the info line, and
which is defined and enabled in the main module:
extern struct TextFont *infoFont;
The positions of each value in the info line must be calculated in the
ini tWindowSi ze function. This happens before restoration of the
linesptr array:
/* Position for Infoline: */
register ULONG xinc,xsize;
xinc = w->Width + ed->G Info.LeftEdge + ed->gi IText.LeftEdge
- 1;
xsize= infoFont->tf_XSize;
ed->ip xpos = xinc + INFO XPOS INC * xsize:
ed->ip~os = xinc + INFO=YPOS=INC * xsize;
ed->ip nurnzlines = xinc + INFO NUMOFLINES INC * xsize;
ed->ip-rninfold = xinc + INFO MINFOLD INC
xsize;
ed->ip-maxfold = xinc + INFO-MAXFOLD-INC * xsize;
ed->ip-flags = xinc + INFO FLAGS INC * xsize;
ed->ip-topedge = w->Height
ed->G Info.TopEdge
+ ed->gi IText.TopEdge
+ (ULONGlinfoFont->tf_Baseline - 1;
Module: Command
*
* Command-Interpreter for
Editor.
************************************/
/*#FOLD: Includes
/************
**
*
522
Includes:
*/
4.3
ABACUS
STEP BY ST E P
************/
#include <stdio.h>
#include <exec/types.h>
#include <intuition/intuition.h>
#include "src/Editor.h"
/*#ENDFD*/
/*#FOLD: Defines */
/***********
** Defines:
************/
#define TAB 9
#define LF 10
#define CR 13
#define UC(c) (c >= 'a') && (c <= 'z'? (c & OxDF): c)
#define DIGIT(c) c >= '0') && (c <= '9'
#define SKIPBLANKS(str) while (*str == , ') str++;
/*#ENDFD*/
/*#FOLD: External Functions */
/**********************
* External Functions:
**********************/
IDOL
cursorLeft(),cursorRight(),cursorUp(),cursorDown(),cursorHome();
BOOL cursorEnd(),deleteChar(),deleteLine(),
backspaceChar(),insertLine();
BOOL saveLine();
void printAll(),printFlags(),fclose(),Insert(),
strncpy(),printNumLines();
void restoreZlinenptr(),SetWindowTitles(),
initFolding(),printYpos();
void DisplayBeep(),saveIfCursorMoved(),CopyMem();
struct Zline *newZline(),*prevLine(),*nextLine();
FILE *fopen ();
int fwrite(),getc();
UBYTE *getLastWord();
ULONG fseek ();
UWORD recalcTopOfWindow();
/*#ENDFD*I
I*#FOLD: External Variables *1
/*********************
* External Variables:
*********************/
**
Globale Variables:
*********************/
523
4.
AMIGA
struct jmpEntry
{
UBYTE c;
UBYTE * (*fkt) ();
};
I*#ENDFD*I
1***************
*
*
Functions:
*
****************1
I*#FOLD: saveASCII *1
1*********************************
* saveASCII(name)
**
**
name
File name.
*********************************1
BOOL saveASCII(name)
UBYTE
*name;
{
(actuaIEditor->bufypos)
(! saveLine (actuaIEditor->actual
DisplayBeep(NULL);
return (FALSE);
(file
if
{
z = actuaIEditor->zlines.head;
while (z->succ)
{
i f (z->len)
1* Error! *1
fclose (file);
return (FALSE);
z
= z->succ;
actuaIEditor->changed
printFlags () ;
fclose (file);
return (TRUE);
else
return (FALSE);
524
= 0;
ABACUS
4.3
STEP BY STEP
I*#ENDFD*I
I*#FOLD: loadASCII *1
1***********************************
* loadASCII(name)
**
name
File name.
***********************************1
BOOL loadASCII(name)
UBYTE
*name;
{
UBYTE buf[MAXWIDTH];
register UBYTE *ptr,*tab;
int e;
UWORD rm = aetuaIEditor->rm;
register FILE *file;
register UWORD len;
register struet Zline *z,*zn;
if (file
fopen(name,"r"
= ZLINESPTR(aetuaIEditor->wdy
(z
if
{
aetuaIEditor->ehanged
printFlags 0 ;
while (!feof(file
{
tab
ptr
len
= aetuaIEditor->tabstring;
= buf;
= 0;
if e = gete(file
break;
else
*ptr++ = (UBYTE)e;
if e
==
EOF)
cb
{
tab++;
len++;
while *tab) && (len < rm;
else
l
525
4.
AMIGA
tab++;
len++;
i f (c == LF)
break;
else if (c == CR)
if (!feof(file) && (len < rm
{
/* of if (TAB) *1
1* of while */
if (len = ptr - buf)
{
1* Wordwrap? *1
if (!actualEditor->skipblanks)
if len >= rm) && (c != CR) && (c != LF
{
tab = getLastWord(buf,len);
fseek(file, (long) tab - ptr,1);
len = (ptr = tab) - buf;
/* save line now: *1
actualEditor->1ineptr =
if (zn = newZline(1en
Z;
1* Garbage-Collection
z = actualEditor->lineptr;
zn->len = len;
CopyMem(buf,zn + 1, (ULONG)len);
actualEditor->num lines++;
Insert (&actualEditor->zlines,zn,z);
z = zn;
actualEditor->lineptr = NULL;
else
{
actualEditor->lineptr = NULL;
fclose (file);
return (FALSE);
ZLINESPTR(O)
ZLINESNR(O)
= actualEditor->zlines.head;
=
1;
restoreZlinenptr();
526
*1
4.3
ABACUS
STEP BY STEP
printAll () ;
printNumLines();
fclose (fil~);
return (TRUE);
else
return (FALSE);
)
/*IIENDFD*/
/*************************************************
* command-Functions:
**
*
*
*
*
**
*************************************************/
/*IIFOLD: commanciASCII */
UBYTE *commanciASCII(str)
register UBYTE
*str;
{
case 'L':
str++;
SKIPBLANKS (str)
if (*str = ,n,)
{
"")
name = buf;
else
527
4.
AMIGA
if (!loadA5CII(name
return (error);
break;
case '5':
str++;
SKIPBlANK5 (str)
if (*str = '"')
{
'"')
name = buf;
else
I*#ENDFD*I
I*#FOLD: commandCursor *1
UBYTE *commandCursor(str)
register UBYTE
*str;
{
switch (UC(*str
{
case 'R':
if (!cursorRight(
return (NULL):
break;
case 'L':
if (!cursorLeft(
return (NULL);
break;
case 'U':
i f (! cursorUp 0 )
return (NULL);
break;
case 'D':
i f (! cursor Down () )
528
4.3
ABACUS
STEP BY STEP
return (NULL);
break;
case 'E':
i f (!cursorEnd(
return (NULL);
break;
case '5':
case 'H':
i f (!cursorHome(
return (NULL);
break;
case 'N':
i f (!cursorDown(
return (NULL);
i f (!cursorHome(
return (NULL);
break;
case 'P':
i f (! cursorUp () )
return (NULL);
i f (! cursorHome () )
return (NULL);
break;
case 'T':
i f (!cursorTop(
return (NULL);
break;
case 'B':
i f (!cursorBottomO)
return (NULL);
break;
default:
return (NULL - str);
I*#ENDFD*I
I*#FOLD: commandDelete *1
UBYTE *commandDelete(str)
register UBYTE
*str;
{
switch (UC(*str
{
case 'L':
i f (! deleteLine(
return (NULL);
break;
case 'e':
i f (! deleteChar(
return (NULL);
break;
case 'B':
i f (! backspaceChar(
return (NULL);
break;
default:
return (NULL - str);
529
/*#ENDFD*/
/*#FOLD: commandLine *1
UBYTE *commandLine(str)
register UBYTE
*str;
{
switch (UC(*str
{
case '5':
i f (! insertLine UBYTE) 0
return (1 - str);
break;
default:
return (NULL - str);
return (str + 1);
}
I*#ENDFD*I
/*#FOLD: commandQuit *1
UBYTE *commandQuit(str)
register UBYTE
*str;
{
return (-1L);
}
I*#ENDFD*I
/*#FOLD: commandSet *1
UBYTE *commandSet(str)
register UBYTE
*str;
{
switch (*str)
{
case 't':
actualEditor->tabs = 0;
printAll () ;
printFlags();
break;
case 'T':
actualEditor->tabs = 1;
printAll () ;
printFlags () ;
break;
case 'i':
case 'I':
actualEditor->insert = 1;
printFlags();
break;
case '0':
case '0':
actualEditor->insert = 0;
printFlags () ;
break;
case 'b':
actualEditor->skipblanks= 0;
printFlags () ;
break;
case 'B':
actualEditor->skipblanks= 1;
printFlags () ;
break;
530
4.3
ABACUS
STEP BY ST E P
case 'a':
actualEditor->autoindent= 0;
printFlags () ;
break;
case 'A':
actualEditor->autoindent= 1;
printFlags () ;
break;
case 'r':
case 'R':
{
==
'=')
str++;
(DIGIT (* str
if
{
rm = 0;
while (DIGIT(*str
{
rm = MAXWIDTH;
else if (rm < 10)
rm = 10;
)
else
1* Fehler *1
return (NULL - str);
else
rm
= MAXWIDTH;
actualEditor->rm = rm;
--str;
break;
default:
return (NULL - str);
return (str + 1);
}
I*#ENDFD*I
I*#FOLD: commandExit *1
UBYTE *commandExit(str)
register UBYTE
*str;
{
if (actualEditor->changed)
if (saveASCII(actualEditor->filename
return (-lL);
else
return (-lL);
}
I*#ENDFD*I
531
/*#FOLD: commandBracket */
UBYTE
*executeCommand();
UBYTE
*commandBracket(str)
register UBYTE
*str;
{
register
UBYTE *ptr;
(ptr = executeCommand(str
if
{
if
(ptr!= -IL)
(ptr >= Ox80000000)
/* Pointer behind command line '}':
ptr =
11 - ptr;
else
/* pointer to error: */
ptr = NULL - ptr;
if
*1
else
{
(*ptr
if
lIl1)
d:>
{
ptr++;
while (*ptr
&&
(*ptr != "";
ptr++;
(*ptr =
0)
ptr = NULL;
else
ptr++;
if
return (ptr);
}
I*IIENDFD*/
I*IIFOLD: messageWaiting *1
/*********************************
* messageWaiting:
*********************************1
BOOL messageWaiting()
{
532
ABACUS
4.3
STEP BY STEP
if im->Class == RAWKEY)
&& ! (im->Code & IECODE UP PREFIX)) return (TRUE);
if (im->Class == NEWSIZE)-return (TRUE);
im = (struct IntuiMessage *)im->ExecMessage.mn_Node.ln_Succ;
return (FALSE);
}
/*#ENDFD*/
/*#FOLD: Functions table */
struct jmpEntry jmpTable[)
{
'A',&commandASCII,
'C',&commandCursor,
'D',&commandDelete,
'L',&commandLine,
'Q',&commandQuit,
'S',&commandSet,
'X',&commandExit,
'{',&commandBracket
};
/*#ENDFD*/
/*#FOLD: executeCommand */
/****************************************
* executeCommand(str)
*****************************************/
UBYTE *executeCommand(str)
register UBYTE
*str;
{
SKIPBIANKS(str)
if
(*str =
'}')
return (NULL - str) ;
i f (DIGIT (*str)
{
count = 0;
while (DIGIT(*str))
{
533
if
(count =
count = 1;
else
count
0)
= 1;
if (c
~=
jmpTable[n].c)
if (ptr
(*fkt) (str +
else
1* break *1
return (NULL);
else
1* Unknown command *1
return (str);
savelfCursorMoved();
SKIPBlANKS(str)
if (*str * = ' ; ' )
str++;
else if (*str ~
return (NULL else if (*str)
return (str);
return (NULL);
}
I*#ENDFD*I
534
, } ')
str);
ABACUS
4.3
STEP BY ST E P
When loading, individual characters are read until the program reaches
an end of line character, or when the line is too wide. Because the
function getc (which reads the characters) works internally with a
buffer, the method is no slower than if we loaded an entire text block
into a buffer and removed the characters from there. The maximum
number of characters per line that can be loaded is a changeable
parameter-the new variable rm in the Editor structure serves this
purpose. rm displays the right margin.
A set skipblanks flag breaks lines if a line is too long to fIt on the
screen. This break shifts the word causing the problem to the next line.
SkipBlanks is also known as word wrap. The getLastWord
function is implemented in the Edit module. It returns a pointer to the
beginning of the last word of a string.
The editor had an error in it since its fIrst stages of development. The
garbageCollection function in the Memory module reorganizes
a memory block. All of the lines can be moved so that this function
535
4.
AMIGA
checks the actual variable and the zlinesptr array, and moves a
pointer. A pointer to the last line borrows data from the loadASCII
function, which must also be checked. The Editor structure uses this
zlinesptr array. The garbageCollection function checks this
pointer. When we have a local variable that points to a line, we save
this in zlineptr before calling the newLine function, which calls
the garbageCollection function. After the call we access
zl inept r again, and can be certain that this pointer points to the
same line. The error occurs in the saveLine function, when the
newLine function is called while a local variable contains a pointer to
a line. We will correct this error.
After all of the lines load, ini tFolding recalculates the fold levels
of all of the lines, and the window contents are redisplayed.
536
4.3
ABACUS
STEP BY ST E P
(actualEditor->lineptr:: z)
actualEditor->lineptr
fz;
The positions and lengths of each value of the info line must be stated
as defines:
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
537
The first six elements contain the gadget structures for the command
and info lines. Next follow the positions of the individual values of the
info line, the current filename initialized from the loadASCII
function, the right margin and the help pointer to a line viewed by the
garbageCollection function.
We now tum our attention to the main module Edi tor. c, which
requires the addition of another include file:
#include <intuition/intuitioribase.h>
Two new defines follow, they state the argument template returned
when the user enters Editor ? from the eLI:
#define UT1 "USAGE: Editot [Flags] <Filename>"
#define UT2 "Flags: [-aJ[-AJ[-b][-BJ[- iIIJ[- 010][- rIR[=nll [-
t1 [-T1"
Since the editor now has built in text loading capability. you can start
the editor from the eLI by entering the editor name. a space and the
filename:
editor myfile
You can also enable and disable flags from the eLI as the editor loads.
For example, instead of loading the editor, pressing <Esc> and entering
SA to enable auto indent, the following loads the editor and sets the
auto indent flag:
editor -a
538
4.3
ABACUS
STEP BY STEP
gc_ SIBuffer,
gc_UndoBuffer,
0,
256,
0,
0,0,0,0,0,
0,
NULL,
NULL
);
SHORT gc_BorderVectors[4)
struct Border gc Border =
{0,0,350,0);
-1,-1,
1,0,JAMl,
2,
gC_BorderVectors,
NULL
);
NULL,
2,-9,
-302,9,
GADGHCOMP I GRELBOTTOM I GRELWIDTH,
RELVERIFY I BOTTOMBORDER,
STRGADGET,
(APTR) &gc_Border,
NULL,
NULL,
0,
(APTR)&gc_GadgetSI,
2,
NULL
);
539
4.
AMIGA
0,0,
1,0,JAMl.
7,
gi_BorderVectors,
NULL
}i
};
1 i=
0 [ 0. 0] lcATB";
1,0,JAM2,
4,2,
&TOPAZ80.
gi_Text.
NULL
};
&G Corrrnand.
-i98,-10,
280,10,
GADGHNONE I GRELBOTTOM I GRELRIGHT,
RELVERIFY I BOTTOMBORDER,
BOOLGADGET,
(APTR) &gi Border,
NULL,
&gi IText.
0, -
NULL,
1,
NULL
};
0,0,640,200,
AUTOFRONTPEN,AUTOBACKPEN,
REFRESHWINDOW I MOUSEBUTTONS t RAWKEY I CLOSEWINDOW I NEWSIZE
GADGETUP,
WINDOWSIZING I WINDOWDRAG I WINDOWDEPTH r WINDOWCLOSE I
SIZEBBOTTOM
I SIMPLE_REFRESH I ACTIVATE,
NULL, NULL.
NULL,
NULL, NULL,
320,50,640,200,
WBENCHSCREEN
);
540
NULL;
4.3
ABACUS
STEP BY ST E P
1* Gadgets initialization: *1
ed->gc SIBuffer[O] = 0;
ed->gc=GadgetSI
= gc_GadgetSI;
= G Command;
ed->G Command
ed->gI IText
= g1 IText;
ed->G Info
= G Info;
strncpy(ed->gi_Text,gi_Text,sizeof(ed->gi_Text);
1* set pointer: *1
ed->gc GadgetSI.Buffer
= ed->gc_SIBuffer;
ed->G Command.SpecialInfo = (APTR) &(ed->gc GadgetSI):
ed->g1 IText.IText
= ed->gi Text;
= &(ed->G Command);
ed->G Info.NextGadget
ed->G-In fo. GadgetText
= &(ed->gI IText);
newEdWindow.FirstGadget
= &(ed->G Info);
newEdWindow.Title
= ed->filename;
ed->filename[O]
'"' 0;
1* Window open: *1
if (wd = OpenWindow(&newEdWindow
{
541
4.
AMIGA
ed->window = wd;
ed->rp = (rp = wd->RPort);
/* Write mode set: */
SetDrMd(rp,JAM2);
SetAPen(rp,FGPEN);
SetBPen(rp,BGPEN);
/* UserPort established: */
wd->UserPort = edUserPort;
ModifyIDCMP(wd,flags);
/* Parameter initialization: */
NewList(&(ed->block));
NewList (& (ed->zlines));
ed->num lines = 0;
ed->actual
= NULL;
ed->buflen
= 0;
ed->bufypos
= 0;
ed->buflastchar= , ';
ed->leftpos
= 0;
ed->xpos
= 1;
ed->ypos
= 1;
ed->wdy
= 0;
ed->changed
= 0;
ed->insert
= 1;
ed->tabs
= 1;
ed->skipblanks = 1;
ed->autoindent = 1;
ed->minfold
= 0;
ed->maxfold
= 0;
ed->rm
= MAXWIDTH;
register UBYTE *ptr;
register struct Zline **zptr;
register UWORD n,*pnr;
/* zlinesptr/nr-Array initialization: */
for (n = 0, zptr = ed->zlinesptr, pnr = ed->zlinesnr;
n < MAXHEIGHT; n++, zptr++, pnr++)
*zptr = NULL;
*pnr = 1;
/* Tab initialization: */
ptr = ed->tabstring;
*ptr++ = 1;
for (n = 1; n < MAXWIDTH; n++)
i f (n % 3)
*ptr++ = 1;
else
*ptr++ = 0;
542
ABACUS
4.3
STEP BY STEP
oldae
= actualEditor;
actualEditor = ed;
printInfo () ;
actualEditor = oldae;
else
{
free(ed);
ed = NULL;
newEdWindow.IDCMPFlags = flags;
return (ed);
The info line is displayed at the end of OpenEdi tor so that this
agrees with the additional values. The actualEditor variable turns
towards the re-opened editor because the output functions assume that
act ualEdi tor points to the info line of the current editor.
Our main program requires some major additions. The main function
expects the argc and argv arguments, which encompass all of the
arguments given to the program from CLI access. At the beginning of
the program, before the libraries open, the program tests to see whether
the editor was called with Edit 0 r ? If so, the program displays the
argument template and returns to the CLI. The puts function displays
a string without the format specifications and escape sequences of the
printf function.
After the libraries are open the character set for the info line is accessed,
and a pointer to it is placed in the infoFont variable. Before the
editor is opened, the program determines the maximum height of the
screen and enters this in the Newwindow structure, giving the window
the maximum size when it is opened. The ActiveScreen variable of
the IntuitionBase structure acts as a pointerto the active screen.
Mter the editor window opens, the program reads the arguments entered
from the CLI (if any) and executes these arguments through the
command line. The order of the arguments must follow the template.
Otherwise the program ends and an error message appears. The one error
that loads the editor anyway occurs when the editor tries to find the
filename stated from the CLI and fails.
The main loop stays the same as before. However, the handleKeys
function returns a value of type BOOL. If this value is FALSE, the
program stops. MOUSEBUTTON and NEWSIZE messages display the
cursor position with printXpos and printYpos, if these are
changed.
543
4.
AMIGA
puts (UT1);
puts (UT2):
goto Ende;
1* Libraries open: *1
i f ( ! (IntuitionBase = OpenLibrary("intuition.library",REV)
goto Ende;
i f ( ! (GfxBase = OpenLibrary ("graphics library" , REV) ) )
goto Ende;
544
ABACUS
4.3
STEP BY STEP
goto Ende;
if ( ! (OpenDevice("console.device",-lL,&ioStdReq,OL)
ConsoleDevice = ioStdReq.io Device;
else
goto Ende;
AddTail(&editorList,ed);
actualEditor = ed;
}
else
goto Ende;
register UWORD n = 1;
while
< argc)
(n
(*argv[n] =
if
._.)
if
(
DisplayBeep{NULL);
break;
}
else
{
1*
*1
n++;
break;
n++:
i f (n < argc)
{
545
4.
AMIGA
C FOR
ADVANCED PROGRAMMERS
1* Error! *1
puts (UTI);
puts (UT2);
goto Ende;
I*#FOLD:
Main loop *1
d>
(
1* Set cursor: *1
Cursor ();
signal = Wait(IL edUserPort->mp_SigBit);
/* erase cursor again: *1
Cursor ();
while (imsg = GetMsg(edUserPort
{
class
code
qualifier
iaddress
mouseX
mouseY
=
imsg->Class;
imsg->Code;
imsg->Qualifier;
= imsg->IAddress;
imsg->MouseX;
imsg->MouseY;
ReplyMsg(imsg) ;
1* Event processing: *1
switch (class)
{
case RAWKEY:
{
inputEvent.ie Code
= code;
inputEvent.ie-Qualifier = qualifier;
if inputLen-= RawKeyConvert(
&inputEvent,inputBuffer,MAXINPUTLEN,NULL)
) >= 0)
running = handleKeys(inputBuffer,inputLen);
1* run-on suppressed: *1
iml = (struct IntuiMessage *)
edUserPort->mp MsgList.lh Head;
while (im2 = (struct IntuiMessage *)
iml->ExecMessage.mn_Node.ln_Succ)
if
if
if
if
if
1* Message reply: *1
iml = GetMsg(edUserPort);
ReplyMsg(iml);
546
ABACUS
case MOUSEBUTTONS:
{
x = 0;
else
x = (mouseX - actualEditor->xoff)
1 actualEditor->cw;
if (++x >= actualEditor->wcw)
x = actualEditor->wcw - 1;
x += actuaIEditor->leftpos;
if (x > MAXWIDTH)
x = MAXWIDTH;
if (mouseY <= actualEditor->yoff)
y = 0;
else
y = (mouseY - actuaIEditor->yoff)
1 actualEditor->ch;
if y < actualEditor->wch)
&& (actualEditor->zlinesptr[y))
actualEditor->xpos = x;
actuaIEditor->wdy = y;
actuaIEditor->ypos = actualEditor->zlinesnr[y);
printXpos();
printypos () ;
case NEWSlZE:
initWindowSize(actualEditor);
actuaIEditor->ypos = actualEditor->zlinesnr
[(actualEditor->wdy = actuaIEditor->wch - I)};
printYpos () ;
break;
case REFRESHWINDOW:
BeqinRefresh(actuaIEditor->window);
547
4.
AMIGA
printAlI () ;
EndRefresh(actuaIEditor->window,TRUE);
break;
case GADGETUP:
{
if
1* Gagdet aktivated: *1
ActivateGadget(&(actuaIEditor->G Command),
actuaIEditor->window~NULL):
DisplayBeep(NULL);
break:
case CLOSEWINDOW:
running = FALSE;
break:
default:
printf("Not a processable event: \lx\n",class):
1* of case *1
savelfCUrsorMoved();
} 1* of while (GetMsg( *1
} while (running):
I*#ENDFD*I
Ende:
DeletePort(edUserPort);
edUserPort = NULL:
1* Font closed: *1
548
4.3
ABACUS
STEP BY STEP
if (infoFont)
{
CloseFont(infoFont);
infoFont = NULL;
1* Close Libraries: *1
i f (DosBase)
{
CloseLibrary(DosBase);
DosBase = NULL;
}
i f (GfxBase)
{
CloseLibrary(GfxBase);
GfxBase = NULL;
}
if (IntuitionBase)
{
CloseLibrary(IntuitionBase);
IntuitionBase = NULL;
eLI
arguments in
the editor
The commandSet function reads the CLI command so that you can
set flags from the CLI instead of in the editor's command line (we
mentioned this earlier). When entering the set flags from the CLI.
remember to separate each argument with a space. and enter a dash
instead of an S. The following CLI command loads the editor. sets the
right margin at 60, disables both SkipBlanks and tabs, and loads the
ASCn me named Floatingtext .ASC into the editor:
Editor -r=60 -b -t Floatingtext.ASC
There are also a few changes in the Cursor. c module. First let's
examine some of the new Defines:
'define
'define
#define
#define
CEND '@'
ESC 27
TAB 9
REPCOM 7
Because flag switching now occurs through the command line, we can
remove the control code flag switching. Delete the De fin e s
TABMODE, WRITEMODE, and AUTOINDENT.
The following external functions are also new. We need these
commands for the info and command lines:
void printXpos(),printYpos(),printFold(),printFlags();
BOOL ActivateGadget();
UBYTE *executeCommand();
The following functions affect the cursor position or other info line
values (cursorLeft, cursorRight. cursorUp, cursorDown.
549
4.
AMIGA
**
cursorEnd:
***************************************1
BOOL cursorEnd()
{
= z->len:
~
UBYTE *) (z + 1
+ len - I:
i f (len)
i f (*ptr
=~ CR)
len--;
else if (*ptr == LF)
i f (--len)
if (*--ptr ~ CR)
len--;
if (actuaIEditor->tabs)
{
ptr
tab
pos
UBYTE *) (z + 1;
= actuaIEditor->tabstring;
= 1;
tab++;
pos++;
} while *tab) && (pos < MAXWIDTH;
else
{
tab++;
pos++;
else
pos
}
else
550
len + 1;
4.3
ABACUS
STEP BY STEP
return (FALSE);
actualEditor->xpos = pos;
1f (pos >= (actualEditor->leftpos + actualEditor->wcw
scrollLeft(pos + 1 - (actualEditor->leftpos
+ actualEditor->wcw;
else if (pos <= actualEditor->leftpos)
scrollRight(actualEditor->leftpos + 1 - pos);
printXpos () ;
return (TRUE);
}
I*#ENDFD*I
In the second switch case function, at the location where the CSI
sequence check occurs, insert three lines following the check for
CURSORHOME:
case CEND:
cursorEnd () ;
break;
*sibuf++
len--;
*buf++;
*sibuf = 0;
cnt = (sibuf - actualEditor->gc_SIBuffer);
actualEditor->gc GadgetSI.BufferPos
= cnt;
actualEditor->gc=GadgetSI.NumChars
= cnt;
sst
actuaIEditor->gc_GadgetSI.DispPos = 0;
1* Gagdet aktivated: *1
if (!ActivateGadget(&(actualEditor->G_Command).
actuaIEditor->window,NULL
DisplayBeep(NULL);
break;
case REPCOM:
{
if
DisplayBeep(NULL);
1* return now: *1
return (TRUE);
break;
When Esc is pressed the rest of the characters entered from the
keyboard are written to the active command line. This character transfer
makes the most sense since you can configure the keyboard. A key
could be configured so that when pressed, it sends <Esc> then a
command sequence. All the user has to do is press <Return> to enter
the sequence.
The second switch case function handles the case if the user
pressed <Ctrl><G> (REP COM), which repeatedly executes the command
currently in the command line. In the case of an error, the cursor of the
command line stops at the error and activates the window, just like the
GADGETUP check in the main program.
You must erase the TABMODE, WRITEMODE and AUTOINDENT
checks, because these flags can now be set through the command line.
552
4.3
ABACUS
STEP BY ST E P
This keeps the user from pressing the wrong key, and allows the user to
insert additional control codes directly into the text. A special feature of
our editor is that we can read text which consists of any characters.
When examining the editor we determined that the <Return> key sends
a CR. However, other CLI commands (e.g., TYPE) cannot make use of
lines ending with just a CR. We always add a linefeed (LF) as the end
of line so that text written from the editor can be used in other
programs. The CR/LF check looks like the following code:
case LF:
case CR:
i f (! insert Line UBYTE) LF
DisplayBeep(NULL);
break;
This concludes the Cursor. c module. Now we can tum to the final
changes needed in the Edi t c module. Start by adding a few new
external functions:
void printXpos(),printYpos(),printFold();
void printNumLines(),printFlags();
BOOL cursorEnd();
actuaIEditor->lineptr = line;
/* Garbage-Collection */
if (line = newZline(len
{
old = actuaIEditor->lineptr;
Insert (&actualEditor->zlines,line,old);
line->flags = old->flags & -ZLF USED;
deleteZline (old) ;
/* now possible to convert pointer: */
for (1 = 0, zptr = actualEditor->zlinesptr;
1 <= actuaIEditor->wch; 1++, zptr++)
if (*zptr == old)
{
*zptr = line;
break;
actualEditor->lineptr = NULL;
}
else
553
actualEditor->1ineptr = NULL;
return (FALSE);
else
{
line->len
= len;
line->flags &= -ZLF_USED;
len++;
This lets the editor divide lines without inserting characters. The end of
the function redisplays the info line's values:
printXpos 0 ;
printypos () ;
printNurnLines();
printFlags () ;
We assumed that the next line had the same number as the current line,
because the current line was erased. The above command sequence must
be changed as follows:
554
ABACUS
4.3
prevnr=
prev =
next ::
nextnr=
STEP BY STEP
ZLlNESNR(actuaIEditor->wdy);
prevLine (line, &prevnr) ;
nextLine (line, &nextnr);
ZLlNESNR(actuaIEditor->wdy);
getLastWord(str,len)
Returns pointer to last
word in a string.
str A String.
len = buffer length.
**************************************1
UBYTE *getLastWord(str,len)
register UBYTE
*str;
register UWORD
len;
(
if c = *-str)
= , .)
lwb = str;
break:
}
if (lwb == NULL)
if (lwn)
lwb = lwn;
else
lwb = end;
else
i f (lwn)
if end - lwb) > MAXWIDTH / 5)
if end - lwn + 5) < (end - lwb
lwb = lwn;
return (lwb + 1);
/*ItENDFD*/
555
4.
AMIGA
The original function returns after the character is inserted in the line.
The ini tFo1ding function should be inserted at the end of the Edit
module:
556
ABACUS
4.3
STEP BY STEP
/*flFOLD: initFolding */
/**********************************
* initFolding:
* calculate the fold level of all
* lines from text start.
**********************************/
void initFoldingO
{
= z->succ;
/*lIENDFD*/
This completes the final changes. Remove the test. 0 module from
the makefile. Replace it with the Command. 0 module and compile
it. You can find source text and the finished program on the optional
disk for this book, in the v 0 6 directory. Appendix A contains the
complete source text for all of the modules..
Now you can load any text; don't use the TestText file. Access the
editor from the CLI and load the Command. 0 file. If loading from the
optional disk for this book, you'd enter the following CLI commands:
cd VO.6
Editor src/Command.c
We added folds to this and all other modules for maximum readability.
You now have an overview of all of the functions in this module.
Pressing <CtrlxF> moves you deeper into a fold, and pressing
<Ctrl><E> moves you toward the top fold level.
From this point, you're on your own. You now have all the elements
for programming an editor. In addition, you have a finished product for
editing your own source texts. We mentioned a few extra commands
that could be added later, and listed some of the elements of a
programmable editor. With this knowledge and some extra study, you
could add extended features to your own editor, such as menus, block
functions and more.
557
4.
4.4
AMIGA
4.4.1
Cursor
movement
<Cursor up/down!left/right>
<Shift><Cursor right/left>
<Shut><Cursorup/down>
<Delete>
<Backspace>
<Tab>
Control keys
<Ctrl><B>
<Ctrl><E>
<Ctrl><P>
<Ctrl><G>
<Ctrl><H>
<Ctrl><1>
<Ctrl><J>
<Ctrl><M>
linefeed
carriage return
4.4.2
ASCII
commands
AL ["Filename"]
AS ["Filename")
CB (Cursor bottom)
CD (Cursor down)
CE (Cursor end)
CH (Cursor home)
Cursor
movement
558
4.4
ABACUS
EDITOR COMMAND
SET
CL (Cursor left)
CN (Cursor next)
CP (Cursor previous)
CR (Cursor right)
CS (Cursor start)
CT (Cursor top)
CU (Cursor up)
DB (delete back)
DC (delete char)
DL (delete line)
backspace
delete character
delete line
LS (line split)
Quit command
exits editor
Set commands
Sa
SA
Sb
SB
Si
So
ST
St
Delete
commands
Line
commands
eXit command x
559
ABACUS
Version 1 makefile
.c.o:
cc +B +Ipre!Editor.pre -0 $@ src/$*.c
prelist. pre:
cc -A -0 ram:$*.preasm +H$@ pre/$*.prelist
delete ram:$*.preasm
OBJ=src/Editor.o
Editor: $ (OBJ)
In $(OBJ) -lc
-0
Editor
Debug: $ (OBJ)
In -w $(OBJ) -lc
-0
Editor
561
Version 1 Editor.h
/************
* Includes:
. ..
************/
#include <exec/types.h>
#include <intuition/intuition.h>
/***********
**
Defines:
************/
#define MAXWIDTH 80
#define EVENLEN (x) (x) +1) &-2)
/*******************
* Data structures:
*******************/
struct Zline
{
struct Memorysection
{
struct FList
{
struct Memoryblock
562
AMIGA
ABACUS
struct BList
{
struct Editor
{
struct EList
{
563
Version 1 Editor.c
1* Editor.c VO.1 *1
1************
* Includes:
************1
#include <exec/types.h>
#include <intuition/intuition.h>
#include "src/Editor.h"
1***********
* Defines:
***********1
#define REV 33L
#define MAXINPUTLEN 128L
1**********************
* External Functions:
**********************1
struct Library *OpenLibrary();
struct Window *OpenWindow();
void CloseLibrary(),NewList(),AddTail(),CloseWindow(),free();
void DeletePort(),ModifyIDCMP(),ReplyMsg();
struct Editor *malloc(),*RemHead();
struct MsgPort *CreatePort();
struct IntuiMessage *GetMsg();
ULONG Wait(),OpenDevice();
SHORT RawKeyConvert();
1*********************
* Global Variables:
*********************1
struct Library *IntuitionBase = NULL, *GfxBase
struct Device *ConsoleDevice = NULL;
= NULL,
*DosBase
= NULL;
100,40,440,156,
AUTOFRONTPEN,AUTOBACKPEN,
REFRESHWINDOW I MOUSEBUTTONS I RAWKEY I CLOSEWINDOW,
WINDOWSIZING I WINDOWDRAG I WINDOWDEPTH I WINDOWCLOSE I SIZEBBOTTOM
I SIMPLE_REFRESH I ACTIVATE,
NULL, NULL,
(UBYTE *)"Editor",
NULL, NULL,
100,40,640,200,
WBENCHSCREEN
};
564
ABACUS
UBYTE inputBuffer[MAXINPUTLEN]:
UWORD input Len = 0;
struct InputEvent inputEvent
{
0,
IECLASS_RAWKEY,O,
0,0
};
1***************
* Functions:
***************1
1***********************************
*
*
*
*
*
OpenEditor ()
Open Editor window and
initalize Editor structure
Returns pointer to Editor structure
or is zero in case of an error
***********************************1
struct Editor *OpenEditor()
{
ed->window = wd:
1* UserPort established: *1
wd->UserPort = edUserPort;
ModifyIDCMP(wd,flags):
1* Parameter initialization: *1
NewList(&(ed->block;
NewList(&(ed->zlines;
ed->num lines = 0:
ed->actual = NULL:
ed->top = NULL:
ed->toppos = 0;
ed->xpos = 1;
ed->ypos = 1:
ed->changed = 0:
ed->insert = 1;
}
else
{
free (ed) ;
ed = NULL:
565
AMIG-A
newEdWindow.IDCMPFlags
return (ed);
flags;
1***************************
* CloseEditor(ed)
* Close editor window.
* ed A Editor structure.
***************************1
void CloseEditor(ed)
struct Editor *ed;
(
1* Close window: *1
ed->window->UserPort = NULL;
CloseWindow(ed->window);
free (ed);
1*****************
* Main program:
*****************1
main()
{
1* Libraries open: *1
if ( I (IntuitionBase = OpenLibrary("intuition.library",REV)
goto Ende;
if ( I (GfxBase
OpenLibrary("graphics.library",REV)
goto Ende;
if ( I (DosBase
OpenLibrary ("dos.library", REV)
goto Ende;
if ( I (OpenDevice("console.device",-IL,&ioStdReq,OL)
ConsoleDevice = ioStdReq.io Device;
else
goto Ende;
1* UserPort open *1
if (I (edUserPort = CreatePort(NULL,OL) goto Ende;
1* Editor list initialization: *1
NewList(&editorList);
1* Open first editor window: *1
if (ed = OpenEditor(
(
AddTail(&editorList,ed);
ActualEditor = ed;
else
goto Ende;
566
ABACUS
ApPENDIX A VERSION
1 EDITOR.C
d:>
{
signal
Wait(1L edUserPort->mp_SigBit);
class
= GetMsg(edUserPort
= imsg->Class;
code
while (imsg
{
qualifier
iaddress
imsg->Code;
imsg->Qualifier;
= imsg->IAddress;
ReplyMsg (imsg) ;
/* Event processing: */
switch (class)
{
case RAWKEY:
if (! (code & IECODE_UP~REFIX
{
inputEvent.ie_Code
= code;
inputEvent.ie_Qualifier = qualifier;
if ((l = RawKeyConvert (
&inputEvent,inputBuffer,MAXINPUTLEN,NULL)
) >= 0)
inputBuffer[l] = 0;
printf(II%3d> Length = %d: ",n++,l);
for (1 = 0; inputBuffer[l]; 1++)
printf("%2x", (UWORD)inputBuffer[l]);
if (inputBuffer[O] >= 32 && inputBuffer[O] <= 127)
printf(II %s",inputBuffer);
printf("\n");
break;
case CLOSEWINDOW:
running = FALSE;
break;
default:
printf("Not a processable event: %lx\n",class);
} /* of case */
/* of while (GetMsg(
while (running);
*/
Ende:
/* close all editor windows: */
while (ed = RemHead(&editorList
CloseEditor(ed);
/* Close UserPort: */
if (edUserPort) DeletePort(edUserPort);
/*
if
if
if
Close Libraries: *1
(DosBase) CloseLibrary(DosBase);
(GfxBase) CloseLibrary(GfxBase);
(IntuitionBase) CloseLibrary(IntuitionBase);
567
Version 4 TestText
The JI characters in the following fIle represent the linefeed character,
decimal 1O. The character at the end of the fIle represents the Escape
character, decimal 27. These characters and the "a" must be added to the
fIle since redirection is used to load the test fIle into Version 4 of the
editor for testing. We used the replace command from the Abacus
Amiga DOS Toolbox to replace a single linefeed with
"linefeed,a,linefeed" (10,97,10) and then placed an "escape,linefeed,
linefeed" (27,10,10) at the end of the file.
Notice the folding marker comments in this text, make sure to add these
to your Test Text fIle. The redirection feature of AmigaDOS allows
the a's and linefeeds in this fIle to be interpeted as input for this version
of the editor. Remember that an Escape character (decimal 27) must be
at the end of the fIle.
ctl!
I************i
as!
*i
as!
* Includes:'lI
as!
*'lI
as!
************/'ll
as!
':II
ctl!
#include <exec/types.h>':II
ctl!
#include <intuition/intuition.h>'lI
ctl!
#include "src/Editor.h"':II
as!
/*#ENDFD Includes */'lI
as!
/*#FOLD: Defines */'ll
as!
/***********'lI
a'll
*'lI
as!
* Defines:'lI
as!
*i
ctl!
***********/i
as!
'lI
568
ApPENDIX A VERSION
ABACUS
a'll
#define REV
TESTTEXT
33L~
a'l[
*/~
I**********************~
a'll
*'11
a'll
* External Functions:'1I
a'll
*'.I!
a'l[
**********************/'11
a~
'11
a'll
void Test(),print();~
a'll
'11
a'll
struct Library *OpenLibrary();'1I
aCJ[
void handleKeys(),Cursor();'ll
a'll
struct Editor *malloc(),*RemHead();'ll
a'l[
569
AMIGA
Version 6 makefile
.c.o:
cc +B +I/pre/Editor.pre -0 $@ src/$*.c
.prelist.pre:
cc -A -0 ram:$*.preasm +H$@ /pre/$*.prelist
delete ram:$*.preasm
OBJ=src/Editor.o src/Memory.o src/OUtput.o src/Cursor.o src/Edit.o\
src/Command.o
Editor: $ (OBJ)
In $(OBJ) -lc
-0
Debug: $ (OBJ)
In -w $(OBJ) -lc
Editor
-0
Editor
..
570
ABACUS
ApPENDIX A VERSION
6 EDITOR.H
Version 6 Editor.h
1************
* Includes:
************1
#include <stdio.h>
#include <exec/types.h>
#include <intuition/intuitionbase.h>
#include <intuition/intuition.h>
1***********
* Defines:
***********1
#define
#define
#define
#define
#define
#define
#define
#define
MAXWIDTH 80
MAXHEIGHT 64
BGPEN OL
FGPEN lL
FOLDPEN 2L
CTRLPEN 3L
EVENLEN (x) (x)+1) &-2)
CONTROLCODE(c) (c)&127)<32)
/*******************
* Data structures:
*******************/
struct Zline
{
571
AMIGA
struct Memorysection
{
struct FList
{
struct Memoryblock
{
struct BList
{
struct Editor
{
572
ApPENDIX A VERSION
ABACUS
6 EDITOR.H
UWORD skipblanks : 1;
UWORD autoindent : 1;
struet RastPort *rp;
UWORD xoff,yoff;
UWORD xser,yser;
UWORD ew,eh;
UWORD wew, weh;
UWORD xrl,xrr,yru,bl;
UWORD minfold;
UWORD maxfold;
UBYTE gc_SIBuffer[256j;
struct StringInfo ge GadgetSI;
struct Gadget G Command;
UBYTE gi Text[36];
struct IntuiText gi_IText;
struct Gadget G Info;
ULONG ip_xpos; ULONG ip ypos;
ULONG ip=nurnzlines;
ULONG ip minfold;
ULONG ip-maxfold;
ULONG ip-flags;
ULONG ip=topedge;
UBYTE filename [80] ;
UWORD rm;
struct Zline *lineptr;
};
struct EList
{
573
AMIGA
Version 6 Editor.c
1*********************************************
*
*
*
Editor
Text editor for book Adv C.
1988 DATA BECKER
ABACUS
*********************************************1
Includes *1
1************
I*#FOLD:
* Includes:
************1
#include <exec/types.h>
#include <intuition/intuitionbase.h>
#include <intuition/intuition.h>
#include "src/Editor.h"
/*#ENDFD*/
/*#FOLD: Defines *1
1***********
* Defines:
************1
#define
#define
#define
#define
T]"
REV 33L
MAXINPUTLEN 128L
UT1 "USAGE: Editor [Flags] <Filename>"
UT2 "
Flags: [-a] [-A] [-b] [-B] [- ill] [- 010] [- rIR[=n]] [-t] [-
I*#ENDFD*/
I*#FOLD: External Functions *1
/**********************
* External Functions:
**********************1
I*#ENDFD*/
I*#FOLD: Global Variables
1*********************
Global Variables:
*********************1
574
*1
ABACUS
ApPENDIX A VERSION
NULL, *DosBase
6 EDITOR.C
NULL:
gc SIBuffer,
gc- UndoBuffer,
0, -
256,
0,
0,0,0,0,0,
0,
NULL,
NULL
};
SHORT gc BorderVectors[4]
struct Border gc Border =
{
-1,-1,
1,0,JAMl,
{0,0,350,0}:
2,
gc_BorderVectors,
NULL
}:
NULL,
2, -9,
-302,9,
GADGHCOMP
GRELBOTTOM
RELVERIFY
BOTTOMBORDER,
GRELWIDTH,
STRGADGET,
(APTR) &gc Border,
NULL,
NULL,
0,
(APTR)&gc GadgetSI,
2,
NULL
}i
SHORT gi_BorderVectors[14]
struct Border gi Border =
{
-
{-1,0,-1,10,0,10,0,0,298,0,298,1,283,1};
0, 0,
1,0,JAMl,
7,
gi_BorderVectors,
NULL
};
575
AMIGA
C FOR
ADVANCED PROGRAMMERS
};
1 #=
a [ 0,
0] lcATB";
NUlL
};
&G Command,
-i98,-10,
280,10,
GADGHNONE I GREIBOTTOM I GRELRIGHT,
RELVERIFY I BOTTOMBORDER,
BOOLGADGET,
(APTR)&gi_Border,
NULL,
&gi IText,
0, NULL,
1,
NUlL
};
0,0,640,200,
AUTOFRONTPEN,AUTOBACKPEN,
REFRESHWINDOW I MOUSEBUTTONS I RAWKEY I CLOSEWINDOW I NEWSIZE I GADGETUP,
WINDOWSIZING I WINDOWDRAG I WINDOWDEPTH I WINDOWCLOSE I SIZEBBOTTOM
I SIMPLE REFRESH I ACTIVATE,
NULL, NULL,
NULL,
NULL, NULL,
320,50,640,200,
WBENCHSCREEN
};
= NULL;
= NULL;
0,
IECLASS RAWKEY,O,
0,0
};
/*#ENDFD*/
576
ApPENDIX A VERSION
ABACUS
6 EDITOR.C
/*IIFOLD: Functions */
/*#FOLD: nextLine */
/*********************************************
**
nextLine(line,pnr)
**
line ~ line-structure.
* pnr ~ to linenumber.
**********************************************/
struct Zline *nextLine(line,pnr)
register struct Zline *line;
register UWORD
*pnr;
(
if z = z->succ)->succ)
if z->flags & ZLF FOLD) < minfold)
{
/* Following lines lie outside of fold */
z = NULL:
break;
else
*pnr += 1;
else
{
1* no following line *1
z = NULL:
break:
while z->flags & ZLF_FOLD) > maxfold):
else
z
= NULL:
return (z);
}
/*#ENDFD*/
/*#FOLD: prevLine */
/*******************************************
**
prevLine(line,pnr)
**
**
line ~ Zline-structure.
* pnr ~ to line number.
*
577
AMIGA
*******************************************1
struct Zline *prevLine(line,pnr)
register struct Zline *line;
register UWORD
*pnr;
{
if ((z = z->pred)->pred)
if ((z->flags & ZLF FOLD) < minfold)
{
/* previous line lies outside fold *1
z = NULL;
break;
else
*pnr -= 1;
else
{
/* no previous line */
z = NULL;
break;
while ((z->flags & ZLF_FOLD) > maxfold);
else
z = NULL;
return (z);
}
I*#ENDFD*/
/*#FOLD: OpenEditor *1
1***********************************
* OpenEditor ()
*
*
***********************************1
578
ABACUS
/* Gadgets initialization: */
ed->gc SIBuffer[Oj = 0;
ed->gc-GadgetSI
= gc GadgetSI;
= G Command;
ed->G Command
ed->g1 IText
= g1 IText;
ed->G Info
:: G Info;
strncpy(ed->gi_Text,gi_Text,sizeof(ed->gi_Text;
/* set pointer: */
ed->gc_GadgetSI.Buffer
= ed->gc_SIBuffer;
ed->G Command.SpecialInfo = (APTR) &(ed->gc GadgetSI);
= ed->gi Text;
ed->g1 IText.IText
ed->G Info.NextGadget
= &(ed->G Command);
ed->G-Info.GadgetText
:: &(ed->gl IText);
newEdWindow.FirstGadget
= &(ed->G Info);
newEdWindow.Title
= ed->filename;
ed->filename [OJ
= 0;
/* Window open: */
if (wd:: OpenWindow(&newEdWindow
{
ed->window = wd;
ed->rp = (rp = wd->RPort);
579
AMIGA
/* zlinesptr/nr-Array initialization: */
for (n = 0, zptr = ed->zlinesptr, pnr ~ ed->zlinesnr;
n < MAXHEIGHT; n++, zptr++, pnr++)
*zptr
*pnr
= NULL:
= 1;
1* Tab initialization: *1
ptr = ed->tabstring;
*ptr++ = 1;
for (n = 1; n < MAXWIDTH; n++)
i f (n % 3)
*ptr++ = 1;
else
*ptr++ = 0;
= actualEditor:
ed;
oldae;
else
{
free (ed) :
ed = NULL;
newBdWindow.IDCMPFlags
return (ed);
flags;
I*#ENDFD*I
I*#FOLD: CloseEditor *1
/***************************
* CloseEditor(ed)
* ed
Editor structure.
***************************1
void CloseEditor(ed)
struct Editor *ed;
{
580
ABACUS
1*/tENDFD*1
/*/tENDFD*/
/*/tFOLD: Main program */
/***************** .
** Main program:
*
*****************1
puts(UT1);
puts (UT2);
gotl) Ende;
1* Libraries open: */
if ( ! (IntuitionBase ~ OpenLibrary("intuition.library",REV)
goto Ende;
if ( ! (GfxBase ~ OpenLibrary("graphics.library",REV)
goto Ende;
if ( ! (DosBase ~ OpenLibrary ("dos.library", REV )
goto Ende;
if ( ! (OpenDevice("console.device",-lL,&ioStdReq,OL)
ConsoleDevice ~ ioStdReq.io Device;
else
goto Ende;
(1
(infoFont
1* UserPort open *1
if
(1
(edUserPort
OpenEditor () )
581
AMIGA
AddTail(&editorList,ed);
actualEditor = ed;
}
else
goto Ende;
/* Command line reading: */
if (argc >= 2)
{
register UWORD n = 1;
while
(n
< argc)
(*argv[n]
if
I_I)
if
(
DisplayBeep(NULL);
break;
}
else
(
(loaciASCII(argv[n]
printAllO;
else
DisplayBeep(NULL);
if
1*
n++;
break;
n++;
if (n < argc)
(
1* Error! */
puts(UT1);
puts (UT2) ;
goto Ende;
/"ilFOLD:
Main loop */
<b
(
/* Set cursor: */
CUrsor 0 ;
signal = Wait(1L edUserPort->mp_SigBit);
/* erase cursor again: */
Cursor ();
while (imsg = GetMsg(edUserPort
{
class
= imsg->elass;
= imsg->Code;
qualifier = imsg->Qualifier;
iaddress
= imsg->IAddress;
code
582
ABACUS
ApPENDIX A VERSION
mouseX
mouseY
6 EOITOR.C
imsg->MouseX;
imsg->MouseY;
ReplyMsg (imsg) ;
/* Event processing: */
switch (class)
{
case RAWKEY:
{
inputEvent. ie_Code
= code;
inputEvent.ie Qualifier = qualifier;
if inputLen-= RawKeyConvert(
&inputEvent,inputBuffer,MAXINPUTLEN,NULL)
) >= 0)
running = handleKeys(inputBuffer,inputLen);
/* run-on suppressed: */
iml = (struct IntuiMessage *)
edUserPort->mp_MsgList.lh_Head;
while (im2 = (struct IntuiMessage *)
iml->ExecMessage.mn_Node.ln_Succ)
if
if
if
if
if
/* Message reply: */
im1 = GetMsg(edUserPort);
ReplyMsg (im1);
im1 = (struct IntuiMessage *)
edUserPort->mp_MsgList.lh_Head;
break;
case MOUSEBUTTONS:
{
583
AMIGA
y = (mouseY - actuaIEditor->yoff)
I actuaIEditor->ch;
if ((y < actuaIEditor->wch)
&& (actuaIEditor->zlinesptr[y]
actuaIEditor->xpos = X;
actuaIEditor->wdy = y;
actuaIEditor->ypos = actuaIEditor->zlinesnr[yJ;
printXpos () ;
printYpos () ;
case NEWSIZE:
initWindowSize(actuaIEditor);
actuaIEditor->ypos = actuaIEditor->zlinesnr
[(actuaIEditor->wdy = actuaIEditor->wch - 1)J;
printYpos () ;
break;
case REFRESHWINDOW:
BeginRefresh(actuaIEditor->window);
printAll () ;
EndRefresh(actuaIEditor->window,TRUE);
break;
case GADGETUP:
{
if
584
ABACUS
ApPENDIX A VERSION
6 EDITOR.C
- actuaIEditor->gc_GadgetsI.DispCount / 2;
1* Gagdet aktivated: */
ActivateGadget(&(actuaIEditor->G Command),
actuaIEditor->window~NULL);
DisplayBeep(NULL);
}
break;
case CLOSEWINDOW:
running = FALSE;
break;
default:
printf("Not a processable event: %lx\n",class);
1* of case */
savelfCUrsorMoved();
} 1* of while (GetMsg(
*/
} while (running);
/*#ENDFD*/
Ende:
/* Close all editor windows: *1
while (ed = RemHead(&editorList
CloseEciitor(ed);
1* Close UserPort: *1
if (edUserPort)
{
DeletePort(edUserPort);
edUserPort = NULL;
1* Font closed: *1
if (infoFont)
(
CloseFont(infoFont);
infoFont = NULL;
1* Close Libraries:
*/
i f (DosBase)
{
CloseLibrary(DosBase);
DosBase = NULL;
}
i f (GfxBase)
{
CloseLibrary(GfxBase);
GfxBase = NULL;
}
if (IntuitionBase)
{
CloseLibrary(IntuitionBase);
IntuitionBase = NULL;
}
I*#ENDFD*I
585
Version 6 Edit.c
1****************************************
*
*
*
Module: Edit
Functions to edit the text
****************************************/
/*#FOLD: Includes
/************
* Includes:
************/
*/
#include <exec/types.h>
#include <intuition/intuition.h>
#include "src/Editor.h"
/*#ENDFD*/
/*#FOLD: Defines */
/***********
* Defines:
***********/
#define CR 13
#define LF 10
#define TAB 9
/* Lenght of fold start and end: */
#define FSE LEN 10
/* Number of characters, significant for fold marking: */
#define FSE SIG 8
/*#ENDFD* / /*#FOLD: External Functions */
/**********************
* External Functions:
**********************/
UWORD convertLineForPrint(),strncmp(),recalcTopOfWindow();
void deleteZline(),Insert(),printAll(),restoreZlinenptr();
void DisplayBeep(),printLine(),AddHead(),scrollRight(),scrollUp();
void ScrollRaster(),printXpos(),printYpos(),printFold();
void printNumLines(),printFlags();
struct Zline *newZline(),*nextLine(),*prevLine();
BOOL cursorLeft(),cursorRight(),cursorDown(),cursorHome(),cursorEnd():
/*#ENDFD*/
/*#FOLD: External Variables */
/*********************
* External Variables:
*********************/
extern struct Editor *actualEditor;
/*#ENDFD*/
/*#FOLD: Global Variables *1
1*********************
* Global Variables:
*********************/
UBYTE foldStart[]
586
"/*#FOLD:*/";
ABACUS
UBYTE foldEnde []
/*!fENDFD*/
"/*!fENDFD*/";
/***************
* Functions:
***************/
/*#FOLD: getLineForEdit */
/********************************
* getLineForEdit(line,znr)
* Copy line to edit into
* the buffer.
* line 1\ Zline.
* znr : line number of the line.
********************************/
void getLineForEdit (line, znr)
register struct Zline *linei
register UWORD
znr;
{
1* convert line: */
actualEditor->buflen
convertLineForPrint(line+l,line->len,MAXWIDTH + 1,
actualEditor->buffer,&actualEditor->buflastchar):
/* mark line as "used" : */
line->flags I: ZLF USED;
actualEditor->actual : line;
actualEditor->bufypos : znr;
/* establish Leftpos: */
actualEditor->leftpos : leftpos;
}
/*#ENDFD*/
/*#FOLD: cursorOnText */
1*******************************
* cursorOnText:
* make sure that the cursor
* is found on text.
*******************************/
void cursorOnText()
{
zptr--:
1-;
actualEditor->wdy
actualEditor->ypos
I;
ZLINESNR (1) :
587
AMIGA
I*#ENDFD*I
I*#FOLD: deconvertLine *1
1*************************
*
*
*
*
*
deconvertLine():
Deconverts line.
buf "target buffer.
buffer" source pointer.
buflen= buffer length.
*************************1
UWORD deconvertLine(buf, buffer,buflen)
UBYTE
*buf, *buffer:
UWORD
buflen;
{
= NULL;
1* deconvert line: *1
pl = buffer:
p2 = buf:
tab: actualEditor->tabstring;
1
= buflen:
if (actualEditor->tabs)
1* Spaces converted to tabs: *1
while (1)
{
if *P2 :: *pl++)
")
if (fb = NULL)
fb :: p2:
}
else
{
i f (fb)
fb
NULL;
*fb :: TAB;
fb++;
p2 :: fbi
else
p2++:
1-;
}
else
while
(1)
*p2 = *pl:
pl++:
p2++:
1-:
588
*1
ABACUS
ApPENDIX A VERSION
&&
, ') II (*(p2-1)
*(p2-1)
6 EDIT.C
TAB )
/*#ENDFD*/
/*#FOLD: getFoldInc */
/****************************************
* getFoldInc(line)
* Tests if line is a fold start
* or end mark and gives 1 or -1
* back.
* Else a 0 is returned.
* line ~ corresponding line.
****************************************/
awORD getFoldlnc(line)
struct Zline
*line;
{
p1++;
1-;
1;
*/
*/
==
0)
0)
else
1 = 0;
else
1 = 0;
retum (1);
}
/*#ENDFD*/
/*#FOLD: saveLine */
/*******************************
* saveLine(line)
* Saves line again.
* line ~ old line structure.
*******************************/
BOOL saveLine(line)
struct Zline *line;
{
/* +2 fuer CR und LF */
=
NULL;
589
AMIGA
C FOR
ADVANCED PROGRAMMERS
p2 = buf + len;
if (1 = line->len)
{
pI = UBYTE *) (line + 1
i f (*pl = CR)
+ line->len - 1;
len++;
*p2 = CR;
}
len++;
if 1 > 1) && (*--pl
==
CR
len++;
*p2++ = CR;
*p2 = LF;
actualEditor->lineptr = line;
if (line = newZline(len
1* Garbage-Collection
old = actualEditor->lineptr;
Insert(&actualEditor->zlines,line,old);
line->flags = old->flags & -ZLF USED;
deleteZline (old) ;
-
*zptr = line;
break;
actualEditor->lineptr = NULL;
else
{
actualEditor->lineptr = NULL;
return (FALSE);
else
{
590
*/
ABACUS
line->len
.. len;
line->flags &= -ZLF_USED;
=
=
while (1'
{
*pl .. *p2;
p1++;
p2++;
1--;
}
1* reconstruct folding: *1
1* was old or is new line a fold marker? *1
1 = getFoldlnc(line);
i f line->flags & ZLF FSE) II 1)
i f (1)
1:
*1
= line->succ)->succ)
*1
-=
1;
i f (1)
{
591
AMIGA
C FOR
ADVANCED PROGRAMMERS
printLine(line,actualEditor->yoff + actualEditor->ch*l);
break;
1* Delete reference: *1
actualEditor->actual = NULL;
actualEditor->buflen
= 0;
actualEditor->bufypos = 0;
1* Text was changed! *1
actualEditor->changed = 1;
printFlags () ;
return (TRUE);
}
I*#ENDFD*!
I*#FOLD: savelfCursorMoved *1
1***************************************
* savelfCursorMoved:
* Saves line in buffer,
* if the cursor is moved.
***************************************1
void savelfcursorMoved()
{
/*#ENDFD*/
I*#FOLD: undoLine
*1
1******************************************
* undoLine:
* Makes changes in actual line
* before cursor leaves line
******************************************1
void undoLine ()
{
actualEditor->actual = NULL;
actualEditor->bufypos = 0;
actualEditor->buflen = 0;
z->flags &= -ZLF_USED;
1* Line output: *1
printLine(z,actualEditor->yoff
+ actualEditor->ch*actualEditor->wdy);
}
I*#ENDFD*I
I*#FOLD: getBufferPointer * I
1***************************************************
* getBufferPointer(pptr,pinc,prest):
592
ABACUS
ApPENDIX A VERSION
6 EDIT.C
*
*
*
*
*
*
***************************************************1
BOOL getBufferPointer(pptr,pinc, prest)
UBYTE
**pptr;
UWORD
*pinc,*prest;
{
i f (rest < 0)
{
593
*p
AMIGA
= , ';
pH;
rest++;
}
else
return (FALSE);
*pptr z ptr;
*pinc = inc;
*prest= rest;
return (TRUE);
}
I*#ENDFD*I
I*#FOLD: deleteChar *1
1************************************
*
*
*
*
deleteChar:
Delet echaracter under the cursor.
False is returned if no more
characters.
************************************1
BOOL deleteChar()
{
UBYTE *ptr;
UWORD inc, rest;
register UBYTE *p1,*p2;
register UWORD I:
if (! getBufferPointer(&ptr,&inc,&rest
return (FALSE);
if (rest)
{
p1 = ptr + 1;
p2 = ptr;
1 = --rest;
while (1)
(
*p2
*p1;
p1++;
p2++;
1--:
*p2
= actualEditor->buflastchar;
1* Lenght - 1 *1
actualEditor->buflen--;
printLine(actualEditor->actua1,actualEditor->yoff
+ actualEditor->ch*actualEditor->wdy);
return (TRUE);
}
I*#ENDFD*I
I*#FOLD: backspaceChar *1
1******************************
* backspaceChar:
* Delete character before cursor.
* False is returned when
594
ABACUS
******************************1
BOOL backspaceChar ()
{
UBYTE *ptr;
UWORD inc, rest;
register UBYTE *pl,*p2;
register UWORD 1;
if (! getBufferPointer(&ptr,&inc,&rest
return (FALSE);
i f (inc)
{
if (actualEditor->insert)
{
pl
p2
ptr;
--ptr;
1 = rest;
while (1)
{
=
=
*p2 = *pl;
pl++;
p2++;
1--;
*p2
= actualEditor->buflastchar;
1* Lenght - 1 *1
actualEditor->buflen--;
inc--;
}
else
{
*--ptr = ';
inc--;
rest++;
cursorLeft () ;
printLine(actualEditor->actual,actualEditor->yoff
+ actualEditor->ch*actualEditor->wdy);
return (TRUE):
}
I*#ENDFD*I
I*#FOLD: insertLine *1
1**************************
* insertLine(c):
* Inserts new line behind
* actual line.
* FALSE returned if
* error encountered.
* c = line end (CR/LF).
**************************1
BOOL insertLine(c)
UBYTE
c;
{
595
AMIGA
UBYTE *ptr;
register UBYTE *pl,*p2;
WORD inc, rest, len;
UWORD autoindent;
register UWORD 1;
register struct Zline *z;
pI
actualEditor->buffer;
= inc;
x=
')
II (*pl
TAB)
pl++;
1--;
}
i f (1)
= 1;
/* deconvert line: */
p2 = buf + (len = deconvertLine(buf,actualEditor->buffer,inc;
i f (*p2 = c)
len++;
= newZline(len
z->flags = (actualEditor->actual->flags
if (z
(
& -ZLF_USED);
*pl. = *p2;
pl++;
p2++;
1-;
/* bind line: */
Insert (&actualEditor->zlines, z,actualEditor->actual->pred) ;
596
ABACUS
actualEditor->num lines++;
actualEditor->changed = 1;
/* move rest of buffer line in front: */
/* remember auto indent! */
pI = actualEditor->buffer;
1
= autoindent:
while (--1)
pl++;
/* Old contents remain! */
p2 = ptr;
1 = rest;
if
(actualEditor->autoindent)
1* Spaces at start cot off:
while (l && (*p2 = , '))
*/
p2++;
1-:
*/
*pl
*p2;
pl++;
p2++;
1-;
actualEditor->buflen
pI - actualEditor->buffer;
*pl = actualEditor->buflastchar:
pl++:
1--:
/* determine if new line is a fold marker: */
if (1 = getFoldInc(z))
{
597
actualEditor->minfold = fold;
printFold () ;
}
actualEditor->maxfold = fold;
printFold ( ) ;
ZLINESPTR(actualEditor->wdy) = Zi
actualEditor->wdy =
recalcTopOfWindow(actualEditor->wdy);
*1
ZLINESPTR(O) = Z;
restoreZlinenptr();
1* redisplay window: *1
actuaIEditor->xpos = autoindent;
if (actuaIEditor->xpos <= actuaIEditor->leftpos)
{
ZLINESPTR(O) = ZLINESPTR(l);
ZLINESNR(O) = ZLINESNR(l);
restoreZlinenptr();
actualEditor->wdy--;
}
actuaIEditor->ypos = ZLINESNR(actualEditor->wdy);
printAll () ;
else
i f (1)
{
actuaIEditor->ypos = ZLINESNR(actuaIEditor->wdy);
printAlIO;
else
{
1* it was scrolled: */
if (++actuaIEditor->wdy >= actuaIEditor->wch)
{
598
ABACUS
APPENDIX A VERSION
6 EDIT.C
scrollUpUWORD) 1);
nr = (--actualEditor->wdy) - 1;
actualEditor->ypos = ZLINESNR(actualEditor->wdy);
printLine(ZLlNESPTR(nr),actualEditor->yoff
+ actualEditor->ch*nr);
else
{
actualEditor->wdy) +
ZLINESNR(actualEditor->wdy);
printLine(ZLlNESPTR(nr),actualEditor->yoff
+ actualEditor->ch*nr);
nr--;
printLine(ZLlNESPTR(nr),actualEditor->yoff
+ actualEditor->ch*nr);
printXpos () ;
printypos () ;
printNumLines();
printFlags () ;
return (TRUE);
else
return (FALSE);
else
{
cursorHome () ;
cursorDown () ;
return (FALSE);
}
/*#ENDFD*/
/*#FOLD: getLastWord */
/**************************************
* getLastWord(str,len)
* Returns pointer to last
* word in a string.
* str A String.
* len = buffer length.
**************************************/
UBYTE *getLastWord(str,len)
register UBYTE
*str;
599
register UWORD
AMIGA
len;
if c
= *--str) == .)
lwb = str;
break;
else i f ( ! c >= 'A')
II (c >= 'a') &&
II (c >= '0') &&
II (c >= 192) II
i f (lwn = NULL)
lwn = str;
' )
i f (lwb == NULL)
i f (lwn)
lwb = lwn;
else
lwb = end;
else
i f (lwn)
if end - lwb) > MAXWIDTH I 5)
if end - lwn + 5) < (end - lwb
lwb = lwn;
return (lwb + 1);
}
/*#ENDFD*/
/*#FOLD: insertChar */
1******************************************
* insertChar (c)
* Inserts character in buffer.
* FALSE returned if character
* cannot be inserted.
* c= character.
******************************************/
BOOL insertChar(c)
register UBYTE
c;
UBYTE *ptr;
WORD inc, rest;
WORD xadd = 1;
if (! getBufferPointer(&ptr,&inc,&rest
return (FALSE);
if c == TAB) && (actualEditor->tabs
{
if (actualEditor->insert)
{
/* Insert spaces: */
register UBYTE *p1,*p2;
register UWORD num,l;
600
ABACUS
p1 = actuaIEditor->tabstring + actuaIEditor->xpos - 1;
1
= MAXWIDTH - actuaIEditor->xpos;
num = 1;
while (1 && (*++pl
{
xadd++;
1--;
num++;
*--p2
*--pl;
1-;
)
1* Insert spaces: *1
pI = ptr;
1 = num;
while (1)
(
*pl =
pl++;
";
1--;
actuaIEditor->buflen += num;
rest += num;
)
else
{
xadd++;
max--;
}
else
{
&&
actuaIEditor->skipblanks)
(actuaIEditor->xpos >= actuaIEditor->rm
601
= ptr + rest;
= pI;
1 = rest;
while (1)
{
*p2 = *--p1;
p2--;
1--;
*ptr
c;
/* Lenght + 1 */
actualEditor->buflen++;
rest++;
else
return (FALSE);
else
{
*ptr = c;
i f (rest =
0)
actualEditor->buflen++;
rest = 1;
1* move cursor: */
while (xadd)
{
i f (! cursorRight (
xadd--;
break;
1* Line output: *1
printLine(actualEditor->actual,
actualEditor->yoff + actualEditor->ch*actualEditor->wdy);
return (TRUE);
602
ABACUS
ApPENDIX A VERSION
EDIT.C
I*#ENDFD*I
deleteLine *1
1****************************
I*#FOLD:
*
*
*
*
deleteLine:
delete the line,
that the cusor is on.
FALSE returned if
cannot be deleted
****************************1
BOOL deleteLine()
{
actualEditor->actual = NULL:
actualEditor->bufypos
0:
actualEditor->buflen = Oi
actuaIEditor->changed = Ii
actuaIEditor->num_lines--:
1* Reconstruct folding: *1
if (line->flags & ZLF FSE)
-Ii
z->flags+l)
&
ZLF]OLD
1 = 1;
else
1
= 0;
i f (l)
while (z->succ)
{
1* Note
prevnr=
prev '"'
next '"'
nextnr=
603
1* Security check: *1
while prev == NULL) && (next
{
==
1 = 1;
1* Window completely output *1
actualEditor->minfold--;
printFold 0 ;
prevnr=
prey =
next =
nextnr=
ZLlNESNR(actualEditor->wdy);
prevLine (line, &prevnr);
nextLine (line, &nextnr);
ZLlNESNR(actualEditor->wdy):
~=
NULL)
break;
1* delete line: *1
deleteZline(line);
1* pointer to actual line reset for recalc: *1
i f (next)
{
ZLlNESPTR(actualEditor->wdy)
ZLlNESNR(actualEditor->wdy)
= next;
= nextnr;
else
(
ZLlNESPTR(actualEditor->wdy)
ZLINESNR(actualEditor->wdy)
prey;
= prevnr;
else
(
1* with scrolling: *1
register UWORD oldwdy;
oldwdy = actualEditor->wdy;
actualEditor->wdy = recalcTopOfWindow(actualEditor->wdy);
restoreZlinenptr();
i f (next)
{
604
ABACUS
(LONG)actualEditor->xscr,
(LONG)actualEditor->yscr):
printLine(ZLINESPTR(actualEditor->wch - 1),
actualEditor->yoff
+ actualEditor->ch*(actualEditor->wch - 1:
else
if (oldwdy == actualEditor->wdy)
{
else
printLine(ZLINESPTR(oldwdy),actualEditor->yoff
+ actualEditor->ch*oldwdy):
cursorOnText () :
}
printXpos () ;
printYpos 0 ;
printNumLines():
printFlags 0 ;
return (TRUE);
}
else
return (FALSE);
}
/*#ENDFD*/
/*#FOLD: initFolding */
/**********************************
* initFolding:
* calculate the fold level of all
* lines from text start.
**********************************/
void initFolding()
{
z->flags 1= ZLF_FSE;
level += 1;
z =
Z->SllCC;
/*#ENDFD*/
605
AMIGA
Version 6 Cursor.c
/**************************
* Module: Cursor.c
***************************/
/*#FOLD: Includes */
/************
**
Includes:
*************/
#inc1ude <exec/types.h>
#inc1ude <intuition/intuition.h>
#inc1ude "src/Editor.h"
/*#ENDFD*/
/*#FOLD: Defines */
/***********
* Defines:
***********/
#define
#define
#define
#define
#define
#define
#define
#define
#define
CSI Ox9B
CUU 'A'
CUD 'B'
CUF 'C'
CUB 'D'
SU 's'
SD 'T'
CHOME 'A'
CEND '@'
#define ESC 27
#define UNDO '?'
#define CFOLD 6
#define CENDF 5
#define DELLINE 2
#define DEL 127
#define BS 8
#define TAB 9
#define LF 10
#define CR 13
#define REPCOM 7
/*#ENDFD*/
/*#FOLD: External Functions */
/**********************
* External Functions:
**********************/
void SetDrMd(),RectFill(),printAl1(),Disp1ayBeep(),undoLine();
606
ApPENDIX A VERSION
ABACUS
6 CURSOR.C
void ScroIIRaster(),cursorOnText(),printXpos(),printYpos();
void printFold(),printFlags(),cursorOnText();
struct Zline *nextLine(),*prevLine();
BooL insertChar(),deleteChar(),backspaceChar(),insertLine(),deleteLine();
BOOL ActivateGadget();
UBYTE *executeCommand();
/*#ENDFD*/
/*#FOLD: External Variables */
/*********************
* External Variables:
**********************/
extern struct Editor *actuaIEditor;
extern awORD SplitDec;
extern struct Gadget G Command;
/*#ENDFD*/
/***************
**
*
Functions: *
***************/
/*1IFOLD: restoreZlinenptr */
/************************************
* restoreZlinenptr:
*
*
*
*
*
*************************************/
void restoreZlinenptr()
{
register UWORD n;
register struct Zline **zptr,*z;
register awORD *pnr;
awORD znr;
for (n
*zptr++
*pnr++
(z = nextLine(z,&znr;
znr;
/*1IENDFD*/
/*#FOLD: Cursor */
1**********************************
* Cursor:
*
607
AMIGA
**********************************1
void Cursor 0
{
I*#ENDFD*I
I*#FOLD: scrollRight *1
1*******************************
* scrollRight(nurn):
**
*******************************1
void scrollRight(nurn)
register UWORD
num;
{
1* Scrolling: *1
ScrollRaster(actualEditor->rp,
-num * (LONG)actualEditor->cw,OL,
(LONG)actualEditor->xoff, (LONG)actualEditor->yoff,
(LONG)actualEditor->xscr, (LONG)actualEditor->yscr);
1* now list printAll(): *1
wcw = actualEditor->wcw;
actualEditor->wcw = nurn + 1;
SplitDec = 1;
y
actualEditor->yoff;
db
actualEditor->ch;
count = actualEditor->wch;
1* Output line: *1
for (z = actualEditor->zlinesptr; count-- && (*z != NULL);
608
ABACUS
ApPENDIX A VERSION
CURSOR.C
z++, Y += chI
printLine(*z,y);
SplitDec = 0;
1* Output line: *1
for (z = actualEditor->zlinesptr; count-- && (*z != NULL);
z++, y += chI
printLine(*z,y);
actualEditor->wcw
= wcw,
actualEditor->leftpos= leftpos;
actualEditor->xoff = xoff;
}
l*lIENDFD*1
l*lIFOLD: scrollLeft *1
1*******************************
* scrollLeft(num):
**
**
*******************************1
void scrollLeft(num)
register UWORD num;
{
1* Scrolling: */
ScrollRaster(actualEditor->rp,
num * (LONG)actualEditor->cw,OL,
(LONG)actualEditor->xoff, (LONG)actualEditor->yoff,
(LONG)actualEditor->xscr, (LONG)actualEditor->yscr);
1* now list printAll(): *1
wcw
actualEditor->wcw;
leftpos = actualEditor->leftpos;
xoff
= actualEditor->xoff;
actuaIEditor->wcw
= num + 1;
609
AMIGA
= aetualEditor->yoff;
aetuaIEditor->eh;
count = aetuaIEditor->wch;
ch
1* Output line: *1
for (z ~ actuaIEditor->zlinesptr; count-- && (*z 1= NULL);
z++, y += chI
printLine(*z,y);
actualEditor->wcw
= wcw;
actuaIEditor->leftpos= leftpos;
actualEditor->xoff = xoff;
}
I*#ENDFD*I
I*#FOLD: scrollUp *1
1*******************************
* serollUp(nurn):
*
*
*
*
**
z = ZLlNESPTR(n);
znr = ZLINESNR(n);
while (++n < nurn)
z = nextLine(z,&znr);
else
(
z = ZLINESPTR(n - 1);
znr = ZLINESNR(n - 1);
610
= (z
= nextLine(z,&znr;
ABACUS
ApPENDIX A VERSION
*pnr++
CURSOR.C
znr;
1* Scrolling: *1
ScrollRaster(actualEditor->rp,
OL,hum * (LONG)actualEditor->eh,
(LONG)actualEditor->xoff, (LONG)aetualEditor->yoff,
(LONG)actualEditor->xser, (LONG)aetualEditor->yscr);
1* zlinesptr/nr recalculated: *1
zptr = actualEditor->zlinesptr;
zold = &(ZLINESPTR(num;
pnr = aetualEditor->zlinesnr;
oldnr = &(ZLINESNR(num;
for (n = 0; n <= weh - num; n++)
{
*zptr++
*pnr++
*zold++;
*oldnr++;
z = *--zold;
zold = zptr - 1;
znr = *--oldnr;
oldnr = pnr - 1;
while (n <= wch)
{
*zptr++ = (z = nextLine(z,&znr;
*pnr++ = znr;
nt+;
1*ItENDFD*1
I*#FOLD: scrollDown *1
1*******************************
* scrollDown(num):
* nurn
number of lines.
*******************************1
611
AMIGA
UWORD znr,*pnr,*oldnr;
if (nurn >= wch)
{
/* Scrolling: */
ScrollRaster(actualEditor->rp,
OL,-nurn * (LONG)actualEditor->ch,
(LONG)actualEditor->xoff, (LONG)actualEditor->yoff,
(LONG)actualEditor->xscr, (LONG)actualEditor->yscr);
/* zlinesptr recalculated: */
zptr = &(ZLINESPTR(wch));
zold = &(ZLINESPTR(wch - num));
pnr = &(ZLINESNR(wch));
oldnr = &(ZLINESNR(wch - num));
for (n = 0; n <= wch - num; n++)
{
*zptr-*pnr--
*zold--;
*oldnr--;
= *++oldnr,
num; n; n--)
*zptr-- = (z = prevLine(z,&znr));
*pnr-- = znr;
/* OUtput new line: */
for (n = nurn, y = actualEditor->yoff; (n && (*zold != NULL));
n--, zold++, y += actuaIEditor->ch)
printLine(*zold,y);
}
/*#ENDFD*/
/*#FOLD: cursorLeft */
/****************************************
* cursor1eft:
612
ApPENDIX A VERSION
ABACUS
CURSOR.C
****************************************1
BOOL cursorLeft()
{
if (actuaIEditor->xpos > 1)
{
actuaIEditor->xpos--;
if (actuaIEditor->xpos <z actualEditor->leftpos)
scroIIRightUWORO)l);
printXpos () ;
return (TRUE);
else
retum (FALSE);
}
I*I!ENDFD*I
I*I!FOLD: cursorRight *1
1*****************************************
** cursorRight:
** moce cursor one position
* right, False returned if
*
cursor,
is in last column.
******************************************1
BOOL cursorRight ()
{
actuaIEditor->xpos++;
if (actuaIEditor->xpos
>= (actuaIEditor->leftpos + actuaIEditor->wcw
scrollLeft ( (UWORD) 1) ;
printXpos () ;
return (TRUE);
}
else
retum (FALSE);
}
I*I!ENDFD*I
I*I!FOLD: cursorUp *1
1*****************************************
**
cursorUp:
**
*
*
******************************************1
BOOL cursorUp ()
{
UWORO help;
if (actualEditor->wdy)
actuaIEditor->wdy--;
else
613
AMIGA
if (prevLine(ZLlNESPTR(O),&help
scroIIDownUWORD)I);
else
return (FALSE);
actuaIEditor->ypos
printYpos 0 ;
= ZLlNESNR(actuaIEditor->wdy);
retum (TRUE);
}
/*#ENDFD*/
/*#FOLD: cursorDown *1
/******************************************
* cursorDown:
**
******************************************/
BOOL cursorDown()
{
if (ZLINESPTR(actuaIEditor->wdy + 1
{
scroIIUpUWORD)I);
actualEditor->wdy--;
}
actuaIEditor->ypos
printYpos () ;
= ZLINESNR(actualEditor->wdy);
return (TRUE);
else
return (FALSE);
}
/*#ENDFD*I
I*#FOLD: halfPageUp *1
/**********************************
* halfPageUp:
**
**
***********************************/
BOOL halfPageUp()
{
614
ABACUS
ApPENDIX A VERSION
CURSOR.C
if (! (z = prevLine(z,&help)
break;
i f (n)
{
scroIIDown(n);
actuaIEditor->ypos
printYpos () ;
ZLlNESNR(actuaIEditor->wdy);
return (TRUE);
}
else
return (FALSE);
}
l*flENDFD*1
l*flFOLD: halfPageDown *1
1**********************************
**
halfpageDown:
**
* down. The
* half page
**********************************1
BOOL halfPageDown ()
{
scroll Up (n) ;
actuaIEditor->ypos - ZLlNESNR(actuaIEditor->wdy);
printYpos();
return (TRUE);
}
else
return (FALSE);
}
I*#ENDFD*I
I*#FOLD: cursorHome *1
1*************************************
*
**
cursorHome:
move cursor to first line.
*************************************1
BOOL cursorHome()
615
AMIGA
actuaIEditor->xpos z 1;
if (actuaIEditor->leftpos)
scroIIRight(actuaIEditor->leftpos);
printXpos 0 ;
return (TRUE);
}
l*flENDFD*1
I*#FOLD: cursorEnd *1
/***************************************
* cursorEnd:
ptr
tab
pos
= UBYTE *) (z + 1));
actualEditor->tabstring;
= 1;
=
tab++;
pos++;
} while *tab) && (pos < MAXWIDTH));
else
{
tab++;
pos++;
}
else
pos
616
len + 1;
ABACUS
ApPENDIX A VERSION
6 CURSOR.C
else
return (FALSE);
actualEditor->xpos = pos;
if (pos >= (actualEditor->leftpos + actualEditor->wcw
scrollLeft(pos + 1 - (actualEditor->leftpos
+ actualEditor->wcw;
else if (pos <= actualEditor->leftpos)
scrollRight(actualEditor->leftpos + 1 - pos);
printXpos () ;
return (TRUE);
}
/*#ENDFD*/
/*#FOLD: recalcTopOfWindow */
/***********************************
**
recalcTopOfWindow(y)
**
UWORD recalcTopOfWindow(y)
register UWORD
y;
{
= ZLlNESPTR(y);
znr = ZLINESNR(y);
while (y)
{
2P
= z;
znrp = znr;
if z = prevLine (z, &znr
break;
== NULL)
y--;
i f (y)
{
ZLINESPTR(O)
ZLINESNR(O)
= zp;
= znrp;
else
{
ZLlNESPTR (0) = z;
ZLlNESNR(O) = znr;
return (oldy - y);
}
/*#ENDFD*/
617
I*#FOLD: eursorTop *1
1*****************************
*
* eursorTop:
*
* Set Cursor to to of
* first line.
Text
*****************************1
BOOL cursorTop()
{
z = ZLINESPTR(O);
n = ZLINESNR(O);
ent = 0;
while (z = prevLine(z,&n
ent++;
i f (ent)
serollDown(ent);
aetualEditor->ypos
printypos 0 ;
ZLINESNR(aetualEditor->wdy
0);
retum (TRUE);
}
I*#ENDFD*I
I*#FOLD: cursorBottom *1
1*****************************
*
*
eursorBottom:
*
* Set Cursor to bottom of text
* last line.
*****************************1
BOOL cursorBottom()
{
(z
:.
ZLlNESPTR (weh
aetualEditor->weh
- 1
n
ZLlNESNR(weh);
ent = 0;
while (z = nextLine(z,&n
ent++;
i f (ent)
scrollUp(ent);
aetualEditor->ypos
ZLlNESNR(aetualEditor->wdy
= weh);
ZLlNESNR(aetualEditor->wdy
= weh);
else
{
aetualEditor->ypos
cursorOnText 0 ;
618
ABACUS
ApPENDIX A VERSION
CURSOR.C
printYpos () ;
return (TRUE);
}
/*#ENDFD*/
/*#FOLD: enterFold */
/***************************
**
enterFold:
**
***************************/
void enterFold()
{
actuaIEditor->maxfold++;
if (z = ZLINESPTR(actuaIEditor->wdy
if z = z->succ)->succ)
/* If there is a following line: */
if (ZLINESPTR(actuaIEditor->wdy)->flags & ZLF_FOLD)
< (z->flags & ZLF FOLD
&& z->flags & ZLF_FOLD) == actuaIEditor->maxfold
/* When this begins a new fold: */
actuaIEditor->minfold = actuaIEditor->maxfold;
ZLINESPTR(O) = z;
ZLINESNR(O)
= ZLINESNR(actuaIEditor->wdy) + 1;
actuaIEditor->wdy = 0;
actuaIEditor->wdy = recalcTopOfWindow(actuaIEditor->wdy);
restoreZlinenptr();
printAlI () ;
actualEditor->ypos = ZLINESNR(actuaIEditor->wdy);
printYpos () ;
printFold () ;
}
/*#ENDFD*/
/*#FOLD: exitFold */
/*****************************
* exitFold:
**
*****************************/
void exitFoldO
{
actuaIEditor->maxfold--;
619
AMIGA
/*#ENDFD*/
/*#FOLD: handleKeys */
/*******************************
* handleKeys(buf,len):
*
*
*
*
* buf
* len
*******************************1
BOOL handleKeys(buf,len)
register UBYTE *buf;
register WORD
len;
{
first = *buf;
buf++i
len--;
if first == CSI) && (len> 0
{
len-;
switch (*buf++)
{
case CUU:
cursorUp () ;
break;
case CUD:
cursorDown 0 ;
break;
case CUF:
cursorRight () ;
break;
case CUB:
620
ABACUS
ApPENDIX A VERSION
6 CURSOR.C
cursorLeft 0 ;
break;
case sU:
halfpageDown 0 ;
break;
case SD:
halfPageUpO;
break;
case ':
i f (len> 0)
{
len--;
switch (*buf++)
{
case CHOME:
cursorHome 0 ;
break;
case CEND:
cursorEnd 0 ;
break;
default:
i f (! insertChar ( (UBYTE) CSI) )
DisplayBeep(NULL);
buf -= 2;
len += 2;
}
break;
}
else
goto noCSI;
case UNDO:
if len> 0) && (*buf
==
._.
buf++;
len--;
undoLineO;
break;
}
/* Else: default */
default:
noCSI:
/* No command-Sequence! */
if (! insertCharUBYTE)CSI
DisplayBeep(NULL);
buf-;
len++;
break;
/* switch (first)
*1
1
else
switch (first)
{
case ESC:
1* Rest of buf. transferred:
*1
621
AMIGA
*sibuf++
len--;
= *buf++;
*sibuf = 0;
cnt = (sibuf - actuaIEditor->gc SIBuffer);
actuaIEditor->gc GadgetSI.Bufferpos = cnt:
actuaIEditor->gc-GadgetSI.NumChars = cnt:
actuaIEditor->gc=GadgetSI.DispPos
= 0;
1* Gagdet aktivated: *1
if (!ActivateGadget(&(actuaIEditor->G_Command),
actuaIEditor->window,NULL
DisplayBeep(NULL):
break;
case REPCOM:
{
if
C~mmand),
actuaIEditor->winciow~NULL);
DisplayBeep(NULL);
/* return now: */
return (TRUE):
break;
case CFOLD:
enterFold 0 ;
break:
case CENDF:
exi tFold () :
break:
case DELLlNE:
if (! deleteLine(
DisplayBeep(NULL);
break:
case DEL:
if (! deleteChar(
DisplayBeep(NULL);
622
ABACUS
ApPENDIX A VERSION
6 CURSOR.C
break;
case BS:
if (1 backspaceChar(
DisplayBeep(NULL);
break;
case LF:
case CR:
if (1 insertLine((UBYTE) LF
DisplayBeep(NULL);
break;
default:
if (1 insertChar(first
DisplayBeep(NULL);
/* switch (first) *1
/* while (len) *1
return (TRUE);
}
/*#ENDFD*I
623
AMIGA
Version 6 Output.c
/***********************************
** Module: Output
** Output text in a window.
************************************/
/*#FOLD: Includes */
/************
**
Includes:
*************/
#include <exec/types.h>
#include <intuition/intuition.h>
#include "src/Editor.h"
/*#ENDFD*/
/*#FOLD: Defines */
/***********
* Defines:
***********1
#define CR 13
#define LF 10
#define TAB 9
/*#ENDFD*/
/*#FOLD: External Functions */
/**********************
* External Functions:
**********************1
void SetAPen(),RectFill(),Text(),Move(),SetFont();
struct Zline *nextLine();
/*#ENDFD*/
/*#FOLD: External Variables *1
/*********************
* External Variables:
*********************1
1*********************
**
*
Global Variables:
*********************1
624
ABACUS
ApPENDIX A VERSION
OUTPUT.C
0;
/***************
**
Functions: *
***************/
/*#FOLD: initWindowSize *1
1**********************************
**
*
initWindowSize(ed)
*
*
* ed
Editor structure.
**********************************/
void initWindowSize
(ed)
register struct Editor *ed;
{
w
rp
ed->window;
ed->rp:
~
~
ed->xoff
ed->yoff
(UWORD)w->BorderLeft:
(UWORD)w->BorderTop;
ed->cw
ed->ch
(rp->TxWidth)? rp->TxWidth
8:
(rp->TxHeight)? rp->TxHeight : 8;
ed->wcw
newh
xsize~
ed->ip xpos
= xinc + INFO XPOS INC * xsize;
ed->ip=ypos
= xinc + INFO-YPOS-INC * xsize;
ed->ip numzlines = xinc + INFO NUMOFLINES INC * xsize;
ed->ip-minfold
= xinc + INFO-MINFOLD INC * xsize;
ed->ip-maxfold
~ xinc + INFO-MAXFOLD-INC * xsize;
ed->ip-flags
= xinc + INFO FLAGS INC-* xsize;
ed->ip=topedge
= w->Height-+ ed->G_Info.TopEdge
625
AMIGA
C FOR
ADVANCED PROGRAMMERS
+ ed->gi IText.TopEdge
+ (ULONG)infoFont->tf_Baseline - 1;
/* Now restore the zlineptr field: */
if (newh != ed->wch)
{
*zptr++ - (z - nextLine(z,&znr;
*pnr++ = znr;
ed->wch
newh;
/*#ENDFD*/
/*#FOLD: convertLineForPrint */
/******************************************
**
convertLineForPrint(line,len,w,buf,lc)
**
*
*
*
*
*
******************************************/
UWORD convertLineForPrint(line,len,w,buf,lc)
UBYTE
*line;
regi ster WORD
len;
w;
register UWORD
register UBYTE
*buf;
626
ABACUS
ApPENDIX A VERSION
UBYTE
OUTPUT.C
*lc;
= actualEditor->leftpos;
tab
= line
+ len - 1;
CR)
i f (*tab =
{
len-;
lastchar
, ,.,
else i f (*tab =
LF)
len--;
lastchar ~ , ';
if len) && (*--tab
len--;
else
lastchar
CR
= 183;
else
last char
183;
if (actualEditor->tabs)
{
1* line convert: *1
tab = actualEditor->tabstring;
while len--) && (1 < wI)
if *buf ~ *line++) = TAB)
<b
{
tab++;
(skip)
skip--;
else
if
1++;
*buf++ =
, ,.,
tab++;
i f (skip)
skip--;
else
{
1++;
buf++;
else
{
627
i f (skip)
skip--;
else
{
1++;
*buf++
*line;
line++;
l*ilENDFD*1
l*ilFOLD: printAt *1
1************************************
** printAt(buf,len,x,y,fgpen)
**Prive character string buf a
position
* (x,y) in a window. The character* string is changed!
**
*
*
*
*
************************************1
1* output spaoes: *1
x2
628
x - 1;
ABACUS
ApPENDIX A VERSION
while *buf
== , ')
OUTPUT.C
&& len--)
buf++;
x2 += cw;
SetAPen(rp,BGPEN);
RectFill(rp, (ULONG)x, (ULONG)y, (ULONG)x2, (ULONG)y2);
SetAPen (rp, fgpen);
x
++x2;
else
{
SetAPen(rp,CTRLPEN);
while (CONTROLCODE(*buf) && len--)
*buf++ 1= 64;
1 = buf - ptr;
Text (rp,ptr, l);
SetAPen(rp,fgpen);
}
else
{
/*iENDFD*/
/*#FOLD: printLine */
/************************************
* printLine(line,y)
************************************/
void printLine
(line,y)
register struct Zline *line;
register UWORD
y;
{
629
w = MAXWIDTH - actuaIEditor->leftpos;
p2 = actuaIEditor->buffer + actuaIEditor->leftpos;
pl = buf;
while (w)
{
*pl = *p2;
p1++;
p2++;
w--;
}
else
pl = buf;
*pl = lc;
p1++;
else
convertLineForPrint(line+l,line->len,
w = actuaIEditor->wcw,buf,&lc);
if z = line->succ)->succ)
{
SetAPen(actuaIEditor->rp,FOLDPEN);
pen = FOLDPEN;
else
pen = FGPEN;
else
{
SetAPen(actuaIEditor->rp,FOLDPEN);
pen = FOLDPEN;
printAt(buf,w - SplitDec,actuaIEditor->xoff,y,pen);
630
ABACUS
ApPENDIX A VERSION
OUTPUT.C
if (pen != FGPEN)
SetAPen(actuaIEditor->rp,FGPEN);
else
printAt(buf,w - SplitDec,actualEditor->xoff,y,FGPEN);
else
{
1* if no line
I*#ENDFD*I
I*#FOLD: printAlI *1
1**********************************
**
**
printAll
print new screen.
**********************************1
void printAll ()
{
count
If>
actuaIEditor->yoff;
actuaIEditor->ch;
actualEditor->wch;
actualEditor->rp;
.=
NULL);
/*#ENDFD*I
I*#FOLD: cn utoa *1
/******************************
**
cn_utoa(buf,val,len):
*
631
AMIGA
C FOR
ADVANCED PROGRAMMERS
* buf
* val
* len
=
=
*******************************/
void cn_utoa();
#asm
cseg
. public _cn_utoa
; 4(sp)
; 8 (sp)
;lO(sp)
lea
move. I
moveq
rnove.w
rnove.w
lea
bra.s
4(sp),aO
(aO)+,al
#O,dO
(aO)+,dO
; = Value
(aD) ,d1
= maximum Lenght
o(a1,d1.w), aD ; ~ End of Buffers
.in
.lp:
divu
swap
add.b
move.b
clr.w
swap
.in:
dbeq
beq.s
bra.s
.bl:
rnove.b
.ib:
dbra
.r:
rts
=
=
Buffer,
Value,
maximum Lenght.
#lO,dO
dl
#'0' ,dO
dO,-(aO)
dO.w
VAlue MOD 10
dl
dl
dl, .lp
.ib
.r
#'
,,- (aO)
dl, .bl
#endasm
/*#ENDFD*/
/*#FOLD: printXpos */
/*****************************
* printXpos:
*****************************/
void printXpos ()
{
632
ABACUS
ApPENDIX A VERSION
OUTPUT.C
oldFont = ae->rp->Font;
SetFont(ae->rp,infoFont);
Move (ae->rp,ae->ip xpos,ae->ip topedge);
Text (ae->rp, ae->gi-Text '. INFO- XPOS INC, (ULONG) INFO XPOS LEN);
SetFont (ae->rp, oldFont: ;
}
I*#ENDFD*/
I*#FOLD: printYpos */
/*****************************
*
*
*
printYpos:
Give Ypos of cursor.
*****************************/
void printypos()
{
oldFont = ae->rp->Font;
SetFont(ae->rp,infoFont);
Move(ae->rp,ae->ip-ypos,ae->ip_topedge);
Text(ae->rp,ae->gi Text + INFO YPOS INC, (ULONG) INFO YPOS LEN);
SetFont (ae->rp, oldFont) ;
}
I*#ENDFD*/
/*#FOLD: printNumLines */
/*****************************
**
printNumLines:
**
*****************************/
void printNumLines()
{
/*#ENDFD*I
/*#FOLD: printFold */
/********************************
633
AMIGA
* printFold:
* Gives minfold and maxfold display.
*********************************1
*
void printFold()
{
I*#ENDFD*I
I*#FOLD: printFlags *1
1*****************************
**
printFlags:
**
At Ypos of cursor.
******************************1
void printFlags()
{
else
*fs++
'0';
if (ae->ehanged)
'C';
*fs++
else
*fs++
'c';
if (ae->autoindent)
*fs++
'A';
else
*fs++
'a';
i f (ae->tabs)
*fs++
'T';
else
*fs++
't';
if (ae->skipblanks)
*fs = 'B';
else
*fs = 'b';
634
ABACUS
ApPENDIX A VERSION
6 OUTPUT.C
oldFont = ae->rp->Font;
SetFont(ae->rp,infoFont);
Move (ae->rp,ae->ip_flags,ae->ip_topedge);
Text (ae->rp,ae->gi Text + INFO FLAGS INC, (ULONG)INFO FLAGS LEN);
SetFont (ae->rp, oldFont) r
-}
I*#ENDFD*I
I*#FOLD: printInfo *1
1****************************
*
* printInfo:
*
* Gives new Infoline.
*
****************************1
void printInfo()
{
printXpos () ;
printypos () ;
printNumLines();
printFold () ;
printFlags () ;
}
/*#ENDFD*I
635
AMIGA
C FOR
ADVANCED PROGRAMMERS
Version 6 Memory.c
/***************************************
* Module: Memory
* Organize memory handling.
***************************************/
/*#FOLD: Includes */
/************
** Includes:
*************/
#include <exec/types.h>
#include <intuition/intuition.h>
#include <exec/memory.h>
#include "src/Editor.h"
/*#ENDFD*/
/*#FOLD: Defines *(
/***********
**
Defines:
***********/
* External Functions:
**********************/
* External Variables:
**********************/
extern struct Editor *actualEditor;
/*#ENDFD*/
/***************
* Functions: *
***************/
63b
ABACUS
ApPENDIX A VERSION
MEMORY.C
/*#FOLD: freeMemoryblock */
/*******************************************
* freeMemoryblock(blk)
**
* blk . r1emorybloc....
********************************************/
void freeMemoryblock(blk)
struct Memoryblock *blk;
{
Remove(blk);
FreeMem(blk,blk->length + sizeof(struct Memoryblock;
}
/*#ENDFD*/
/*#FOLD: newerBlock */
/**********************************************
* newerBlock(len):
**
**********************************************/
/*#ENDFD*/
/*#FOLD: searchMemorysection */
1***********************************************
**
searchMemorysection(block,len)
* Searches for memory piece with len length in the block block
* and returns the pointer or zero in case
* of no room.
637
**
block ~ Memoryblock.
* len = Length of line (even).
***********************************************1
else
if 1 >= minI) && (1 > fnd)? fnd->len
1* Search for largest block: *1
fnd = st;
0)
return (fnd);
}
l*lIENDFD*1
l*lIFOLD: ConvertSpstToZline *1
1********************************************
* ConvertSpstToZline(blk,st,len)
**
** blk
*********************************************1
struct Zline *ConvertSpstToZline(blk,st,len)
struct Memoryblock
*blk;
register struct Memorysection
*st;
register UWORD
len;
{
register UWORD 1;
register struct Zline *z;
Z
638
= (struct
Zline *)st;
ABACUS
ApPENDIX A VERSION
MEMORY.C
if (st->len == (1 = EVENLEN(len)))
1* Found piece fits exactly: *1
Remove (st):
else
{
/* distribute piece: */
UBYTE *)st) += (1 += sizeof (struct Zline)):
( st->succ = struct Memorysection *)z)->succ )->pred
( st->pred = struct Memorysection *)z)->pred )->succ
st->len = struct Memorysection *)z)->len - 1;
st:
st;
blk->free -- 1;
z->flags = 0;
z->len = len;
return (z);
}
I*#ENDFD*I
I*#FOLD: getZline *1
1***********************************************
* getZline (len)
*
*
*
*
**
***********************************************1
register UWORD 1:
register struct Memoryblock *blk,*fblk
register struct Memorysection *st,*fst
=
=
NULL:
NULL:
639
AMIGA
return (ConvertSpstToZline(fblk,fst,len:
else
1* no piece found: *1
return (NULL);
}
I*IIENDFD*I
I*IIFOLD: optimalBlock *1
1*********************************************
* optimalBlock(block)
** block ~ Memoryblock.
**********************************************f
void optimalBlock
(blk)
register struct Memoryblock *blk;
{
= blk->freeliste.head;
stl->succ; stl
stl->succ)
= st2->succ)
I*IIENDFD*I
I*IIFOLD: garbageCollection *f
/****************************************
* garbageCollection(len)
*
*
*
*
*
* len
= number
****************************************1
640
ABACUS
ApPENDIX A VERSION
6 MEMORY.C
z->succ)
i f (fz)
i f (fz != ptr)
{
1* pointer correction: *1
*zptr = fz;
break;
st = (struct Memorysection *)ptr;
st->succ = spst.succ;
st->pred = spst.pred;
st->len = spst.len;
spst.succ->pred = st;
641
AMIGA
spst.pred->succ = st;
else
else
l*lIENDFD*1
l*lIFOLD: newZline *1
1*****************************************
**
newZline (len)
**
******************************************1
struct Zline *newZline(len)
UWORD
len;
{
register
register
register
register
if (actualEditor->block.head->succ)
1* blocks already exist: *1
if (z = getZline(len))
return (z);
else
1* Garbage-Collection:line not directly' usable; *1
if (blk = garbageCollection(l = EVENLEN(len)))
if (st = searchMemorysection(blk,l))
return (ConvertSpstToZline(blk,st,len));
else
1* may not actually be encountered I!! *1
return (NULL);
else
1* no reults for Garbage-Collection ->pick new block *1
if (blk = newerBlock(BLOCKSIZE))
{
AddTail(&(actualEditor->block),blk);
return (getZline(len));
642
ABACUS
ApPENDIX A VERSION
MEMORY.C
else
return (NULL);
else
AddTail(&(actualEditor->block),blk);
return (getZline(len;
}
else
return (NULL);
}
I*#ENDFD*I
I*#FOLD: deleteZline *1
1*********************************************
* deleteZline(line)
*
*
*
*
*********************************************1
void deleteZline
(line)
register struct Zline *line;
{
register
register
register
register
= NULL;
fblk = blk;
break;
i f (fblk)
{
I*#ENDFD*I
643
AMIGA
Version 6 Command.c
See Section 4.3.9 for a complete listing of the comand. c module.
644
Index
+L compiler option
15
absolute instructions
64
absolute memory locations
77
ACTIVATE
96,428
ACTIVEWINDOW
263
ALERT_TYPE
258
Alerts
251
AIIocEntryO
372
AIIocMemO
370,372
AIIocRememberO
375,376
application
381
argc (argument count)
14,543
argv
14,543
Anays
41,382
ASCII
21,25,384,365,399
assembler
35,63
AUTO
234
Auto Indent
384,514
auto requester
229,232
AUTOBACKPEN
234
AUTODRAWMODE
234
AUTOFRONIPEN
234
AUTOINDENT
549
AUI'OITEXTFONT
234
AUTOLEFIEDGE
234
AUI'ONEXTIEXT
234
AUTOTOPEDGE
234
Aztec (Manx) C 4, 186, 381, 408, 446,
516
back gadget
BackDrop
Backspace
BCPL
BGPEN
BIGBLOCK
bit fields
bit-maps
bit-planes
BlockPen
BLOCKSIZE
Boolean gadgets
BOOLGADGET
BORDERLESS
BOITOMBORDER
178
87,95
476
3,62
433
422
41,43
126
158
85
422
177
182
86,95
181
breakpoint
buffer
BuildSysRequestO
75
407
234
Capsable
363,368
CD_ASKDEFAULTKEYMAP
365
CD_ASKKEYMAP
363
CD_SETDEFAULTKEYMAP
365
CFOLD
551
CHECKED
292
CHECKIT
292
Chip RAM
369
chip memory
162
ClearMenuStrip
289
CLI
407,543
CLI command access
385
CLlwindow
427
Close gadget
178
close gadget
88,428
CLOSEWINDO
264
CLOSEWINDOW
404
CLOSEWINDOW
428
code segment
64
command line
512
COMMSEQ
292,299
compiler
35,381
compiler errors
36
compiler options
36
144,449
COMPLEMENT mode
console.device
337
console.device flags
266
ConsoleReadMsg
338
ConsoleWriteMsg
338
Control characters 343,384,433,474
341
control sequence
385
control structures
control structures.
54
433
CONlROLCODE
coordinate
153
CPTR pointers
62
341,462,504
CSI
C1RLPEN
433
Cursor display
449
Cursor movement
513
445
cursor
cursor movement
449
645
INDEX
CURSORHOME
custom gadgets
custom requester
custom screen
AMIGA
551
177
234
85, 123
data segment
64
DEADEND_ALERT
252,255,258
debug post mortem
75
debugger
74,471,473
Delete character
476
Delete commands
513
Delete line
476
Deleting Lines
422
DELTAMOVE
264,278
DetailPen
85
development stage
381
DEVICE STATUS REPORT
346
devices
262
DIGIT
535
disk flags
281
DISKINSERTED
265
DISKREMOVED
265,281
double linked list
385,386
drag bar
88
dynamic arrays
78
editor commands
environment variables
ERROR_CLASS
Exchange commands
ExecMessage
505
7
259
513
268
Fast RAM
369
FGPEN
468
file attribute
17
Find commands
513
flags
266
FOLD
432
Fold levels
447
fold
496
fold end mark
445
fold mark
468
fold starting mark
445
Folding
384,432,445,446
FOLDPEN
468
FOLLOWMOUSE
264
font preference flags
146
fonts
145
FreeRememberQ
375,376
646
FreeSysRequestO
front gadget
FSE_LEN
FSE_SIG
functions
234
178
495
495
59
GADGDISABLED
180
gadget border
171
gadget flags 94, 180,264,266
gadget IDCMP flag
271
gadget manipulation
263
GADGETDOWN
263
gadgets
83,177,271
GADGETUP
264,515,541,544
GADGHBOX
179
GADGHCOMP
179
GADGHIGHBITS
179
GADGHIMAGE
179
GADGIMAGE
180
GADGIMMEDIATE
181
GADGIMMEDIATE
263
GAGDHNONE
179
garbage collection
409,535
GetMsgO
269
GIMMEZEROZERO 87,95, 104, 114,
181,428
global symbol
48
goto
59
graphics
158
graphics chip
162
GRELBOTIOM
179
GRELHEIGHT
180
GRELRIGHT
179
GRELWIDTH
179
Guru Meditation
258,385
Guru number
258
GZZGADGET
182
HAM
handleKeys
HIGHBOX
HIGHCOMP
HIGHFLAG
HIGHFLAGS
HIGHIMAGE
HIGHITEM
HIGHNONE
hunks
125
449
292,301
292
301
292
292,300
292
292,301
71
ABACUS
INDEX
144
144
70
keyboard input
keyboard menu item
keyboard table
KeyMap
Key Map Types
Kickstart ROM
338
299
361
363
367
369
large code
large data 3
Lattice C
LEFTBORDER
libraries
Line commands
line output
link
37, 70
7, 70
4
181
6,89,404,543
514
152
408
linked list
linker
linker options
Local variables
local symbol table
long variables
383,385
35,67
71
60,509
48
15
514
Macro command
12
macro definition
macros
385
main loop
404
make utility
9
9,393
makefile
137
MakeScreenO
MEMF_CHIP
370
MEMF_CLEAR
370
MEMF_FAST
370
MEMF_PUBLIC
370
Memory Management
408
memory
383
memory classes
53
memory management
369
Menu flags
266
menu reading
264
menu strip
283,287
menu structure
288
276
MENUOOWN
MENUENABLED
288
290,300
Menultem structure
MENUPICK
264,274
MENUPICK
321
Menus
274,283
MENUTOGGLE
305
276
MENUUP
MENUVERIFY
264
MessageKey
267
MessagePort
267,398
288
MIDRAWN
missing semicolon error
36
266
ModifyIDCMPO
266
Mouse flags
276
mouse
264,277
mouse status
MOUSEBUTIONS 264,277,466,543
MOUSEMOVE
264
107
MoveWindowO
268
MsgPort structure
MutualExclude
305
647
INDEX
AMIGA
NEWPREFS
265
NEWSIZE
263,429
NewWindow structure
90
No Intuition/IDCMP message flags 266
NOCAREREFRESH
95
numeric keypad
279
object me
octal code
Operating system programming
output
Overwrite mode
394
383
381
143
474
PC-relative instructions
65, 70
pointer assignments
62
Pointers
47
PowerWindows
325
Preferences change flags
266
PrintIText
147,429,518
program translation
40
programmability
384
PROPGADGET
182
Proportional gadgets
178
proportional character sets
430
proportional gadget
189
puts function
543
Quit command
514
RAM disk
408,544
RAWKEY 265, 278, 337, 361, 399,
506,537
RAWKEY diagram
362
recoverable Alert
233
RECOVERY_ALERT
252,258
Redirection
35
REFRESHWINDOW 263, 429, 420,
436
register storage class
53
register variables
512
registers
53
relative instructions
64
relocatible code
63
RELVERIFY
181, 186, 264
RELVERIFY flag
514
REMOUSE
95
REPEAT
506
648
Repeat command
Repeatable
REPORTMOUSE
REQCLEAR
REQSET
Requester flags
requester structure
Requesters
REQVERIFY
resolution
retumvalue
RIGHTBORDER
RMB1RAP
RMB1RAP
ROM fonts
screen refreshing
screen types
screens
SCRGADGET
script file
Scrolling
SELECTOOWN
SELEC1ED
SELECTUP
Set commands
SetMenuStrip
SIMPLE_REFRESH
single-stepping
size gadget
SIZEBOTTOM
SIZEBRIGHT .
SIZEVERIFY
SizeWindowO
sizing gadget
SKIPBLANKS
small code
small data
SMART_REFRESH
source code
source code utilities
src/command.c
src/Cursor.c
src/Editor.c
src/Editor.h
src/Memory.c
src/OutpulC
src/feslc
stack
stack overflow
514
368
264
264,273
264,273
266
235
229,251,273
264,274
125
61
181
96
276
145
428
126
83, 123
182
9
445,446,497
276
180
276
514
289
86,94,263,428
75
428
94
94
263,273
108
88
535
70
70
86,95,263,428
381
325
522
451
400
399
410
436
419
49
49
INDEX
ABACUS
StandardRequest
339
start of block
512
startup code
68
startup-sequence
6
static variables
53
STRGADGET
182
String gadget
178,195,514
submenu
302
SUBSYSlEM_CODE
258
super-high-resolution screen
138
SUPER_BITMAP
86, 88, 95, 428
switch case
55, 505, 535
symbol table 37,41,44,61,64,68, 74
SYSGADGET
182
system gadgets
116, 177
system requester
229
WINDOWDEPTH
WINOOWDRAG
WindowLimitsO
WindowPort
WINOOWSIZING
WindowToBackO
WindowToFrontO
word processors
Workbench message flags
WRI1EMODE
Zeditor
94
94
109
267
94
110
110
466
266
549
384,446
Tab commands
TABMODE
Tabs
Text
Text output
text entry
timer.device flags
TOGGLESELECT
topaz.font
514
549
384,446,474,514
429
143,497
474
266
180
146
TOPBORDER
181
266
tIackdisk.device flags
type conversions
45
Undo command
Undo function
union
UNIX
user-friendly
UserPort
video chip
514
383,476
43
3
383
267,268,269
265,278,337,399
40
125
WaitO
while loop
Window flags
window
window flags
window title
window types
WINDOWCLOSE
270
54
266,272
383,398,427,445
93
93
95
94
VANILLAKEY
variable types
649
Optional Diskette
Amlga C for
Advanced
Programmers
Optional diskette
For your convenience, the program listings contained in this book are
available on an Amiga formatted floppy diskette. You should order the
diskette if you want to use the programs, but don't want to type them in from
the listings in the book.
All programs on the diskette have been fully tested. You can change the
programs for your particular needs. The diskette is available for $14.95 plus
$2.00 ($5.00 foreign) for postage and handling.
When ordering, please give your name and shipping address. Enclose a
check, money order or credit card information. Mail your order to:
Abacus
5370 52nd Street SE
Grand Rapids, MI 49512
Or for fast service, call 616-698-0330.
Credit Card orders only 1-800-451-4319.
New Software
TextPro
AMIGA
TextPro AMIGA upholds the true spirit of !he AMlOA:
it's powerful, it has a surprising number of "extra"
features, but it's also very easy to use. TextPro
AMIGA-the Ideal AMlGA word processor that proves
just how easy word processing can be. You can write
your fmt documents immediately, with a minimum of
learning-without even reading the manual. But
TextPro AMIGA is much more than a beginner's
package. Ultra-fast onscreen formatting, graphic merge
capabilities, automatic hyphenation and many more
features make TextPro AMIGA ideal for !he
professional user as well. TextPro AMIGA features:
'--------------=----'
7extPro
AM/GA'
$79.95
BeckerText
AMIGA
This is one program for BIiwI1 AMloA owners.
BeckerText Amiga is more than a word processor. It
has all the features ofTextPro AMIGA, butit also has
features that you might not expect:
FastWYSIWYG formatting
Calculations within a text-like having a spreadsheet
program anytime you want it
Templates for calculations in columns
Line spacing options
Auto-hyphenation and Auto-indexing
Multiple~olumn printing, up to 5 colunms on a single
page
Online dictionary checks spelling in text as it's written
Spell checker for interactive proofing of documents
Up to 999 characters per line (with scrolling)
Many more features for the professional
BeckerText AMIGA
is a vital addition for
C programmers-it's
an extremely flexible
C editor. Whether
you're deleting,
adding or duplicating
AMIGA"
a block of C sourcecode, BeckerText
AMIGA does it all,
automatically. And
the online dictionary
acts as a C syntax
checker and finds
syntax errors in a
flash.
BeckerText AMIGA. When you need more from your
word processor than just word processing.
8edferText
--
$150.00
DataRetrieve
AMIGA
Imagine, for a moment, what the perfect database for
your AMIOA would have. You'd want power and speed,
for quick access to your information. An unlimited
amount of storage space. And you'd want it easy to
use-no baming commands or file structures-with a
graphic interface that does your AMIoAjustice.
Enter DataRetrleve AIIIIGA. It's unlike any other
database you can buy. Powerful, feature-packed, with
the capacity for any business or personal applicationmailing lists, inventory, billing, etc. Yet it's so simple to
use, it's startling. DataRetrleve AIIIIGA'S drop-down
menus help you to define files quickly. Then you conveniently enter information using on-screen templates.
DataRetrieve AIIIIGA takes advantage of the Amiga's
multi-tasking capability for optimum processing speed.
DataRetrleve AIIIIGA features:
Open eight files simultaneously
Password protection
Edit files in memory
Maximum of 80 index fields with variable preciSion
(1-999 characters)
Convenient search/select criteria (range, AND/OR
compari sons)
Text, date, time, numeric and selection fields, IFF file
reading capability
Exchange data with other software packages (for form
letters, mailing lists, etc.)
Control operations with keyboard or mouse
Adjustable screen masks, up to 5000 x 5000 pixels
Insert graphic elements into screen masks (e.g.,
rectangles, circles, lines, patterns, etc.)
Screen masks support different text styles and sizes
Multiple text fields with word make-up and
formatting capabilities
Integrated printer masks and list editor.
Maximum filesize 2 billion characters
Maximum data record size 64,000 characters
Maximum data set 2 billion characters
Unlimited number of data fields
Maximum field size 32,000 characters
DataRetrieve AIIIIGA -it'll handle your data with the
speed and easy operation that you've come to expect
from Abacus products for the AMIOA.
Suggested retail price:
$79.95
AssemPro
AMIGA
AssemPro AIIIIGA lets every Amigaownerenjoy the
benefits offast machine language programming.
Because machine language programming isn't just for
68000 experts. Assem Pro AIIIIGA is easily learned and
user-friendly-it uses Amiga menus for simplicity. But
AssemPro AIIIIGA boasts a long list of professional
features that eliminate the tedium and repetition of MIL
programming. Assem Pro AIIIIGA is the complete
developer's package for writing of 68000 machine
language on the Amiga, complete with editor, debugger,
disassembler and reassembler. AssemPro AIIIIGA is the
perfect introduction to machine langage development
and programming. And it's even got what you 68000
experts need.
AssemPro AIIIIGA features:
Written completely in machine language, for ultra-fast
operation
Integrated editor, debugger, disassembler, reassembler
Large operating system library
Runs under CLI and Workbench
Produces either PC-relocatable or absolute code
Macros possible for nearly any parameter (of different
types)
Error search function
Cross-reference list
Menu-controlled conditional and repeated aSfo~mbly
Full 32-bit arithmetic
Debugger with 68020 single-step emulation
Runs on any AMIGA with SI2K and Kickstart 1.2.
Suggested retail price:
$99.95
Amiga
computers
Professional DataRetrieve
The Professional Level
Database Management System
Professional DatJIRetrieve, for the Amiga 500/1000/2000,
isa friendly easy-to-opcrate professional level data management package with the features most wanted in a relational
data bft3e syslem_
Professional DataRetrieve has complete relational data
mangagement capabilities. Define relationships between
different files (one to one, one to many, many to many).
Ch!lllge relations withOllt file reorgani7A1tion.
Professional DatJIRetrieve includes an extensive programming laguage which includes more than 200 BASIC-like
commands and functions and integrated program editor.
Design custom u~r interfaces with pulldown menus, icon
selection, window activation and more.
Professional DataRetrleve can perform calculations and
searches using complex mathematical comparisons using
over 80 functions and constants.
Professional DataRetrieve is a friendly, easy to operate
programmable RELATIONAL data base system. PDR includes PROFlL, a programming language similar to BASIC.
You can open and edit up to 8 files simultaneously and the
size ofYOIIrdata rlClds, records and files are limited only by
your memory and disk storage. You have complete interrelation between files which can include IFF graphics. NOT
COpy PROJECTED. ISBN 1-55755..0484
MORE features of Professional DataRdrleve
Easily import data from other databoscs ....file compatible
with standanl Data Retrieve....supPOrlS multitasking...design
your own custom forms with the completely integrated
printer mask editor... .includes PROFIL programming language that allows the programmer to custom tailor his database requirements.
MORE features ofPROFlL include:
Open Amiga devices including the console, printer,
serial and the CLI.
Create your own programmable requestors
Complete error trapping.
Built-in compiler and much, much more.
$295.00
---
Titt
PmfmimrJ/
lml
Features
Up to 8 files can be ed~ed simuHaneously
Maximum size of a data field 32,000 characters
(text fields only)
Maximum number of data fields limited by RAM
Maximum record size 01 64,000 characters
Maximum number of records diSk dependent
(2,000,000,000 maximum)
Up to 80 index fields per file
Up to 6 field types - Text, Date, Time, Numeric,
IFF, Choice
Unlimijed number 01 searches and subrange
cr~eria
....DOS
.. "{fA
Abacus Amlga
...
:
.
:
~Toa
wX :
.. .. .. .. . . .. .. . ... .. ..
Essential software tools
for all Amiga users.
AbacusllHill
5370 52nd Sireel SE Grand Rapids, M149508 Phone (616) 6980330 Telex 709101 Facsimile (616) 6980325
Companion Diskette
Amlga C for
Advanced
Programmers
Companion diskette
For your convenience, the program listings contained in this book are
available on an Amiga fonnatted floppy diskette. You should order the
diskette if you want to use the programs, but don't want to type them in from
the listings in the book.
All programs on the diskette have been fully tested. You can change the
programs for your particular needs. The diskette is available for $14.95 plus
$2.00 ($5.00 foreign) for postage and handling.
When ordering, please give your name and shipping address. Enclose a
check, money order or credit card infonnation. Mail your order to:
Abacus
5370 52nd Street SE
Grand Rapids, MI 49512
3-~
Volume 3
Volume 4
Save Time and Money!-Optional program disks are available lor all our Amiga relerence
books (except Amiga lor Beginners). AH programs listed in the book are on each respective
disk and will save you countless hours 01 typing! $14.95
Volume 5
ISBN 0-916439-88-7
EXEC Structure
Multijasking functions
I/O management through devices and I/O request
Interrupts and resource management
RESET and ijs operation
DOS libraries
Disk Management
Detailed infonmation about the CLI and Hs commands
Volume 6
Save TIme and MoneylOptional program disks are available for all our Amiga reference
books (except Amiga for Beginners). All programs listed in the book are on each respective
disk and will save you countless hours of typing! $14.95
Volume 8
Volume 9
ISBN 1-55755-042-5
Save Time and Money!-Optional program disks are available for all our Amiga reference
books (except Amiga for Beginners). All programs listed in the book are on each respective
disk and will save you countless hours of typing! $14.95
Volume 10
Volume 11
Save Time and Money!Optional program disks are available for all our Amiga reference
books (except Amiga for Beginners). AH programs listed in the book are on each respective
disk and will save you countless hours of typingl $14.95
$14.95
'727
Presenting ...
UiHHIH~1
ISBN 155755-049-2
1-55755-021-2
Vol. 2
0-916439-87-9
$16.95
$24.95
Vol. 3
1-55755-044-1
119.95
Vol. 4
1-55755-025-5
$19.95
Vol. 5
0-916439-88-7
119.95
Vol. 6
1-55755-034-4
$34.95
Vol. 7
1-55755-047-6
$34.95
Vol. 8
1-55755-041-7
119.95
Vol. 9
1-55755-042-5
$29.95
1-55755-045-X
$19 .95
1-55755-046-8
$34.95
1-55755-051-4
$19 . 95
1-55755-052-2
$34.95
1-55755-057-3
119.95
1-55755-049-2
$ 9.95
ISBN 1-55755-046-8