[comp.sys.mac.programmer] Wanted: Code for simple text interface

bskendig@phoenix.Princeton.EDU (Brian Kendig) (07/31/89)

A while ago, I made a thorough search for a command-line interface for a
Macintosh.  The only one I found did not quite suit my needs, so I decided
to take the most logical route and create my own.

Here's my problem: I want to be able to open a simple window into which the
user can type commands.  When he types a character, it is displayed; when he
types delete, a character is erased; when he types return or enter, the
entire command is parsed by the program.  Simple enough, right?

When I use TextEdit for the text window, the user is able to go back into the
stuff already displayed and edit it.  I don't want this.  Besides, I can't
trap for the return or enter keys easily, and even when I do, I don't know
how to go back to the beginning of the line and retrieve the whole thing.

A simpler way would be to ignore TextEdit.  I can listen for characters and
echo them to the screen when I get them, and when I get a return or enter, I
can parse the line.  Scrolling is a tad difficult and unwieldy, but that can
be overcome.  The problem here is that when the user types delete, the delete
appears as a *character* on the screen - one of those little white rectangles.

So, how do I do this?  I want to have an IBM-style interface (hold your cries;
there's a method to my madness) in a window.  There's got to be an easy way!

Help!

-- 
| Brian S. Kendig       |  I feel more like I   | bskendig                   |
| Computer Engineering  |  did when I got here  | @phoenix.Princeton.EDU     |
| Princeton University  |       than I do now.  | @PUCC.BITNET               |
| Systems Engineering, NASA Space Station Freedom / General Electric WP3     |

levin@bbn.com (Joel B Levin) (07/31/89)

In article <9674@phoenix.Princeton.EDU> bskendig@phoenix.Princeton.EDU (Brian Kendig) writes:
|A simpler way would be to ignore TextEdit.  I can listen for characters and
|echo them to the screen when I get them, and when I get a return or enter, I
|can parse the line.  Scrolling is a tad difficult and unwieldy, but that can
|be overcome.  The problem here is that when the user types delete, the delete
|appears as a *character* on the screen - one of those little white rectangles.

So don't just echo the characters, look at them.  If you get a delete,
don't echo it, erase the previous character.  If you get a word-delete
character (whatever you choose), don't echo it, erase the previous
word, etc.  terminal interfaces have been doing this for years!
|
|So, how do I do this?  I want to have an IBM-style interface (hold your cries;
|there's a method to my madness) in a window.  There's got to be an easy way!

I'll leave the flames to others, I sense them coming...
	/JBL
=
UUCP:     levin@bbn.com (new) or {backbone}!bbn!levin (old)
INTERNET: levin@bbn.com       		POTS: (617) 873-3463
   "The night was"

bskendig@phoenix.Princeton.EDU (Brian Kendig) (08/01/89)

In article <43528@bbn.COM> levin@BBN.COM (Joel B Levin) writes:
>In article <9674@phoenix.Princeton.EDU> bskendig@phoenix.Princeton.EDU (Brian Kendig) writes:
>|A simpler way would be to ignore TextEdit.  I can listen for characters and
>|echo them to the screen when I get them, and when I get a return or enter, I
>|can parse the line.  Scrolling is a tad difficult and unwieldy, but that can
>|be overcome.  The problem here is that when the user types delete, the delete
>|appears as a *character* on the screen - one of those little white rectangles.
>
>So don't just echo the characters, look at them.  If you get a delete,
>don't echo it, erase the previous character.  If you get a word-delete
>character (whatever you choose), don't echo it, erase the previous
>word, etc.  terminal interfaces have been doing this for years!

Sounds simple enough, doesn't it?  But: how do I erase the previous character?
If I send a 'delete' to the window, it appears as a gremlin (the 'technical'
name for those little white rectangles).  Methinks I'd have to do some pretty
snappy QuickDraw manipulations to manually erase the graphic shape of the
last character I sent to the screen; think for a moment about the sheer number
of calculations involved in such a move and you'll see why I don't care to
try it.

If I could figure out how to erase the last character I sent to the screen
(and ones before that, then, if the user types delete more than once), I would
be a happy puppy.  (Then, what about a flashing cursor...?  Ugh.)  Barring
that, if that ain't possible, I'd have to mess with TextEdit somehow.  How?
I don't know.  I'd have to disable moving the cursor with mouse clicks, at
the very least.  TE would take care of the delete key.  If I could trap for
a carriage return (and I think I know how to), how can I get back into the
TE record and extract all the text between this CR and the last one?

I know this stuff is possible.  The terminal program I'm using is doing it
(well, kind of) right now.

>| ... I want to have an IBM-style interface (hold your cries;
>|there's a method to my madness) in a window.  There's got to be an easy way!
>
>I'll leave the flames to others, I sense them coming...

Actually, the reason I want to do this is to attract the attention of those
poor souls who think that COMPLEXITY=POWER.  They think that if they have to
learn how to type all sorts of convoluted commands, they can do more than
pointing-and-clicking could ever get them.  With a little work, I could easily
(?!) get this thing to recognize commands from Unix, MS-D*S, CP/M... you name
it.  Ain't resources great?

Besides, I'd use this interface myself.  Typing "cd HD:Applications:Word proc:
MacWrite files" is *much* faster than digging through four cluttered windows
and looking for specific folder icons.  (Well, maybe not *much* faster... but
you get the general idea.)

Hm.  Imagine what people would think of HyperCard if you were to run it on an
IBM text-only system!

>	/JBL
>UUCP:     levin@bbn.com (new) or {backbone}!bbn!levin (old)
>INTERNET: levin@bbn.com       		POTS: (617) 873-3463

-- 
| Brian S. Kendig       |  I feel more like I   | bskendig                   |
| Computer Engineering  |  did when I got here  | @phoenix.Princeton.EDU     |
| Princeton University  |       than I do now.  | @PUCC.BITNET               |
| Systems Engineering, NASA Space Station Freedom / General Electric WP3     |

al@inebriae.UUCP (Al Evans) (08/01/89)

In article <9674@phoenix.Princeton.EDU> bskendig@phoenix.Princeton.EDU (Brian Kendig) writes:
>Here's my problem: I want to be able to open a simple window into which the
>user can type commands.  When he types a character, it is displayed; when he
>types delete, a character is erased; when he types return or enter, the
>entire command is parsed by the program.  Simple enough, right?
>
>When I use TextEdit for the text window, the user is able to go back into the
>stuff already displayed and edit it.  I don't want this.  Besides, I can't
>trap for the return or enter keys easily, and even when I do, I don't know
>how to go back to the beginning of the line and retrieve the whole thing.

Maybe I'm not understanding correctly, but this actually looks to me like
the best way to do what you want. You simply check for the return/enter
keys before posting them to TEKey. You should be able to get the last line
typed from calculations based on nLines and lineStarts in the TERec. In fact,
by adding calculations based on selStart and selEnd, it should be fairly
easy to allow the user to select and execute previously-entered text. In
my opinion, this would be the best way to handle it.

>A simpler way would be to ignore TextEdit.  I can listen for characters and
>echo them to the screen when I get them, and when I get a return or enter, I
>can parse the line.  Scrolling is a tad difficult and unwieldy, but that can
>be overcome.  The problem here is that when the user types delete, the delete
>appears as a *character* on the screen - one of those little white rectangles.

This is a bit more unwieldy, but still fairly easy. Basically, you allocate
a buffer to hold the keystrokes (24X80 or whatever) and write a routine to
draw this buffer on the screen (surely you don't intend to disallow DAs,
etc.!) for updates. You'll probably want to provide a separate (faster)
routine for drawing single characters as they're typed. But you've gotta
CASE on the characters -- when the user types delete, YOU've got to move
the pointer one back in the buffer, do an EraseRect on the character just
deleted, etc. For scrolling, just make the buffer bigger. Is this beginning
to sound like you're reinventing TextEdit? You are. Don't do this unless 
you absolutely HAVE to. [long story about EBCDIC term emulator and blink
attributes omitted, much to the collective relief of the audience]

                                         --Al Evans--
-- 
Al Evans    {tndev,texbell,ssbn}!inebriae!al
            al@inebriae.WLK.COM

tim@hoptoad.uucp (Tim Maroney) (08/01/89)

In article <9674@phoenix.Princeton.EDU> bskendig@phoenix.Princeton.EDU
(Brian Kendig) writes:
>Here's my problem: I want to be able to open a simple window into which the
>user can type commands.  When he types a character, it is displayed; when he
>types delete, a character is erased; when he types return or enter, the
>entire command is parsed by the program.  Simple enough, right?

(Too bad no women will be using the software, eh?)  Simple, yes, but
it seems like you want something so simple that it violates users'
expectations of the Mac user interface.

>When I use TextEdit for the text window, the user is able to go back into the
>stuff already displayed and edit it.  I don't want this.

Why?  If you're going to be displaying text, I assure you that you *do*
want users to be able to copy and paste it at least.  If you don't want
them to be able to change it, then that's easy enough -- don't allow
any typing while there's a selection anywhere but at the end of the
text, and don't pass on Cut or Clear commands.  However, this is an
inferior solution -- it implies that users can't use the normal text
editing mechanisms on the Macintosh to edit their command lines.
That's a mess.  I recommend that you re-examine this assumption that
editing the command text is bad.  It works quite well in the MPW
Shell, after all.

>Besides, I can't
>trap for the return or enter keys easily, and even when I do, I don't know
>how to go back to the beginning of the line and retrieve the whole thing.

Why?  You are getting event records with the character codes stored in
them in a not very subtle fashion.  What prevents you from checking for
them?  Getting the text line is admittedly a bit harder, but the line
starts array stored in the text edit record is not hard to use either.
Just look in the last line start entry, add the offset to the start of
the text area (a handle to which is stored in the text edit record) and
extract the line there to the end of the record.

>A simpler way would be to ignore TextEdit.  I can listen for characters and
>echo them to the screen when I get them, and when I get a return or enter, I
>can parse the line.

How is getting a return or enter here any different from if you are
calling TextEdit to handle characters?  You're dealing in the same
commodity, key down event records.

>Scrolling is a tad difficult and unwieldy, but that can
>be overcome.  The problem here is that when the user types delete, the delete
>appears as a *character* on the screen - one of those little white rectangles.

Yes, of course, if you call Quickdraw with that character code, you get
whatever the Font Manager says is the appearance of that character.  If
you want to do editing, then you'll have to do your own using other
Quickdraw calls, specifically, MoveTo (to backspace over the previous
character) and EraseRect (to get rid of the image of the previous
character).  You will also have to draw your own flashing caret using
MoveTo and LineTo calls.  And your own selection handling, with all the
mouse tracking and InvertRect calls and so forth.  If you don't do all
this, you haven't really written a Mac program.

>So, how do I do this?  I want to have an IBM-style interface (hold your cries;
>there's a method to my madness) in a window.  There's got to be an easy way!

The easy way is to use TextEdit.  That puts an effective length
limitation on the size of your text, but you can cope with a
screenful.  But I have to question whether there really is a method to
this.  I feel that your comments are rife in assumptions that conflict
with the basic goals of the Mac interface, and which seem to show that
you don't have a lot of Mac experience yet.  Most of us went through
this stage; a horrible example is the MacNosy interface.  You will
likely find that after you assimilate more of the basic Mac concpets,
you will decide that your decision to provide a command line interface
was not a very good one.

It may be that you do have some reason, but you haven't shared it with
us.  Nonetheless, because of your Mac inexperience, I am inclined to
think that your design decisions have a great deal to do with a mental
model that speaks well to the tty-interface problem and very badly to
the Mac-interface problem.
-- 
Tim Maroney, Mac Software Consultant, sun!hoptoad!tim, tim@toad.com

"The pride of the peacock is the glory of God.
 The lust of the goat is the bounty of God.
 The wrath of the lion is the wisdom of God.
 The nakedness of woman is the work of God."
    - Blake, "The Marriage of Heaven and Hell"

guido@piring.cwi.nl (Guido van Rossum) (08/01/89)

Well, it seems like you want to write your own textedit-like package,
only less fancy, and with some features that textedit doesn't have (or
maybe it does, only it's hidden somewhere in the documentation).

You could begin by splitting the design in two parts (this is what
textedit also does, basically): a data structure to remember the text to
be displayed (maybe only the still editable part would be enough,
although then you couldn't reconstruct stuff that got temporarily hidden
under a desk accessory or some similar thing from outer space, not to
mention MultiFinder), and code to display it on the screen, by drawing
all characters using quickdraw operations.

Editing the buffer is easy: keep a counter of how many characters there
are, etc.

Conceptually, after each edit operation (append/erase character), you
could erase the screen and redraw everything -- if only your code were
fast enough to avoid flickering.  What you do in practice is keep some
extra data around to avoid unnecessary redrawing.  The start and ending
position of each character in the editable part of the buffer should be
sufficient, really.  Actually, you can compute this on the fly as well
-- quickdraw has some nifty calls to calculate the width of a character.
Of course, restricting yourself to a fixed-width font such as monaco
would make things a lot easier.

Etc., etc.; if you think a bit more about it you'll see it's entirely
doable, but you may decide not to implement it as it could be a lot of
work (although also a lot of fun).

--
Guido van Rossum, Centre for Mathematics and Computer Science (CWI), Amsterdam
guido@cwi.nl or mcvax!guido or guido%cwi.nl@uunet.uu.net
"You're so *digital*, girl!" -- Neneh Cherry

jakob@nada.kth.se (Jakob Cederlund) (08/01/89)

In my opinion you should do it just like the MPW Shell does it:
Return key does nothing except splitting the text line.
      ___
Enter  ^  key executes the selection. If nothing is selected it
executes the line with the caret on. If several lines are selected,
they are executed in order.

Using this method you do not have need for any special treatment of
rubout, users going back in the text etc.

BTW MPW Shell uses a not too bad script language, take a look at
it.

/Jakob Cederlund       jakob@nada.kth.se
Computer Science Dept. Royal Institute of Technology, Stockholm, Sweden

svc@well.UUCP (Leonard Rosenthol) (08/02/89)

In article <9674@phoenix.Princeton.EDU>, bskendig@phoenix.Princeton.EDU (Brian Kendig) writes:
> A while ago, I made a thorough search for a command-line interface for a
> Macintosh.  The only one I found did not quite suit my needs, so I decided
> to take the most logical route and create my own.
> 
> Here's my problem: I want to be able to open a simple window into which the
> user can type commands.  When he types a character, it is displayed; when he
> types delete, a character is erased; when he types return or enter, the
> entire command is parsed by the program.  Simple enough, right?
> 
> When I use TextEdit for the text window, the user is able to go back into the
> stuff already displayed and edit it.  I don't want this.  Besides, I can't
> trap for the return or enter keys easily, and even when I do, I don't know
> how to go back to the beginning of the line and retrieve the whole thing.
> 
	Just because the default interaction of TE does what you describe above
does NOT mean that it HAS to.  Since all events go through your application (or
whatever) BEFORE TE gets them, you can do all sorts of preprocessing to decide
whether TE should get them at as is, modified, or not at all.
	For example, to prevent editing of previous text, you can check the
selection point/rect (TE^^.selStart, TE^^.selEnd) and if it is not on the last
line of the text (current entry line, I am assuming one line at a time) (you
can get lines using the TE^^.lineStarts array) then don't process keydowns,
edit commands, etc.
	As to trapping the return/enter keys you get the keyDown first, and
simply check to see what the key is and if CR or Enter than get the last line
using the lineStarts array and TE^^.length and parse it, etc.  
	If you need some example code for using the lineStarts array I can
dig some up and post it.

> A simpler way would be to ignore TextEdit.  I can listen for characters and
> echo them to the screen when I get them, and when I get a return or enter, I
> can parse the line.  Scrolling is a tad difficult and unwieldy, but that can
> be overcome.  The problem here is that when the user types delete, the delete
> appears as a *character* on the screen - one of those little white rectangles.
> 
	As mentioned above, TE will work just fine.  If, however, you want to
write your own then you will need to do certain things to emulate TE. For 
example, you site one of the best examples, the handling of backspace. The
backspace character is a character in teh font just like any other, and as such
you really do want to call DrawChar on it. What you want to do is to back the
'cursor' up one character (probably erasing it as well), just like TE does.
	the other major problem would be handling of scrolling, storage, etc.
I think your tiem would be best spent working with my above suggestions to use
TE rathe than reinventing the wheel for this 'simple' taks.

-- 
+--------------------------------------------------+
Leonard Rosenthol        |  GEnie : MACgician
Lazerware, inc.          |  MacNet: MACgician
UUCP: svc@well.UUCP      |  ALink : D0025