Article 2755 of comp.sources.unix:
Submitted-By: mik@cs.su.oz.au (Michael Koelling)
Posting-Number: Volume 29, Issue 102
Archive-Name: red-96/part07

#!/bin/sh
# This is `part07' (part 7 of a multipart archive).
# Do not concatenate these parts, unpack them in order with `/bin/sh'.
# File `Red/Red/screen.cc' is being continued...
#
touch -am 1231235999 $$.touch >/dev/null 2>&1
if test ! -f 1231235999 && test -f $$.touch; then
  shar_touch=touch
else
  shar_touch=:
  echo
  echo 'WARNING: not restoring timestamps.  Consider getting and'
  echo "installing GNU \`touch', distributed in GNU File Utilities..."
  echo
fi
rm -f 1231235999 $$.touch
#
if test ! -r _sharseq.tmp; then
  echo 'Please unpack part 1 first!'
  exit 1
fi
shar_sequence=`cat _sharseq.tmp`
if test "$shar_sequence" != 7; then
  echo "Please unpack part $shar_sequence next!"
  exit 1
fi
if test ! -f _sharnew.tmp; then
  echo 'x - still skipping Red/Red/screen.cc'
else
  echo 'x - continuing file Red/Red/screen.cc'
  sed 's/^X//' << 'SHAR_EOF' >> 'Red/Red/screen.cc' &&
X  max_cx = left_offset + columns * fontwidth;
X  max_cy = (lines-1) * fontheight;
X}
X
X// -------------------------------------------------------------------------
X// next_line: move cursor to the beginnning of the next line. Scrolls screen
X//	up if cursor is in bottom line.
X// -------------------------------------------------------------------------
X
Xvoid SCREEN::next_line ()
X{
X  if (cursor_visible) hide_cursor ();
X
X  if (line < lines-1) {			// not at bottom...
X    cx = left_offset;  column = 0;
X    cy += fontheight;  line++;
X  }
X  else {
X    XCopyArea (display, window, window, gcNorm, 0, fontheight, screenwidth, 
X		screenheight-fontheight, 0, 0);
X    cx = left_offset;  column = 0;
X    clear_to_eol ();
X  }
X  if (cursor_is_on) show_cursor ();
X}
X
X// -------------------------------------------------------------------------
X// -------------------------------------------------------------------------
X
Xvoid SCREEN::cursor_to (int x, int y)
X{
X  assert ((x>=0) && (x<=columns));
X  assert ((y>=0) && (y<lines));
X
X  hide_cursor ();
X
X  column = x;
X  line = y;
X  cx = left_offset + column * fontwidth;
X  cy = line * fontheight;
X
X  show_cursor ();
X}
X
X// -------------------------------------------------------------------------
X// insert_char: insert a character at the cursor position, moving the cursor
X//	and the rest of the line to the right. If the cursor is already in
X//	the last column, it remains there.
X// -------------------------------------------------------------------------
X
Xvoid SCREEN::insert_char (char* ch)
X{
X  assert (column < columns);
X
X  hide_cursor ();
X  XCopyArea (display, window, window, gcNorm, cx, cy,
X	     max_cx-cx-fontwidth, fontheight, cx+fontwidth, cy);
X  XDrawImageString (display, window, gcNorm, cx, cy+fontascent, ch, 1);
X  column++;
X  cx += fontwidth;
X  show_cursor ();
X}
X
X// -------------------------------------------------------------------------
X// -------------------------------------------------------------------------
X
Xvoid SCREEN::delete_char ()
X{
X  hide_cursor ();
X  XCopyArea (display, window, window, gcNorm, cx+fontwidth, cy, 
X	       max_cx-cx-fontwidth, fontheight, cx, cy);
X  XClearArea (display, window, max_cx-fontwidth, cy, fontwidth, fontheight, False);
X  show_cursor ();
X}
X
X// -------------------------------------------------------------------------
X// -------------------------------------------------------------------------
X
Xvoid SCREEN::insert_line ()
X{
X  hide_cursor ();
X  XCopyArea (display, window, window, gcNorm, 0, cy, screenwidth,
X	       max_cy-cy, 0, cy+fontheight);
X  XClearArea (display, window, 0, cy, screenwidth, fontheight, False);
X  XDrawLine (display, window, gcNorm, left_offset-3, cy,
X	     left_offset-3, cy+fontheight);
X  cx = left_offset;  column = 0;
X  show_cursor ();
X}
X
X// -------------------------------------------------------------------------
X// write_string: Write a line at the current cursor position. The line can
X//	go over several lines. Returns the number of screen lines used and
X//	the last index within the text line that was written.
X// -------------------------------------------------------------------------
X
Xvoid SCREEN::write_string (const char* str, int length,
X			 int& screenlines, int& index)
X{
X  int start_index, screencol, string_index;
X
X  screenlines = 1;
X  string_index = 0;
X  screencol = column;
X
X  if (cursor_visible) hide_cursor ();
X
X  while (string_index < length) {		// go through the whole line...
X
X    if (screencol == columns) {		// at edge of screen?
X      set_wrap_mark ();
X      if (cursor_at_bottom ()) break;	// end of screen?
X      next_line ();
X      clear_wrap_mark ();
X      screencol = 0;
X      screenlines++;
X    }
X    if (str[string_index] == TAB) {
X      write_tab ();
X      string_index++;
X      screencol = column;
X    }
X
X    start_index = string_index;
X    while ((str[string_index] != TAB) && (string_index<length) && (screencol < columns)) {
X      string_index++;
X      screencol++;
X    }
X    if (string_index-start_index > 0)
X      write_chars (&str[start_index], string_index-start_index);
X  }
X  index += string_index;
X  clear_to_eol ();
X
X  if (cursor_is_on) show_cursor ();
X}
X
X// -------------------------------------------------------------------------
X// -------------------------------------------------------------------------
X
X// void SCREEN::scroll_up (char *bottom_line)
X// {
X// }
X
X// -------------------------------------------------------------------------
X// -------------------------------------------------------------------------
X
X// void SCREEN::scroll_down (char *top_line)
X// {
X// }
X
X// -------------------------------------------------------------------------
X// clear_screen: clears the screen. The cursor is positioned to 0,0.
X// -------------------------------------------------------------------------
X
Xvoid SCREEN::clear_screen ()
X{
X  XClearArea (display, window, 0, 0, 0, 0, False);
X  XDrawLine (display, window, gcNorm, left_offset-3, 0,
X	     left_offset-3, screenheight);
X  cx = left_offset;  column = 0;
X  cy = 0;	     line = 0;
X  cursor_visible = False;
X  if (cursor_is_on) show_cursor ();
X}
X
X// -------------------------------------------------------------------------
X// clear_to_eol: erases the characters from the cursor position to the end 
X//	of the line.
X// -------------------------------------------------------------------------
X
Xvoid SCREEN::clear_to_eol ()
X{
X  if (cursor_is_on) hide_cursor ();
X  if (cx<max_cx)
X    if (in_selection)
X      XFillRectangle (display, window, gcSelectFill, cx, cy, max_cx-cx, fontheight);
X    else
X      XClearArea (display, window, cx, cy, max_cx-cx, fontheight, False);
X  if (cursor_is_on) show_cursor ();
X}
X
X// -------------------------------------------------------------------------
X// clear_to_eos: Clear screen fromthe current line (including the whole
X//	current line) to end of screen. Leaves the cursor at the beginning 
X//	of the current line.
X// -------------------------------------------------------------------------
X
Xvoid SCREEN::clear_to_eos ()
X{
X  hide_cursor ();
X  clear_to_eol ();
X  if (tag_grey)
X    XClearArea (display, window, left_offset, cy+fontheight, 0, 0, False);
X  else {
X    XClearArea (display, window, 0, cy+fontheight, 0, 0, False);
X    XDrawLine (display, window, gcNorm, left_offset-3, cy,
X	       left_offset-3, screenheight);
X  }
X  column = 0;
X  cx = left_offset;
X  show_cursor ();
X}
X
X// -------------------------------------------------------------------------
X// -------------------------------------------------------------------------
X
Xvoid SCREEN::set_tag (ScreenTag tag)
X{
X  if (!tag_grey)
X    switch (tag) {
X
X      case NoScreenTag:
X        XClearArea (display, window, 0, cy-1, 14, 14, False);
X        break;
X      case ErrorScreenTag:
X        XCopyArea (display, utility->error_pix, window, gcNorm, 0, 0, 14, 14, 0, cy-1);
X        break;
X      case BreakScreenTag:
X        XCopyArea (display, utility->stop_pix, window, gcNorm, 0, 0, 14, 14, 0, cy-1);
X        break;
X      case ExecScreenTag:
X        XCopyArea (display, utility->exec_pix, window, gcNorm, 0, 0, 14, 14, 0, cy-1);
X        break;
X      case ExecBreakScreenTag:
X        XCopyArea (display, utility->execstop_pix, window, gcNorm, 0, 0, 14, 14, 0, cy-1);
X        break;
X    }
X}
X
X// -------------------------------------------------------------------------
X//  set_wrap_mark: Display an arrow at the end of the current screen line as
X//	a sign that this text line continues in the next screen line.
X// -------------------------------------------------------------------------
X
Xvoid SCREEN::set_wrap_mark ()
X{
X  XCopyArea (display, utility->longline_pix, window, gcNorm, 
X		0, 0, 8, 9, max_cx, cy+2);
X
X}
X
X// -------------------------------------------------------------------------
X//  clear_wrap_mark: Clear the wrap line arrow at the end of the current 
X//	screen line.
X// -------------------------------------------------------------------------
X
Xvoid SCREEN::clear_wrap_mark ()
X{
X  XClearArea (display, window, max_cx, cy, 0, fontheight, False);
X}
X
X// -------------------------------------------------------------------------
X//  set_tag_grey: 
X// -------------------------------------------------------------------------
X
Xvoid SCREEN::set_tag_grey (Boolean grey)
X{
X  tag_grey = grey;
X  if (tag_grey) {
X    XSetForeground (display, gcNorm, utility->backgroundColour);
X    XFillRectangle (display, window, gcNorm, 0, 0, left_offset-3, screenheight);
X    XSetForeground (display, gcNorm, utility->black);
X  }    
X  else
X    XClearArea (display, window, 0, 0, left_offset-4, screenheight, False);
X}
X
X// -------------------------------------------------------------------------
X//  cursor_on: show or hide the cursor and return the previous state.
X// -------------------------------------------------------------------------
X
XBoolean SCREEN::cursor_on (Boolean on)
X{
X  Boolean prev = cursor_is_on;
X
X  hide_cursor ();
X  cursor_is_on = on;
X  if (cursor_is_on) show_cursor ();
X  return prev;
X}
X
X// -------------------------------------------------------------------------
X//  set_selection: Set or unset mode for displaying the selection.
X// -------------------------------------------------------------------------
X
Xvoid SCREEN::set_selection (Boolean on)
X{
X  in_selection = on;
X}
X
X// -------------------------------------------------------------------------
X//  toggle_selection: Toggle mode for displaying the selection.
X// -------------------------------------------------------------------------
X
Xvoid SCREEN::toggle_selection ()
X{
X  in_selection = !in_selection;
X}
X
X// -------------------------------------------------------------------------
X// -------------------------------------------------------------------------
X
Xvoid SCREEN::set_font (char *fontname)
X{
X}
X
X// -------------------------------------------------------------------------
X// -------------------------------------------------------------------------
X
XBoolean SCREEN::cursor_at_bottom ()
X{
X  return line==lines-1;
X}
X
X// -------------------------------------------------------------------------
X// nr_of_lines: Return the number of screen lines occupied by the printing
X//	of str,length starting at column startcol.
X// -------------------------------------------------------------------------
X
Xint SCREEN::nr_of_lines (char* str, int length, int startcol)
X{
X  int lines, col;
X
X  line_layout (str, length, startcol, lines, col);
X  return lines;
X}
X
X// -------------------------------------------------------------------------
X// index_to_square: Given in index in a string, this function returns the
X//	line and column offset of the position behind the string.  (If it is
X//	within one line, the line value is 0.)  'cursor'
X//	indicates whether this is used to determine a cursor or character
X//	position. The cursor may be behind the last column, characters 
X//	aren't. So if the position is behind the last column for a character
X//	it is set to the first column of the next line.
X// -------------------------------------------------------------------------
X
Xvoid SCREEN::index_to_square (char* str, int length, int& line, int& endcol,
X			      Boolean cursor)
X{
X  line_layout (str, length, 0, line, endcol);
X
X  if (!cursor)
X    if (endcol == columns)
X      endcol = 0;
X    else
X      line--;
X  else
X    line--;
X
X}
X
X// -------------------------------------------------------------------------
X// square_to_index: Find an index in a buffer line that is on a given square
X//	on screen.  Only the one line passed in is checked by doing the same
X//	calculations that would be done while printing.  str and length 
X//	specify the line to test, starting at screen line line.  If, while 
X//	testing, the position x,y is reached, the index in the line is 
X//	returned, otherwise -1 is returned.
X// -------------------------------------------------------------------------
X
Xint SCREEN::square_to_index (char* str, int length, int& line, int x, int y)
X{
X  int index=0, screencol = 0;
X
X  while (index < length) {
X
X    if ((line == y) && (screencol >= x))		// found!
X      return index;
X
X    if (screencol >= columns) {
X      screencol = 0;
X      line++;
X      if ((line == y) && (screencol >= x))		// found!
X        return index;
X    }
X
X    if (str[index] == TAB)
X      screencol = next_tab (screencol);
X    else
X      screencol++;
X    index++;
X  }
X  if (line == y)		// right line but index not reached, ret length
X    return length;
X  else
X    return -1;			// haven't reached it yet
X}
X
X// -------------------------------------------------------------------------
X// point_to_square: Transform point coordinates to character coordinates on
X//	screen. IN: x,y is the point to be transformed.  OUT: in_tag is true
X//	if the click was in the tag line. ln,col are set to the character
X//	square at point.
X// -------------------------------------------------------------------------
X
Xvoid SCREEN::point_to_square (int x, int y, int& col, int& ln, Boolean& in_tag)
X{
X  ln = y / fontheight;
X
X  if (ln >= lines) {
X    ln = lines-1;
X    col = columns;
X    return;
X  }
X
X  if (ln < 0) {
X    ln = 0;
X    col = 0;
X    return;
X  }
X
X  if (x < left_offset-2) {
X    in_tag = True;
X    return;
X  }
X
X  in_tag = False;
X  col = (x+(fontwidth/2)-left_offset) / fontwidth;
X  if (col > columns)
X    col = columns;
X}
X
X// =========================================================================
X// 			    private functions
X// =========================================================================
X
X// -------------------------------------------------------------------------
X// -------------------------------------------------------------------------
X
Xvoid SCREEN::setup_gc (Widget da)
X{
X  int foreground, background;
X  XGCValues vals;
X
X  Arg al[10];
X  int ac;
X
X  // get font attributes
X  fontwidth = font->max_bounds.width;
X  if (fontwidth != font->min_bounds.width)	// check for fixed font
X    giveup ("Red: Error in resources settings. \"editFont\" must be "
X	   "a fixed width font.");
X
X  fontascent = font->ascent;
X  fontheight = font->ascent + font->descent;
X
X  // get fg and bg colors
X  ac=0;
X  XtSetArg (al[ac], XmNforeground, &foreground); ac++;
X  XtSetArg (al[ac], XmNbackground, &background); ac++;
X  XtGetValues (da, al, ac);
X
X  // create the gc					// FOR NORMAL TEXT
X  vals.foreground = foreground;
X  vals.background = background;
X  vals.font = font->fid;		// see also: XSetFont
X  gcNorm = XtGetGC (da, GCForeground | GCBackground | GCFont, &vals);
X
X  vals.background = utility->get_colour (SELECT_COLOUR, True);	// FOR SELECTION
X  if (vals.background == utility->black)		// could not get colour
X    vals.foreground = utility->white;
X  else
X    vals.foreground = utility->black;
X  gcSelect = XtGetGC (da, GCForeground | GCBackground | GCFont, &vals);
X
X  vals.foreground = vals.background;				// FOR SELECTION
X  gcSelectFill = XtGetGC (da, GCForeground | GCBackground | GCFont, &vals);
X
X  vals.function = GXxor;					// FOR CURSOR
X  vals.foreground = utility->get_colour (CURSOR_COLOUR, True);
X  vals.background = WhitePixelOfScreen (XtScreen(da));
X  vals.foreground =  vals.foreground ^ vals.background;
X  vals.line_width = 2;
X  gcXor = XtGetGC (da, GCForeground | GCBackground | GCFunction | GCLineWidth, &vals);
X}
X
X// -------------------------------------------------------------------------
X//  show_cursor: Show the cursor at the current cursor_position on screen.
X// -------------------------------------------------------------------------
X
Xvoid SCREEN::show_cursor ()
X{
X  if (cursor_is_on && !cursor_visible) {
X    XDrawLine (display, window, gcXor, cx, cy-2, cx, cy+fontheight+2);
X    cursor_visible = True;
X  }
X}
X
X
X// -------------------------------------------------------------------------
X//  hide_cursor: Hide the cursor at the current cursor_position on screen.
X// -------------------------------------------------------------------------
X
Xvoid SCREEN::hide_cursor ()
X{
X  if (cursor_is_on && cursor_visible) {
X    XDrawLine (display, window, gcXor, cx, cy-2, cx, cy+fontheight+2);
X    cursor_visible = False;
X  }
X}
X
X// -------------------------------------------------------------------------
X// write_chars: write some chars at the current cursor position.
X//	It is assumed that cursor_to is called after this.
X// -------------------------------------------------------------------------
X
Xvoid SCREEN::write_chars (const char* str, int length)
X{
X  if (in_selection)
X    XDrawImageString (display, window, gcSelect, cx, cy+fontascent, str, length);
X  else
X    XDrawImageString (display, window, gcNorm, cx, cy+fontascent, str, length);
X  cx = cx + length*fontwidth;
X  column = column+length;
X  assert (column<=columns);
X}
X
X// -------------------------------------------------------------------------
X// write_tab:  Write a TAB to screen.
X// -------------------------------------------------------------------------
X
Xvoid SCREEN::write_tab ()
X{
X  int nr_spaces;
X
X  nr_spaces = next_tab (column) - column;
X  if (column+nr_spaces < columns)
X    write_chars (Spaces, nr_spaces);
X  else
X    write_chars (Spaces, columns-column);
X}
X
X// -------------------------------------------------------------------------
X// next_tab: Return position of next TAB stop after col.
X// -------------------------------------------------------------------------
X
Xint SCREEN::next_tab (int col)
X{
X  int tab;
X
X  tab = ((col / TAB_length + 1) * TAB_length);
X  if (tab < columns)
X    return tab;
X  else
X    return columns;
X}
X
X// -------------------------------------------------------------------------
X// line_layout: Analyse a line to determine how it would be printed. Takes
X//	as arguments a string and length, a column on screen where to start 
X//	printing (startcol), and returns how many lines would be used to
X//	print the line (lines) and in which screen column the cursor ends
X//	up (endcol).
X// -------------------------------------------------------------------------
X
Xvoid SCREEN::line_layout (char* str, int length, int startcol,
X			  int& lines, int& endcol)
X{
X  int index;
X
X  index = 0;
X  endcol = startcol;
X  lines = 1;
X
X  // count through str, looking for TABs and checking for end of screen
X
X  while (index < length) {
X
X    if (endcol >= columns) {		// at end of screen?
X      endcol = 0;
X      lines++;
X    }
X
X    if (str[index] == TAB)
X      endcol = next_tab (endcol);
X    else
X      endcol++;
X    index++;
X  }
X}
SHAR_EOF
  echo 'File Red/Red/screen.cc is complete' &&
  $shar_touch -am 0512222696 'Red/Red/screen.cc' &&
  chmod 0640 'Red/Red/screen.cc' ||
  echo 'restore of Red/Red/screen.cc failed'
  shar_count="`wc -c < 'Red/Red/screen.cc'`"
  test 20957 -eq "$shar_count" ||
    echo "Red/Red/screen.cc: original size 20957, current size $shar_count"
  rm -f _sharnew.tmp
fi
# ============= Red/Red/screen.h ==============
if test -f 'Red/Red/screen.h' && test X"$1" != X"-c"; then
  echo 'x - skipping Red/Red/screen.h (file already exists)'
  rm -f _sharnew.tmp
else
  > _sharnew.tmp
  echo 'x - extracting Red/Red/screen.h (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'Red/Red/screen.h' &&
X#ifndef _SCREEN_H
X#define _SCREEN_H
X
X#include <Xm/Xm.h>
X#include <Xm/DrawingA.h>
X
Xenum ScreenTag { 
X  NoScreenTag, ErrorScreenTag, BreakScreenTag, ExecScreenTag, ExecBreakScreenTag
X};
X
X
Xclass SCREEN {
X
X  public:
X    SCREEN (XFontStruct* screenfont, Boolean greytag);
X
X    void init (Widget draw_area,	// be sure to call init WHILE THE 
X	       int tag_width);		// widget is visible on screen!
X
X    void expose ();
X    void resize ();
X
X    void next_line ();
X    void cursor_to (int x, int y);
X
X    void insert_char (char* ch);		// insert a character
X    void delete_char ();			// delete a character
X    void insert_line ();			// insert empty line
X    void write_string (const char* str,
X		     int length,
X		     int& screenlines,
X		     int& index);
X//    void scroll_up (char* bottom_line);	// maybe later for optimisation
X//    void scroll_down (char* top_line);
X
X    void clear_screen ();
X    void clear_to_eol ();
X    void clear_to_eos ();
X
X    void set_tag (ScreenTag tag);
X    void set_wrap_mark ();
X    void clear_wrap_mark ();
X
X    void set_tag_grey (Boolean grey);	// set/reset tag area being grey
X
X    Boolean cursor_on (Boolean on);	// show or hide the cursor and return
X					// the previous state
X    void set_selection (Boolean on);	// turn font/style for selection on/off
X    void toggle_selection ();		// toggle font/style for selection
X
X    void set_font (char* fontname);
X
X    Boolean cursor_at_bottom();		// True if cursor in bottom screen line
X
X    int nr_of_lines (char* str,		// number of screen lines that would
X		     int length,	// be used for printing this line
X		     int startcol);
X
X    void index_to_square (char* str,	// return line and column offset
X		     int length,	// from starting point after printing
X		     int& line,		// this line
X		     int& endcol,
X		     Boolean cursor);
X
X    int square_to_index (char* str,	// given a string, print position and
X		      int length,	// screen coordinates, check whether
X		      int& line,	// and where the coordinates lie
X		      int x,		// within the string
X		      int y);
X
X    void point_to_square (int x,	// given a pixel in the screen area,
X		      int y,		// return the screen coordinate (line
X		      int& col,		// and column at that point
X		      int& ln,
X		      Boolean& in_tag);
X
X  // NOTE: all public variables should be considered read only!!
X
X    int lines, columns;		// number of text lines/columns on screen
X    int line, column;		// current cursor position on screen
X
X  private:
X    void setup_gc (Widget da);
X    void show_cursor ();
X    void hide_cursor ();
X
X    void write_chars (const char* str, int length);
X    void write_tab ();
X    int next_tab (int col);
X
X    void line_layout (char* str,
X		      int length,
X		      int startcol,
X		      int& lines,
X		      int& endcol);
X
X    const char* Spaces;
X    const int TAB_length;
X    const char TAB;
X    const char EOL;
X
X    int left_offset;		// offset in points for first char in line
X    int right_offset;		// offset in points for last char in line
X    Boolean tag_grey;		// True, if tag area is greyed
X    int cx, cy;			// current cursor position in pixels
X    int max_cx, max_cy;		// maximal cursor position in pixels
X    Dimension screenwidth, screenheight;	// screen dimension in pixels
X    Boolean cursor_is_on;       // True if cursor is on
X    Boolean cursor_visible;     // True if cursor is displayed currently
X    Boolean in_selection;	// True if displaying selected text
X
X    XFontStruct* font;		// font used for display of text
X
X    GC gcXor;			// xor gc with colors for cursor
X    GC gcNorm;			// gc with colors for normal text
X    GC gcSelect;		// gc with colors for selected text
X    GC gcSelectFill;		// gc with colors for selected background
X    Display *display;
X    Window window;
X    Widget da;		// the draw area
X
X    Boolean first_init;
X    int fontwidth;
X    int fontascent;
X    int fontheight;
X};
X
X#endif
SHAR_EOF
  $shar_touch -am 0512222696 'Red/Red/screen.h' &&
  chmod 0640 'Red/Red/screen.h' ||
  echo 'restore of Red/Red/screen.h failed'
  shar_count="`wc -c < 'Red/Red/screen.h'`"
  test 3876 -eq "$shar_count" ||
    echo "Red/Red/screen.h: original size 3876, current size $shar_count"
  rm -f _sharnew.tmp
fi
# ============= Red/Red/userfunc.cc ==============
if test -f 'Red/Red/userfunc.cc' && test X"$1" != X"-c"; then
  echo 'x - skipping Red/Red/userfunc.cc (file already exists)'
  rm -f _sharnew.tmp
else
  > _sharnew.tmp
  echo 'x - extracting Red/Red/userfunc.cc (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'Red/Red/userfunc.cc' &&
X// ===========================================================================
X//  
X//  File: userfunc.cc						-mik- 1995
X//
X//  Implementation of class USERFUNC.  See userfunc.h for description.
X//  Userfunc is part of the Blue programming environment.
X//
X// ===========================================================================
X
X// the following three are all for getting the home directory 
X//  (getuid and getpwuid)
X#include <sys/types.h>
X#include <unistd.h>
X#include <pwd.h>
X
X#include <X11/keysym.h>
X#include <Xm/Form.h>
X#include <Xm/PushB.h>
X#include <Xm/RowColumn.h>
X#include <Xm/List.h>
X
X#include "userfunc.h"
X#include "editor.h"
X#include "defines.h"
X#include "../Utility/utility.h"
X#include "../Utility/messages.h"
X#include "../Utility/question_dlg.h"
X#include "../Utility/debug.h"
X
X// ---------------------------------------------------------------------------
X//  USERFUNC: Create and bind all user functions.
X// ---------------------------------------------------------------------------
X
XUSERFUNC::USERFUNC (Widget parent)
X{
X  int i;
X
X  for (i=0; i<NR_OF_ITEMS; i++)
X    menu_func[i] = NOT_BOUND;
X  for (i=0; i<NR_OF_BUTTONS; i++)
X    tool_func[i] = NOT_BOUND;
X
X  plain_keymap          = new KEYMAP ("");
X  shift_keymap          = new KEYMAP ("Shift-");
X  ctrl_keymap           = new KEYMAP ("Ctrl-");
X  alt_keymap            = new KEYMAP ("Alt-");
X  shift_ctrl_keymap     = new KEYMAP ("Shift-Ctrl-");
X  shift_alt_keymap      = new KEYMAP ("Shift-Alt-");
X  ctrl_alt_keymap       = new KEYMAP ("Ctrl-Alt-");
X
X  init_functions ();
X  init_function_bindings (parent);
X}
X
X// ---------------------------------------------------------------------------
X//  ~USERFUNC: Destructor.
X// ---------------------------------------------------------------------------
X
XUSERFUNC::~USERFUNC ()
X{
X  delete plain_keymap;
X  delete shift_keymap;
X  delete ctrl_keymap;
X  delete alt_keymap;
X  delete shift_ctrl_keymap;
X  delete shift_alt_keymap;
X  delete ctrl_alt_keymap;
X}
X
X// ---------------------------------------------------------------------------
X//  translate_key: Given a key and modifiers, translate that key to a function.
X//	Return NULL if the key was undefined.
X// ---------------------------------------------------------------------------
X
XUserFuncId USERFUNC::translate_key (KeySym key, unsigned int modifiers)
X{
X  KEYMAP *kmap;
X
X  kmap = find_keymap (modifiers);	// find out which keymap to use
X  if (kmap) 
X    return (kmap->map (key));
X  else
X    return NOT_BOUND;
X}
X
X// ---------------------------------------------------------------------------
X//  translate_item: Given a menu item, translate that item to a function code.
X// ---------------------------------------------------------------------------
X
XUserFuncId USERFUNC::translate_item (MenuItemId item)
X{
X  return menu_func[item];
X}
X
X// ---------------------------------------------------------------------------
X//  translate_toolbutton: Given a tool button number, translate that button to
X//	a function code.
X// ---------------------------------------------------------------------------
X
XUserFuncId USERFUNC::translate_toolbutton (int button)
X{
X  return tool_func[button];
X}
X
X// -------------------------------------------------------------------------
X//  toolbutton_name: Return the button name of the toolbar button with the 
X//	ID button_no
X// -------------------------------------------------------------------------
X
Xchar* USERFUNC::toolbutton_name (int button_no)
X{
X  return tool_name[button_no];
X}
X
X// -------------------------------------------------------------------------
X//  funcname: Return the name of the user function with the ID 'func'
X// -------------------------------------------------------------------------
X
XXmString USERFUNC::funcname (UserFuncId func)
X{
X  return func_table[func].functionName;
X}
X
X// -------------------------------------------------------------------------
X//  code_pointer: Return the code pointer of the user function with the 
X//	ID 'func'
X// -------------------------------------------------------------------------
X
XCodePtr USERFUNC::code_pointer (UserFuncId func)
X{
X  return func_table[func].code;
X}
X
X// -------------------------------------------------------------------------
X//  help: Return the help string of the user function with the ID 'func'
X// -------------------------------------------------------------------------
X
XXmString USERFUNC::help (UserFuncId func)
X{
X  return func_table[func].helpString;
X}
X
X// ---------------------------------------------------------------------------
X//  show_dialog: Show the function reference dialog
X// ---------------------------------------------------------------------------
X
Xvoid USERFUNC::show_dialog (Widget parent)
X{
X  int ac;
X  Arg al[10];
X  int i;
X
X  Widget menu, red_label;
X  Widget key_label;
X  Widget but_close, but_defaults, but_help;
X
X  /* initialisation of state variables */
X
X  current_group = 0;
X  current_func = UNKNOWN_KEY;
X  keys_modified = False;
X
X  /* create a window for the dialog */
X
X  ac=0;
X  XtSetArg (al[ac], XmNmwmDecorations, 2 | 8); ac++;	// border and title
X  XtSetArg (al[ac], XmNnoResize, True); ac++;
X  XtSetArg (al[ac], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); ac++;
X  func_dlg = XmCreateFormDialog (parent, "DialogFunction", al, ac);
X
X
X  /* create the Category menu */
X
X  menu = XmCreatePulldownMenu (func_dlg, "menuBar", NULL, 0);
X  ac=0;
X  XtSetArg (al[ac], XmNsubMenuId, menu); ac++;
X  XtSetArg (al[ac], XmNtopAttachment, XmATTACH_FORM); ac++;
X  XtSetArg (al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
X  category_menu = XmCreateOptionMenu (func_dlg, "CategoryMenu", al, ac);
X  XtManageChild (category_menu);
X
X  for (i=0; i<NR_OF_GROUPS-1; i++)		// last group is hidden
X    utility->create_item (menu, group_names[i], func_dlg_menu_cb, i, this);
X
X
X  /* create the function list */
X
X  func_list = utility->create_list (func_dlg, "FunctionList", True,
X				   func_dlg_list_cb, this,
X				   NULL, NULL,
X				   AtForm, NULL, category_menu, NULL);
X  make_list ();
X
X  
X  /* create the description label */
X
X  descr_area = utility->create_rc_area (func_dlg, "descrArea", XmVERTICAL,
X		XmALIGNMENT_CENTER);
X  utility->attach (descr_area, AtForm, AtForm, func_list, AtForm);
X
X  descr_label = utility->create_label (descr_area, "descrField");
X  ac=0;
X  XtSetArg (al[ac], XmNheight, 70); ac++;
X  XtSetArg (al[ac], XmNwidth, 500); ac++;
X  XtSetArg (al[ac], XmNrecomputeSize, False); ac++;
X  XtSetArg (al[ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
X  XtSetValues (descr_label, al, ac);
X
X  clear_description ();
X
X  /* create the key list */
X
X  key_area = utility->create_rc_area (func_dlg, "keyArea", XmVERTICAL,
X		XmALIGNMENT_BEGINNING);
X  utility->attach (key_area, func_list, AtForm, NULL, descr_area);
X  key_label = utility->create_label (key_area, "KeyLabel");
X
X  key_list = utility->create_list (key_area, "KeyList", True,
X				func_dlg_keylist_cb, this, NULL, NULL,
X				NULL, NULL, NULL, NULL);
X
X  /* create the key buttons */
X
X  key_button_area = utility->create_rc_area (key_area, "buttonArea", XmHORIZONTAL,
X			XmALIGNMENT_CENTER);
X
X  ac=0;
X  XtSetArg (al[ac], XmNadjustLast, True); ac++;
X  XtSetValues (key_button_area, al, ac);
X
X  but_addkey = utility->create_button (key_button_area, "Add Key",
X				      func_dlg_button_cb, AddKeyButton, this);
X  but_delkey = utility->create_button (key_button_area, "Delete Key",
X					 func_dlg_button_cb, DeleteKeyButton, this);
X  utility->set_sensitive (but_addkey, False);
X  utility->set_sensitive (but_delkey, False);
X
X  
X  /* create a button area with the buttons */
X
X  button_area = utility->create_rc_area (func_dlg, "buttonArea", XmVERTICAL,
X			XmALIGNMENT_CENTER);
X  utility->attach (button_area, NULL, AtForm, AtForm, NULL);
X
X  but_close = utility->create_button (button_area, "Close",
X				      func_dlg_button_cb, CloseButton, this);
X  but_defaults = utility->create_button (button_area, "Defaults",
X					 func_dlg_button_cb, DefaultsButton, this);
X  but_help = utility->create_button (button_area, "HelpButton",
X				     show_help, HlpUserFunc, this);
X
X  ac=0;
X  XtSetArg (al[ac], XmNhorizontalSpacing, 20); ac++;
X  XtSetArg (al[ac], XmNverticalSpacing,20); ac++;
X  XtSetArg (al[ac], XmNdefaultButton,but_close); ac++;
X  XtSetValues (func_dlg, al, ac);
X
X  red_label = utility->create_label (func_dlg, "redLabel");
X  ac=0;
X  XtSetArg (al[ac], XmNlabelType, XmPIXMAP); ac++;
X  XtSetArg (al[ac], XmNlabelPixmap, utility->logo); ac++;
X  XtSetArg (al[ac], XmNtopAttachment, XmATTACH_FORM); ac++;
X  XtSetArg (al[ac], XmNrightAttachment, XmATTACH_WIDGET); ac++;
X  XtSetArg (al[ac], XmNrightWidget, button_area); ac++;
X  XtSetValues (red_label, al, ac);
X  XtManageChild (func_dlg);
X}
X
X// ========================= private functions ===============================
X
X// ---------------------------------------------------------------------------
X//  push_button: A button in the function dialog was pressed. Find out which
X//	one and call the appropriate routine.
X// ---------------------------------------------------------------------------
X
Xvoid func_dlg_button_cb (Widget w, XtPointer button_no, XtPointer call_data)
X{
X  int ac;
X  Arg al[10];
X  USERFUNC* userfunc;
X
X  // extract object pointer from widget user data
X
X  ac=0;
X  XtSetArg (al[ac], XmNuserData, &userfunc); ac++;
X  XtGetValues (w, al, ac);
X
X  userfunc->push_button (FuncDlgButton ((int)button_no) ); 
X}
X
X// ---------------------------------------------------------------------------
X
Xvoid USERFUNC::push_button (FuncDlgButton button)
X{
X  switch (button) {
X
X    case AddKeyButton:
X	key_add ();
X	break;
X
X    case DeleteKeyButton:
X	key_delete ();
X	break;
X
X    case CloseButton:
X	close_dialog ();
X	break;
X
X    case DefaultsButton:
X	set_defaults1 ();
X	break;
X
X    default:
X	assert (False);
X  }
X}
X
X// ---------------------------------------------------------------------------
X//  menu_select: A new item in the categories menu was selected. Update the
X//	function list to show the functions for the selected group.  The item
X//	parameter in the callback holds the number of the item that was 
X//	selected.
X// ---------------------------------------------------------------------------
X
Xvoid func_dlg_menu_cb (Widget w, XtPointer item, XtPointer call_data)
X{
X  int ac;
X  Arg al[10];
X  USERFUNC* userfunc;
X
X  // extract object pointer from widget user data
X
X  ac=0;
X  XtSetArg (al[ac], XmNuserData, &userfunc); ac++;
X  XtGetValues (w, al, ac);
X
X  userfunc->menu_select ((int)item); 
X}
X
X// ---------------------------------------------------------------------------
X
Xvoid USERFUNC::menu_select (int group)
X{
X  current_group = group;
X  current_func = UNKNOWN_KEY;
X  clear_keylist ();
X  utility->set_sensitive (but_addkey, False);
X  clear_description ();
X  make_list ();
X}
X
X// ---------------------------------------------------------------------------
X//  list_select: A new item in the function list was selected. Show the
X//	description and the key bindings for this function.
X// ---------------------------------------------------------------------------
X
Xvoid func_dlg_list_cb (Widget w, USERFUNC* userfunc, 
X			XmListCallbackStruct* call_data)
X{
X  userfunc->list_select (call_data->item_position - 1); 
X}
X
Xvoid USERFUNC::list_select (int item)
X{
X  current_func = get_func (current_group, item);
X  display_description (current_func);
X  clear_keylist ();
X  search_keys (current_func);
X  utility->set_sensitive (but_addkey, True);
X}
X
X// ---------------------------------------------------------------------------
X//  key_list_select: A new item in the key list was selected.  Enable the 
X//	Delete button.
X// ---------------------------------------------------------------------------
X
Xvoid func_dlg_keylist_cb (Widget w, USERFUNC* userfunc, 
X			XmListCallbackStruct* call_data)
X{
X  userfunc->key_list_select (call_data->item_position - 1); 
X}
X
Xvoid USERFUNC::key_list_select (int item)
X{
X  assert (item < no_of_keys);
X  current_key = item;
X  utility->set_sensitive (but_delkey, True);
X}
X
X// ---------------------------------------------------------------------------
X//  add_new_key: A new key to be added as a function key has been pressed. Now
X//	define that key binding.  This function is in two parts, because we
X//	need a question dialog in the middle.
X// ---------------------------------------------------------------------------
X
Xvoid func_dlg_defkey_cb (Widget w, USERFUNC* userfunc, XEvent* event, Boolean* ctd)
X{
X  userfunc->add_new_key1 ((XKeyPressedEvent*)event);
X}
X
X// ---------------------------------------------------------------------------
X
Xvoid USERFUNC::add_new_key1 (XKeyPressedEvent *event)
X{
X  char str[2];
X  unsigned int modifierMask, modifiers;
X  char* name;
X  char message[128];
X  UserFuncId func;
X
X  assert (event->type == KeyPress);
X  
X  modifierMask = ShiftMask | ControlMask | Mod1Mask;
X  modifiers = event->state & modifierMask;
X
X  assert (XLookupString (event, str, 1, &add_keysym, NULL) <= 1);
X  if ((str[0] == 'q') || str[0] == 'Q') {
X    add_new_key2 (False);		// abort this operation
X    return;
X  }
X
X  add_kmap = find_keymap (modifiers);	// find out which keymap to use
X  if (!add_kmap)			// modifiers that we don't support
X    return;
X
X  func = add_kmap->map (add_keysym);
X
X  if (func == UNKNOWN_KEY)		// do nothing. probably modifier key, 
X    return;				// or something we don't want to handle
X
X  if (func == UFSelfInsert) {
X    messages->show_help (func_dlg, HlpCantRedefine);
X    add_new_key2 (False);		// complete this operation
X    return;
X  }
X
X  if (func == NOT_BOUND)
X    add_new_key2 (True);		// complete this operation
X  else {
X    XmStringGetLtoR (func_table[func].functionName, 
X			XmSTRING_DEFAULT_CHARSET, &name);
X    strcpy (message, "This key currently calls ");
X    strcat (message, name);
X    strcat (message, ".  Do you want\nto replace this binding?");
X    new QUESTION_DLG (func_dlg, message,
X		      "Replace", "Cancel", false, replace_question_cb, this);
X  }
X}
X
X// ---------------------------------------------------------------------------
X
Xvoid replace_question_cb (Widget w, XtPointer dlg, XtPointer call_data)
X{
X  USERFUNC* userfunc;
X  bool yes_no;
X
X  userfunc = (USERFUNC*)((QUESTION_DLG*)dlg)->get_answer (w, yes_no);
X  delete (QUESTION_DLG*)dlg;
X  userfunc->add_new_key2 (yes_no);
X}
X
X// ---------------------------------------------------------------------------
X
Xvoid USERFUNC::add_new_key2 (Boolean cont)
X{
X  if (cont) {
X    defkey (add_kmap, add_keysym, current_func);
X    if ((add_keysym >= XK_a) && (add_keysym <= XK_z))
X      defkey (add_kmap, (KeySym)(add_keysym - (XK_a-XK_A)), current_func);
X    clear_keylist ();
X    search_keys (current_func);
X  }
X  display_description (current_func);
X
X  XUngrabKeyboard (XtDisplay(func_dlg), CurrentTime);
X  utility->set_sensitive (func_list, True);
X  utility->set_sensitive (key_list, True);
X  utility->set_sensitive (category_menu, True);
X  utility->set_sensitive (button_area, True);
X  utility->set_sensitive (key_button_area, True);
X
X}
X
X// ---------------------------------------------------------------------------
X//  key_add: The Add-key button was pressed. Add a key definition to the
X//	current function.
X// ---------------------------------------------------------------------------
X
Xvoid USERFUNC::key_add ()
X{
X  utility->set_label (descr_label, 
X	"ADDING KEY BINDING\n\n"
X	"Press the key or key combination you want to bind to this function.\n"
X	"(Press 'q' to cancel)");
X  utility->set_sensitive (func_list, False);
X  utility->set_sensitive (key_list, False);
X  utility->set_sensitive (category_menu, False);
X  utility->set_sensitive (button_area, False);
X  utility->set_sensitive (key_button_area, False);
X
X  XtAddEventHandler (func_dlg, KeyPressMask, False, func_dlg_defkey_cb, this);
X  XGrabKeyboard (XtDisplay(func_dlg), XtWindow(func_dlg), False, GrabModeAsync, 
X		GrabModeAsync, CurrentTime);
X  keys_modified = True;
X}
X
X// ---------------------------------------------------------------------------
X//  key_delete: The Delete-key button was pressed. Delete the selected key
X//	definition.
X// ---------------------------------------------------------------------------
X
Xvoid USERFUNC::key_delete ()
X{
X  defkey (key_maps[current_key], key_syms[current_key], NOT_BOUND);
X  clear_keylist ();
X  search_keys (current_func);
X  keys_modified = True;
X}
X
X// ---------------------------------------------------------------------------
X//  set_defaults: The Set-defaults button was pressed. After confirming, reset
X//	all key bindings to their default values.
X// ---------------------------------------------------------------------------
X
Xvoid USERFUNC::set_defaults1 ()
X{
X  messages->show_question (func_dlg, QuKeyDefaults, defaults_question_cb, this);
X}
X
X// ---------------------------------------------------------------------------
X
Xvoid defaults_question_cb (Widget w, XtPointer dlg, XtPointer call_data)
X{
X  USERFUNC* userfunc;
X  bool yes_no;
X
X  userfunc = (USERFUNC*) messages->close_question (dlg, w, yes_no);
X  userfunc->set_defaults2 ();
X}
X
X// ---------------------------------------------------------------------------
X
Xvoid USERFUNC::set_defaults2 ()
X{
X  default_key_bindings ();
X  clear_keylist ();
X  search_keys (current_func);
X  keys_modified = True;
X}
X
X// ---------------------------------------------------------------------------
X//  close_dialog: Close the function reference dialog
X// ---------------------------------------------------------------------------
X
Xvoid USERFUNC::close_dialog ()
X{
X  XtDestroyWidget (func_dlg);
X  if (keys_modified)
X    save_key_bindings ();
X}
X
X// ---------------------------------------------------------------------------
X//  clear_list: Clear the function list.
X// ---------------------------------------------------------------------------
X
Xvoid USERFUNC::clear_list ()
X{
X  XmListDeleteAllItems (func_list);
X}
X
X// ---------------------------------------------------------------------------
X//  make_list: Build the function list for the current function group 
X//	(category)
X// ---------------------------------------------------------------------------
X
Xvoid USERFUNC::make_list ()
X{
X  int func;
X
X  assert (current_group >=0);
X  assert (current_group < NR_OF_GROUPS-1);
X  
X  clear_list ();
X  func = groups[current_group];
X  while (func != groups[current_group+1]) {
X    add_to_list ((UserFuncId)func);
X    func++;
X  }
X}
X
X// ---------------------------------------------------------------------------
X//  add_to_list: Add one function to the function list (remember that some
X//	indices in the function list are unused - don't add those).
X// ---------------------------------------------------------------------------
X
Xvoid USERFUNC::add_to_list (UserFuncId func)
X{
X  if (func_table[func].functionName)	// take care of unused indices
X    XmListAddItem (func_list, func_table[func].functionName, 0);
X}
X
X// ---------------------------------------------------------------------------
X//  clear_keylist: Clear the key list
X// ---------------------------------------------------------------------------
X
Xvoid USERFUNC::clear_keylist ()
X{
X  XmListDeleteAllItems (key_list);
X  utility->set_sensitive (but_delkey, False);
X  no_of_keys = 0;
X}
X
X// ---------------------------------------------------------------------------
X//  add_to_keylist: Add an item to the key list
X// ---------------------------------------------------------------------------
X
Xvoid USERFUNC::add_to_keylist (KEYMAP* kmap, KeySym key)
X{
X  XmString s;
X  char *item;
X
X  key_maps[no_of_keys] = kmap;		// store key map and symbol
X  key_syms[no_of_keys] = key;
X  no_of_keys++;
X  item = kmap->key_string (key);
X  s = XmStringCreateSimple (item);
X  XmListAddItem (key_list, s, 0);
X  XmStringFree (s);
X  XtFree (item);
X}
X
X// ---------------------------------------------------------------------------
X//  clear_description: Clear the description field
X// ---------------------------------------------------------------------------
X
Xvoid USERFUNC::clear_description ()
X{
X  utility->set_label (descr_label, "");
X}
X
X// ---------------------------------------------------------------------------
X//  display_description: Display the help text of func in the description
X//	field
X// ---------------------------------------------------------------------------
X
Xvoid USERFUNC::display_description (UserFuncId func)
X{
X  int ac;
X  Arg al[10];
X
X  ac=0;
X  XtSetArg (al[ac], XmNlabelString, func_table[func].helpString); ac++;
X  XtSetValues (descr_label, al, ac);
X}
X
X// ---------------------------------------------------------------------------
X//  search_keys: Search for all keys calling func and add them to key-list
X// ---------------------------------------------------------------------------
X
Xvoid USERFUNC::search_keys (UserFuncId func)
X{
X  search_keymap (plain_keymap, func);
X  search_keymap (shift_keymap, func);
X  search_keymap (ctrl_keymap, func);
X  search_keymap (alt_keymap, func);
X  search_keymap (shift_ctrl_keymap, func);
X  search_keymap (shift_alt_keymap, func);
X  search_keymap (ctrl_alt_keymap, func);
X}
X
X// ---------------------------------------------------------------------------
X//  search_keymap: Search one keymap for keys calling func and add them to 
X//	key-list
X// ---------------------------------------------------------------------------
X
Xvoid USERFUNC::search_keymap (KEYMAP* km, UserFuncId func)
X{
X  KeySym key;
X
X  km->init_search ();
X  while ((key = km->search_key (func)) != XK_VoidSymbol)
X    add_to_keylist (km, key);
X}
X
X// ---------------------------------------------------------------------------
X//  get_func: Given a group and an item number for that group, find the 
X//	function.  (The first item in the list is number 0.)
X// ---------------------------------------------------------------------------
X
XUserFuncId USERFUNC::get_func (int group, int item)
X{
X  int func = groups[group];
X
X  while (func_table[func].functionName == NULL)		// skip unused indices
X    func++;
X
X  for (; item>0; item--)
X    do
X      func++;
X    while (func_table[func].functionName == NULL);	// skip unused indices
X
X  assert (func < groups[group+1]);
X  return (UserFuncId)func;
X}
X
X// -------------------------------------------------------------------------
X// find_keymap: Convert modifier code into pointer to corrosponding keymap.
X// -------------------------------------------------------------------------
X
XKEYMAP *USERFUNC::find_keymap (unsigned int modifiers)
X{
X  switch (modifiers) {
X
X    case (0):	     // no  modifiers
X      return plain_keymap;
X
X    case (ShiftMask):
X      return shift_keymap;
X
X    case (ControlMask):
X      return ctrl_keymap;
X
X    case (Mod1Mask):
X      return alt_keymap;
X
X    case (ShiftMask | ControlMask):
X      return shift_ctrl_keymap;
X
X    case (ShiftMask | Mod1Mask):
X      return shift_alt_keymap;
X
X    case (ControlMask | Mod1Mask):
X      return ctrl_alt_keymap;
X
X    default:
X      return NULL;	// modifier combination that we don't handle
X  }
X}
X
X// -------------------------------------------------------------------------
X// save_key_bindings: Save the current key bindings to disk.
X// -------------------------------------------------------------------------
X
Xvoid USERFUNC::save_key_bindings ()
X{
X  char filename[100];
X  passwd *pw;
X
X  pw = getpwuid (getuid ());
X  strncpy (filename, pw->pw_dir, 90);
X  strcat (filename, KB_FILE_NAME);
X
X  ofstream file(filename);		// open file
X  if (!file) {
X    messages->show_error (func_dlg, ErrSaveBindings);
X    return;
X  }
X
X  file << RED_VERSION << '\n';
X  plain_keymap->save_to_file (file);
X  shift_keymap->save_to_file (file);
X  ctrl_keymap->save_to_file (file);
X  alt_keymap->save_to_file (file);
X  shift_ctrl_keymap->save_to_file (file);
X  shift_alt_keymap->save_to_file (file);
X  ctrl_alt_keymap->save_to_file (file);
X
X  file.close ();
X}
X
X// -------------------------------------------------------------------------
X// load_key_bindings: Save the current key bindings to disk.
X// -------------------------------------------------------------------------
X
XBoolean USERFUNC::load_key_bindings (Widget parent)
X{
X  char filename[100];
X  char version[10];
X  passwd *pw;
X
X  pw = getpwuid (getuid ());
X  strncpy (filename, pw->pw_dir, 90);
X  strcat (filename, KB_FILE_NAME);
X
X  ifstream file(filename);		// open file
X  if (!file)				// bindings file does not exist
X    return False;
X
X  file.get (version, 9, '\n');
X
X// make sure to get the right format
X// new key-bindings format started from Red version 0.75b
X
X  if ((strcmp (version, "0.94b") < 0) || (strcmp (version, "0.9b") == 0)) {
X
X    messages->show_error (func_dlg, ErrReadKeys);	// version earlier than 0.94b not
X    return False;					//  supported any more
X  }
X
X  if (strcmp (version, "0.96b") < 0) {			// versions before 0.96b must be
X							//  converted
X    messages->show_help (parent, HlpChangingPref);
X    file.get (version[9]);			// skip '\n'
X    plain_keymap->read_from_file (file, True);
X    shift_keymap->read_from_file (file, True);
X    ctrl_keymap->read_from_file (file, True);
X    alt_keymap->read_from_file (file, True);
X    shift_ctrl_keymap->read_from_file (file, True);
X    shift_alt_keymap->read_from_file (file, True);
X    ctrl_alt_keymap->read_from_file (file, True);
X
X      defkey (alt_keymap, XK_comma, UFShiftLeft);
X      defkey (alt_keymap, XK_period, UFShiftRight);
X
X    file.close ();
X    save_key_bindings ();				// save in new format (0.96b)
X  }
X
X  else {
X    file.get (version[9]);			// skip '\n'
X    plain_keymap->read_from_file (file, False);
X    shift_keymap->read_from_file (file, False);
X    ctrl_keymap->read_from_file (file, False);
X    alt_keymap->read_from_file (file, False);
X    shift_ctrl_keymap->read_from_file (file, False);
X    shift_alt_keymap->read_from_file (file, False);
X    ctrl_alt_keymap->read_from_file (file, False);
X
X    file.close ();
X  }
X  return True;
X}
X
X// -------------------------------------------------------------------------
X//  defun: define a new user function
X// -------------------------------------------------------------------------
X
Xinline void USERFUNC::defun (UserFuncId index, char *name, CodePtr code, 
X				char *help)
X{
X  func_table[index].define (name, code, help);
X}
X
X// -------------------------------------------------------------------------
X//  defkey: Add a key to envoke the function last defined.
X// -------------------------------------------------------------------------
X
Xinline void USERFUNC::defkey (KEYMAP *kmap, KeySym key, UserFuncId func)
X{
X  kmap->bind (key, func);
X}
X
X// -------------------------------------------------------------------------
X//  defmenu: Define a menu item to call the function last defined.
X// -------------------------------------------------------------------------
X
Xinline void USERFUNC::defmenu (MenuItemId item, UserFuncId func)
X{
X  menu_func[item] = func;
X}
X
X// -------------------------------------------------------------------------
X//  deftool: Define a tool button to call the function last defined.
X// -------------------------------------------------------------------------
X
Xinline void USERFUNC::deftool (int button, char *name, UserFuncId func)
X{
X  tool_func[button] = func;
X  tool_name[button] = name;
X}
X
X// -------------------------------------------------------------------------
X//  init_functions: create a function object for each user function and 
X//	link then in the list 'function_list'. Link entries in keymaps to 
X//	these functions, so they can be called when a key is pressed.
X// -------------------------------------------------------------------------
X
Xvoid USERFUNC::init_functions ()
X{
X// ------------------- EDITING FUNCTIONS ------------------
X
X  groups [FGEdit] = UFNewLine;					// GROUP 0
X  group_names [FGEdit] = "Edit Functions";
X
X    defun (UFNewLine, "new-line", &EDITOR::new_line,
X      "Insert a new line at cursor position.");
X
X    defun (UFOpenLine, "open-line", &EDITOR::open_line,
X      "Open a new line and leave the cursor in the current line.");
X
X    defun (UFDeleteChar, "delete-char", &EDITOR::delete_char,
X	"Delete the character to the right of the cursor.");
X
X    defun (UFBackDeleteChar, "backward-delete-char", &EDITOR::backward_delete_char,
X	"Delete the character to the left of the cursor.");
X
X    defun (UFBackDeleteUntab, "backward-delete-untab", &EDITOR::backward_delete_untab,
X	"Like \"backward-delete-char\", but if the character to be deleted\n"
X	"is a TAB, then convert that TAB into spaces first.");
X
X    defun (UFTabToTabStop, "tab-to-tab-stop", &EDITOR::tab_to_tab_stop,
X      "Insert a TAB into the text.  This moves the cursor to the next tab\n"
X	"stop.  Tab stops are every eight characters.");
X
X    defun (UFHalfTab, "half-tab", &EDITOR::half_tab,
X      "Move cursor to next half-tab stop (every four characters), using TABs\n"
X      "and spaces as appropriate.");
X
X    defun (UFIndent, "indent", &EDITOR::indent,
X      "Move cursor to same indentation as next word on the line above.");
X
X    defun (UFNewLineIndent, "new-line-and-indent", &EDITOR::new_line_and_indent,
X      "Insert a new line and indent as line above.");
X
X    defun (UFCutWord, "cut-word", &EDITOR::cut_word,
X	"Cut the whole word the cursor is currently in and place\n"
X	"it into the paste buffer.");
X
X    defun (UFCutToEOWord, "cut-to-end-of-word", &EDITOR::cut_to_end_of_word,
X	"Cut from the cursor position to the end of the word and place\n"
X	"it into the paste buffer.");
X
X    defun (UFCutLine, "cut-line", &EDITOR::cut_line,
X	"If no selection is on, cut the line in which the cursor is to the\n"
X	"paste buffer.  If a selection is on, cut the selection instead.");
X
X    defun (UFCutToEOLine, "cut-to-end-of-line", &EDITOR::cut_to_end_of_line,
X	"Cut the text from the cursor position to the end of the line and place\n"
X	"it into the paste buffer.");
X
X    defun (UFCutRegion, "cut-region", &EDITOR::cut_region,
X	"Cuts the current region (area between the cursor and mark).  The text is\n"
X	"placed into the paste buffer.  It can then be pasted by using the 'paste'\n"
X	"command.");
X
X    defun (UFCut, "cut", &EDITOR::cut,
X	"Cuts the current selection.  That is: Removes the current selection\n"
X	"from the text and places it into the paste buffer.  It can then be\n"
X	"pasted by using the 'paste' command.");
X
X    defun (UFPaste, "paste", &EDITOR::paste,
X	"Pastes the contents of the paste buffer at the cursor position into\n"
X	"the text.  The paste buffer contains the taxt last selected or cut.");
X
X    defun (UFSelectWord, "select-word", &EDITOR::select_word,
X	"Select the word in which the cursor is.");
X				   
X    defun (UFSelectLine, "select-line", &EDITOR::select_line,
X	"Select the complete line in which the cursor is.");
X
X    defun (UFSelectRegion, "select-region", &EDITOR::select_region,
X	"Select the text area between the mark and the cursor.\n"
X	"The mark must be set before calling this function.");
X
X    defun (UFShiftLeft, "shift-left", &EDITOR::shift_left,
X	"Shift the content of the current line (or, if the selection is on, every\n"
X	"line in the current selection) one character to the left.  If the leftmost\n"
X	"character in the line is not a whitespace character, the function has no\n"
X	"effect.");
X
X    defun (UFShiftRight, "shift-right", &EDITOR::shift_right,
X	"Shift the content of the current line (or, if the selection is on, every\n"
X	"line in the current selection) one character to the right.  This is done\n"
X	"by inserting spaces or TABs at the beginning of the line.");
X
X
X// ----------------- MOVE & SCROLL FUNCTIONS --------------
X
X  groups [FGMove] = UFForwardChar;				// GROUP 1
X  group_names [FGMove] = "Move & Scroll Functions";
X
X    defun (UFForwardChar, "forward-char", &EDITOR::forward_char,
X	"Move the cursor one character forward.");
X
X    defun (UFBackwardChar, "backward-char", &EDITOR::backward_char,
X      "Move the curser one character back.");
X
X    defun (UFForwardWord, "forward-word", &EDITOR::forward_word,
X	"Move the cursor one word forward.");
X
X    defun (UFBackwardWord, "backward-word", &EDITOR::backward_word,
X	"Move the cursor one word back.");
X
X    defun (UFEndOfLine, "end-of-line", &EDITOR::end_of_line,
X	"Move the cursor to the end of the current line.");
X
X    defun (UFBegOfLine, "beginning-of-line", &EDITOR::beginning_of_line,
X	"Move the cursor to the beginning of the current line.");
X
X    defun (UFNextLine, "next-line", &EDITOR::next_line,
X	"Move cursor down into next text line");
X
X    defun (UFPrevLine, "previous-line", &EDITOR::previous_line,
X	"Move cursor up into previous text line");
X
X    defun (UFScrollLineDown, "scroll-line-down", &EDITOR::scroll_line_down,
X	"Scroll text window one line down (text scrolls up)");
X
X    defun (UFScrollLineUp, "scroll-line-up", &EDITOR::scroll_line_up,
X	"Scroll text window one line up (text scrolls down)");
X
X    defun (UFScrollHPDown, "scroll-half-page-down", &EDITOR::scroll_half_page_down,
X	"Scroll text window half a page down (text scrolls up)");
X
X    defun (UFScrollHPUp, "scroll-half-page-up", &EDITOR::scroll_half_page_up,
X	"Scroll text window half a page up (text scrolls down)");
X
X    defun (UFPrevPage, "previous-page", &EDITOR::previous_page,
X	"Move display window to previous page of text.  The original two lines at\n"
X	"the top of the screen are shown at the bottom of the screen.");
X
X    defun (UFNextPage, "next-page", &EDITOR::next_page,
X	"Move display window to next page of text.  The original two lines at the\n"
X	"bottom of the screen are shown at the top of the screen.");
X
X    defun (UFBegOfText, "beginning-of-text", &EDITOR::beginning_of_text,
X	"Move cursor to the beginning of the text. Redisplay if out of screen.");
X
X    defun (UFEndOfText, "end-of-text", &EDITOR::end_of_text,
X	"Move cursor to the end of the text. Redisplay if out of screen.");
X
X    defun (UFSwapCursorMark, "swap-cursor-mark", &EDITOR::swap_point_mark,
X	"Swaps the cursor and the mark. Redisplay the screen if\n"
X	"cursor is not on screen any more.");
X
X// ------------------- FILE FUNCTIONS ---------------------
X
X  group_names [FGFile] = "File Functions";
X  groups [FGFile] = UFNew;					// GROUP 2
X
X#ifdef RED_ONLY
X
X    defun (UFNew, "new", &EDITOR::new_window,
X	"Open a new, empty window.");
X
X    defun (UFOpen, "open", &EDITOR::open_other,
X	"Open a file for editing in another window. A file selection\n"
X	"dialog is shown to select the file.\n");
X#else
X    defun (UFNew, NULL, &EDITOR::empty_function, NULL);
X    defun (UFOpen, NULL, &EDITOR::empty_function, NULL);
X#endif
X
X    defun (UFSave, "save", &EDITOR::save,
X	"Save the buffer to file if modified.\n"
X	"Makes the previous version into a backup file.");
X
X#ifdef RED_ONLY
X
X    defun (UFSaveAs, "save-as", &EDITOR::save_as,
X	"Save the buffer under a new file name.  A file selection box is\n"
X	"opened to specify a name.\n"
X	"Makes the previous version of a file with that name, if it existed,\n"
X	"into a backup file.");
X#else
X    defun (UFSaveAs, NULL, &EDITOR::empty_function, NULL);
X#endif
X
X    defun (UFRevert, "revert", &EDITOR::revert,
X	"Revert the buffer to the last saved version.\n"
X	"All changes since the last save will be lost.");
X
X    defun (UFClose, "close", &EDITOR::close,
X	"Close the editor window.  If it was the only Red window\n"
X	"open, leave Red.");
X
X    defun (UFPrint, "print", &EDITOR::print,
X	"Print the buffer.  The print command and default printer\n"
X	"are specified using the 'Preferences' command from the\n"
X	"Options menu.");
X
X// --------------- CUSTOMISATION FUNCTIONS ----------------
X
X  groups [FGCustom] = UFPreferences;				// GROUP 3
X  group_names [FGCustom] = "Customisation Functions";
X
X    defun (UFPreferences, "preferences", &EDITOR::preferences,
X	"Shows a dialog to set some preferences for behaviour of Red.");
X
X    defun (UFKeyBindings, "key-bindings", &EDITOR::key_bindings,
X	"Opens this dialog that you are watching now.  Shows a list of all\n"
X	"user functions, their key bindings and a short description.  It\n"
X	"also shows the keys which call this function and allows to add or\n"
X	"remove key bindings.");
X
X    defun (UFEditToolb, "edit-toolbar", &EDITOR::edit_toolbar,
X	"NOT YET IMPLEMENTED!!");
X
X    defun (UFSetFonts, "set-fonts", &EDITOR::fonts,
X	"NOT YET IMPLEMENTED!!");
X
X    defun (UFSetColours, "set-colours", &EDITOR::colours,
X	"NOT YET IMPLEMENTED!!");
X
X// -------------------- HELP FUNCTIONS --------------------
X
X  groups [FGHelp] = UFDescribeKey;				// GROUP 4
X  group_names [FGHelp] = "Help Functions";
X
X    defun (UFDescribeKey, "describe-key", &EDITOR::describe_key,
X	"Prints out the name of the function called by a key.  After calling\n"
X	"describe-key, press the key you are interested in and the associated\n"
X	"function will be shown in the information area.");
X
X    defun (UFShowManual, "show-manual", &EDITOR::manual,
X	"Show the online manual for Red.  The manual is an HTML document and a\n"
X	"WWW browser is started to display it.  The default browser is Netscape,\n"
X	"but this can be changed using Red's X resources (see the \"Customisation\"\n"
X	"section in the manual).");
X
X// ------------------- MISC. FUNCTIONS --------------------
X
X  groups [FGMisc] = UFUndo;					// GROUP 5
X  group_names [FGMisc] = "Misc. Functions";
X
X    defun (UFUndo, "undo", &EDITOR::undo,
X	"Undo the last editing command.  Undo can be called repeatedly to undo\n"
X	"several previous changes.  The number of possible undos currently is 40.\n"
X	"Undos themselves are recorded on the undo-stack and can be undone (redo).\n"
X	"To redo, use a non-undo function (e.g. move cursor), then undo again.");
X
X    defun (UFFind, "find", &EDITOR::find,
X	"Find a word or string in the buffer. Starts a dialog to specify\n"
X	"some options.  Find forward is the default option.");
X
X    defun (UFFindBackward, "find-backward", &EDITOR::find_backward,
X	"Same as \"find\", but the default button is \"Find Backward\"");
X
X    defun (UFFindNext, "find-next", &EDITOR::find_next,
X	"Finds the string currently selected or, if nothing is selected, the same pattern\n"
X	"as in the last find operation.  Uses the same parameters (direction, case\n"
X	"sensitivity, whole word option) as the last find operation. Stops at the end\n"
X	"of the buffer. If called again, wraps around to the beginning (end) of the buffer.");
X
X    defun (UFFindNextRev, "find-next-reverse", &EDITOR::find_next_reverse,
X	"Like \"find-next\", but reverse the direction of the search.");
X
X    defun (UFReplace, "replace", &EDITOR::replace,
X	"Replace string in the buffer with another one. Starts a dialog to specify\n"
X	"some options.");
X
X    defun (UFSetMark, "set-mark", &EDITOR::set_mark,
X	"Set the mark at the current cursor position.");
X
X    defun (UFGotoLine, "goto-line", &EDITOR::goto_line,
X	"Move cursor to line by line number.  The line number is prompted for\n"
X	"after starting this command.\n");
X
X    defun (UFShowLine, "show-line-number", &EDITOR::show_line,
X	"Display the current line number in the information area.");
X
X    defun (UFDefMacro, "define-macro", &EDITOR::define_macro,
X	"Record the following keq sequence until the next call to\n"
X	"\"end-macro\" and store it as a macro.\n\n"
X	"(NOT YET IMPLEMENTED!)");
X
X    defun (UFEndMacro, "end-macro", &EDITOR::end_macro,
X	"End the current macro recording, ask for a key, and bind execution\n"
X	"of the macro to that key. (Must be currently defining a macro.\n\n"
X	"(NOT YET IMPLEMENTED!)");
X
X    defun (UFRunMacro, "run-macro", &EDITOR::run_macro,
X	"...\n"
X	"...\n\n"
X	"(NOT YET IMPLEMENTED!)");
X
X#ifndef RED_ONLY
X
X    defun (UFInterface, "interface", &EDITOR::interface_toggle,
X	"NOT YET IMPLEMENTED!!");
X#else
X    defun (UFInterface, NULL, &EDITOR::empty_function, NULL);
X
X#endif
X
X    defun (UFRedisplay, "redisplay", &EDITOR::redisplay,
X	"Center cursor in the window and redisplay the screen.");
X
X    defun (UFBlueNewRout, "blue-new-routine", &EDITOR::blue_new_routine,
X	"Insert a skeleton for a Blue routine into the text.");
X
X    defun (UFStatus, "edit-status", &EDITOR::edit_debug,
X	"Print out some debugging information.");
X
X#ifndef RED_ONLY
X
X    defun (UFCompile, "compile", &EDITOR::compile,
X	"Compile this class.  If an error is detected, show the error.");
X
X    defun (UFGotoError, "goto-error", &EDITOR::goto_error,
X	"Go to the error that was detected during the last compilation and\n"
X	"display the error message again.\n"
X	"This is only possible if a compilation was made and resulted in an error.");
X#else
X    defun (UFCompile, NULL, &EDITOR::empty_function, NULL);
X    defun (UFGotoError, NULL, &EDITOR::empty_function, NULL);
X#endif
X
X// --------------------- DEBUG FUNCTIONS --------------------
X
X#ifndef RED_ONLY
X
X  groups [FGDebug] = UFSetBreak;					// GROUP 5
X  group_names [FGDebug] = "Debug Functions";
X
X    defun (UFSetBreak, "set-breakpoint", &EDITOR::set_breakpoint,
X	"Set a breakpoint at the current line.\n"
X	"A breakpoint can only be set after the class was compiled.");
X
X    defun (UFClearBreak, "clear-breakpoint", &EDITOR::clear_breakpoint,
X	"Clear the breakpoint in the current line.\n"
X	"If there is no breakpoint in the current line, this function has no effect.");
X
X    defun (UFPrintVar, "print-variable", &EDITOR::print_variable,
X	"Print the current value of a variable.\n"
X	"This is possible only after the machine has been started and stopped at\n"
X	"a breakpoint.  The variable name must be highlighted before this function\n"
X	"is called.");
X
X    defun (UFWatchVar, "watch-variable", &EDITOR::watch_variable,
X	"Declare a variable as a \"watch variable\".  The execution of the project\n"
X	"will be interrupted (as at a breakpoint) each time the value of a watch\n"
X	"variable changes.  The variable name must be highlighted before this\n"
X	"function is called.");
X
X    defun (UFStep, "step", &EDITOR::step,
X	"Execute the next line of the program text.\n"
X	"This is possible only if the machine has been started and stopped.");
X
X    defun (UFStepInto, "step-into", &EDITOR::step_into,
X	"\"step-into\" is similar to \"step\", but if the statement to be executed is a\n"
X	"routine call, then \"step-into\" goes to the next line inside the routine (while \n"
X	"\"step\" executes the whole routine as one statement).");
X
X    defun (UFContinue, "continue", &EDITOR::continue_exec,
X	"Continue execution of the project after it has been interrupted.");
X
X    defun (UFTerminate, "terminate", &EDITOR::terminate_exec,
X	"Terminate execution of a project after it has been interrupted.");
X#else
X    defun (UFSetBreak, NULL, &EDITOR::empty_function, NULL);
X    defun (UFClearBreak, NULL, &EDITOR::empty_function, NULL);
X    defun (UFPrintVar, NULL, &EDITOR::empty_function, NULL);
X    defun (UFWatchVar, NULL, &EDITOR::empty_function, NULL);
X    defun (UFStep, NULL, &EDITOR::empty_function, NULL);
X    defun (UFStepInto, NULL, &EDITOR::empty_function, NULL);
X    defun (UFContinue, NULL, &EDITOR::empty_function, NULL);
X    defun (UFTerminate, NULL, &EDITOR::empty_function, NULL);
X#endif
X
X// -------------------- HIDDEN FUNCTIONS --------------------
X
X  groups [FGHidden] = UFSelfInsert;		// last group (grp 6) is hidden
X  group_names [FGHidden] = "Hidden Functions";
X
X    defun (UFSelfInsert, "self-insert", &EDITOR::self_insert_char, "");
X}
X
X// -------------------------------------------------------------------------
X//  init_function_bindings: Initialise all menu, tool bar and key bindings.
X// -------------------------------------------------------------------------
X
Xvoid USERFUNC::init_function_bindings (Widget parent)
X{
X  if (!load_key_bindings (parent))		// try to load from file
X    default_key_bindings ();
X
X  init_menu_bindings ();
X}
X
X
X// -------------------------------------------------------------------------
X//  init_menu_bindings: Initialise all menu and tool bar bindings.
X// -------------------------------------------------------------------------
X
Xvoid USERFUNC::init_menu_bindings ()
X{
X#ifdef RED_ONLY
X      defmenu (NewItem, UFNew);
X      defmenu (OpenItem, UFOpen);
X#endif
X      defmenu (SaveItem, UFSave);
X#ifdef RED_ONLY
X      defmenu (SaveAsItem, UFSaveAs);
X#endif
X      defmenu (RevertItem, UFRevert);
X      defmenu (PrintItem, UFPrint);
X      defmenu (CloseItem, UFClose);
X
X      defmenu (UndoItem, UFUndo);
X      defmenu (CutItem, UFCut);
X      defmenu (PasteItem, UFPaste);
X
X      defmenu (FindItem, UFFind);
X      defmenu (FindNextItem, UFFindNext);
X      defmenu (ReplaceItem, UFReplace);
X      defmenu (GotoLineItem, UFGotoLine);
X      defmenu (ShowLineItem, UFShowLine);
X#ifndef RED_ONLY
X      defmenu (CompileItem, UFCompile);
X      defmenu (GotoErrItem, UFGotoError);
X      defmenu (InterfaceItem, UFInterface);
X#endif
X
X      defmenu (SetBreakItem, UFSetBreak);
X      defmenu (ClearBreakItem, UFClearBreak);
X      defmenu (WatchItem, UFWatchVar);
X      defmenu (PrintVarItem, UFPrintVar);
X      defmenu (StepItem, UFStep);
X      defmenu (StepIntoItem, UFStepInto);
X      defmenu (ContinueItem, UFContinue);
X      defmenu (TerminateItem, UFTerminate);
X
X      defmenu (PrefItem, UFPreferences);
X      defmenu (KeyBindItem, UFKeyBindings);
X      defmenu (EditToolbarItem, UFEditToolb);
X      defmenu (FontsItem, UFSetFonts);
X      defmenu (ColoursItem, UFSetColours);
X
X      defmenu (FunctionsItem, UFKeyBindings);
X      defmenu (DescribeItem, UFDescribeKey);
X      defmenu (ManualItem, UFShowManual);
X
X#ifdef RED_ONLY
X      deftool (0, "Save", UFSave);
X      deftool (1, "Open...", UFOpen);
X      deftool (2, "Undo", UFUndo);
X      deftool (3, "Cut", UFCut);
X      deftool (4, "Paste", UFPaste);
X      deftool (5, "Find...", UFFind);
X      deftool (6, "Find Next", UFFindNext);
X      deftool (7, "Replace...", UFReplace);
X      deftool (8, "Close", UFClose);
X#else
X      deftool (0, "Interface", UFInterface);
X      deftool (1, "Compile", UFCompile);
X      deftool (2, "Undo", UFUndo);
X      deftool (3, "Cut", UFCut);
X      deftool (4, "Paste", UFPaste);
X      deftool (5, "Find...", UFFind);
X      deftool (6, "Find Next", UFFindNext);
X      deftool (7, "Replace...", UFReplace);
X      deftool (8, "Close", UFClose);
X#endif
X}
X
X// -------------------------------------------------------------------------
X//  default_key_bindings: Set the key bindings to the default.
X// -------------------------------------------------------------------------
X
Xvoid USERFUNC::default_key_bindings ()
X{
X      plain_keymap->init ();
X      shift_keymap->init ();
X      ctrl_keymap->init ();
X      alt_keymap->init ();
X      shift_ctrl_keymap->init ();
X      shift_alt_keymap->init ();
X      ctrl_alt_keymap->init ();
X
X      plain_keymap->bind_printables (UFSelfInsert);
X      shift_keymap->bind_printables (UFSelfInsert);
X
X// ------------------- EDITING FUNCTIONS ------------------
X
X      defkey (plain_keymap, XK_Return, UFNewLine);
X
X      defkey (ctrl_keymap, XK_O, UFOpenLine);
X
X      defkey (ctrl_keymap, XK_D, UFDeleteChar);
X      defkey (ctrl_keymap, XK_Right, UFDeleteChar);
X      defkey (plain_keymap, XK_Delete, UFDeleteChar);
X
X      defkey (plain_keymap, XK_BackSpace, UFBackDeleteChar);
X      defkey (ctrl_keymap, XK_Left, UFBackDeleteChar);
X
X      defkey (alt_keymap, XK_comma, UFShiftLeft);
X      defkey (alt_keymap, XK_period, UFShiftRight);
X
X      defkey (plain_keymap, XK_Tab, UFTabToTabStop);
X
X      defkey (shift_keymap, XK_Tab, UFIndent);
X
X      defkey (shift_keymap, XK_Return, UFNewLineIndent);
X
X      defkey (ctrl_alt_keymap, XK_Left, UFCutWord);
X
X      defkey (ctrl_alt_keymap, XK_Right, UFCutToEOWord);
X
X      defkey (shift_ctrl_keymap, XK_Left, UFCutLine);
X      defkey (plain_keymap, XK_F4, UFCutLine);
X
X      defkey (shift_ctrl_keymap, XK_Right, UFCutToEOLine);
X      defkey (ctrl_keymap, XK_K, UFCutToEOLine);
X
X      defkey (shift_ctrl_keymap, XK_C, UFCutRegion);
X
X      defkey (alt_keymap, XK_X, UFCut);
X      defkey (plain_keymap, XK_F1, UFCut);
X
X      defkey (plain_keymap, XK_F3, UFPaste);
X      defkey (alt_keymap, XK_V, UFPaste);
X      defkey (plain_keymap, XK_Insert, UFPaste);
X
X      defkey (ctrl_keymap, XK_W, UFSelectWord);
X				   
X      defkey (plain_keymap, XK_F2, UFSelectLine);
X      defkey (ctrl_keymap, XK_L, UFSelectLine);
X
X      defkey (shift_ctrl_keymap, XK_space, UFSelectRegion);
X      defkey (plain_keymap, XK_F8, UFSelectRegion);
X
X// ----------------- MOVE & SCROLL FUNCTIONS --------------
X
X      defkey (plain_keymap, XK_Right, UFForwardChar);
X      defkey (plain_keymap, XK_Left, UFBackwardChar);
X      defkey (alt_keymap, XK_Right, UFForwardWord);
X      defkey (alt_keymap, XK_Left, UFBackwardWord);
X      defkey (shift_keymap, XK_Right, UFEndOfLine);
X      defkey (shift_keymap, XK_Left, UFBegOfLine);
X      defkey (plain_keymap, XK_Down, UFNextLine);
X      defkey (plain_keymap, XK_Up, UFPrevLine);
X      defkey (shift_keymap, XK_Down, UFScrollLineDown);
X      defkey (shift_keymap, XK_Up, UFScrollLineUp);
X      defkey (shift_alt_keymap, XK_Down, UFScrollHPDown);
X      defkey (shift_alt_keymap, XK_Up, UFScrollHPUp);
X      defkey (plain_keymap, XK_Page_Up, UFPrevPage);
X      defkey (alt_keymap, XK_Up, UFPrevPage);
X      defkey (plain_keymap, XK_Page_Down, UFNextPage);
X      defkey (alt_keymap, XK_Down, UFNextPage);
X      defkey (plain_keymap, XK_Home, UFBegOfText);
X      defkey (shift_ctrl_keymap, XK_Up, UFBegOfText);
X      defkey (plain_keymap, XK_End, UFEndOfText);
X      defkey (shift_ctrl_keymap, XK_Down, UFEndOfText);
X      defkey (ctrl_keymap, XK_X, UFSwapCursorMark);
X
X// ------------------- FILE FUNCTIONS ---------------------
X
X#ifdef RED_ONLY
X      defkey (alt_keymap, XK_N, UFNew);
X      defkey (alt_keymap, XK_O, UFOpen);
X#endif
X      defkey (alt_keymap, XK_S, UFSave);
X      defkey (alt_keymap, XK_W, UFClose);
X      defkey (alt_keymap, XK_P, UFPrint);
X
X// --------------- CUSTOMISATION FUNCTIONS ----------------
X
X      defkey (alt_keymap, XK_K, UFKeyBindings);
X
X// -------------------- HELP FUNCTIONS --------------------
X
X      defkey (alt_keymap, XK_D, UFDescribeKey);
X
X// ------------------- MISC. FUNCTIONS --------------------
X
X      defkey (alt_keymap, XK_Z, UFUndo);
X      defkey (plain_keymap, XK_F12, UFUndo);
X
X      defkey (alt_keymap, XK_F, UFFind);
X
X      defkey (shift_alt_keymap, XK_F, UFFindBackward);
X
X      defkey (alt_keymap, XK_G, UFFindNext);
X
X      defkey (shift_alt_keymap, XK_G, UFFindNextRev);
X
X      defkey (alt_keymap, XK_R, UFReplace);
X
X      defkey (ctrl_keymap, XK_space, UFSetMark);
X      defkey (plain_keymap, XK_F7, UFSetMark);
X
X      defkey (plain_keymap, XK_F5, UFGotoLine);
X
X      defkey (plain_keymap, XK_F6, UFShowLine);
X
X      defkey (ctrl_keymap, XK_bracketleft, UFDefMacro);
X
X      defkey (ctrl_keymap, XK_bracketright, UFEndMacro);
X      defkey (ctrl_keymap, XK_M, UFRunMacro);
X
X      defkey (alt_keymap, XK_L, UFRedisplay);
X
X      defkey (ctrl_keymap, XK_R, UFBlueNewRout);
X
X      defkey (shift_ctrl_keymap, XK_D, UFStatus);
X
X// --------------------- DEBUG FUNCTIONS --------------------
X
X#ifndef RED_ONLY
X
X      defkey (alt_keymap, XK_C, UFCompile);
X      defkey (alt_keymap, XK_E, UFGotoError);
X
X      defkey (ctrl_keymap, XK_B, UFSetBreak);
X      defkey (shift_ctrl_keymap, XK_B, UFClearBreak);
X
X      defkey (ctrl_keymap, XK_P, UFPrintVar);
X      defkey (ctrl_keymap, XK_V, UFWatchVar);
X
X      defkey (ctrl_keymap, XK_S, UFStep);
X      defkey (shift_ctrl_keymap, XK_S, UFStepInto);
X      defkey (ctrl_keymap, XK_U, UFContinue);
X      defkey (ctrl_keymap, XK_T, UFTerminate);
X#endif
X
X}
SHAR_EOF
  $shar_touch -am 0512222696 'Red/Red/userfunc.cc' &&
  chmod 0640 'Red/Red/userfunc.cc' ||
  echo 'restore of Red/Red/userfunc.cc failed'
  shar_count="`wc -c < 'Red/Red/userfunc.cc'`"
  test 50892 -eq "$shar_count" ||
    echo "Red/Red/userfunc.cc: original size 50892, current size $shar_count"
  rm -f _sharnew.tmp
fi
# ============= Red/Red/userfunc.h ==============
if test -f 'Red/Red/userfunc.h' && test X"$1" != X"-c"; then
  echo 'x - skipping Red/Red/userfunc.h (file already exists)'
  rm -f _sharnew.tmp
else
  > _sharnew.tmp
  echo 'x - extracting Red/Red/userfunc.h (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'Red/Red/userfunc.h' &&
X#ifndef USERFUNC_H
X#define USERFUNC_H
X
X// ===========================================================================
X//
X//  Class name: USERFUNC
X//
X//  Author: M. Koelling							1995
X//
X//  Description:
X//
X//  Class specification of class USERFUNC.  This class manages the functions
X//  that are accessible to the user via key sequences or menus.  It holds
X//  internally a linked list of function objects, each describing one user
X//  function.  Functions are grouped into groups (sometimes called 
X//  categories).  A table (groups) exists with one entry for each group.  The
X//  entries in that table are pointers into the function list pointing to the
X//  first functin in that group.  From there on, all functions belong to the 
X//  same group up to the function pointed to by the next group pointer.  The
X//  last group are hidden functions.  Hidden functions are not to be shown in
X//  the key definition dialog, since redefinition by the user is not allowed.
X//
X// ===========================================================================
X
X#include <Xm/Xm.h>
X
X#include "function.h"
X#include "keymap.h"
X
X// IDs for menus
X
Xenum MenuId { 
X  FileMenu, EditMenu, ViewMenu, ToolsMenu, OptionsMenu, HelpMenu 
X};
X
X// IDs for each menu item
X
Xenum MenuItemId { 
X  NewItem, OpenItem, SaveItem, SaveAsItem, RevertItem, PrintItem, CloseItem,
X  UndoItem, CutItem, PasteItem,
X  InterfaceItem,
X  FindItem, FindNextItem, ReplaceItem, GotoLineItem, ShowLineItem, CompileItem,
X  GotoErrItem,
X  SetBreakItem, ClearBreakItem, WatchItem, PrintVarItem, StepItem, StepIntoItem,
X  ContinueItem, TerminateItem,
X  PrefItem, KeyBindItem, EditToolbarItem, FontsItem, ColoursItem,
X  AboutItem, CopyrightItem, FunctionsItem, DescribeItem, MouseItem, ManualItem, 
X  ErrorItem,
X  NR_OF_ITEMS
X};
X
X// enum UserFuncId is defined in keymap.h
X
Xenum FuncGroupId {
X  FGEdit, FGMove, FGFile, FGCustom, FGHelp, FGMisc,
X  #ifndef RED_ONLY
X    FGDebug,
X  #endif
X   FGHidden, NR_OF_GROUPS
X};
X
X// IDs for buttons in key binding dialog
X
Xenum FuncDlgButton { AddKeyButton, DeleteKeyButton, CloseButton,
X		      DefaultsButton, HelpButton };
X
Xconst int NR_OF_BUTTONS=9;	// number of buttons in the toolbar
Xconst int MAX_KEYS=20;		// maximum keys for one function
X
X
X
Xclass USERFUNC {
X
X    friend void func_dlg_defkey_cb (Widget w, USERFUNC* userfunc, 
X				XEvent* event, Boolean* ctd);
X    friend void replace_question_cb (Widget w, XtPointer dlg, XtPointer call_data);
X    friend void defaults_question_cb (Widget w, XtPointer dlg, XtPointer call_data);
X    friend void func_dlg_button_cb (Widget w, XtPointer button_no, XtPointer call_data);
X    friend void func_dlg_menu_cb (Widget w, XtPointer item, XtPointer call_data);
X    friend void func_dlg_list_cb (Widget w, USERFUNC* userfunc, 
X				XmListCallbackStruct* call_data);
X    friend void func_dlg_keylist_cb (Widget w, USERFUNC* userfunc, 
X				XmListCallbackStruct* call_data);
X
X  public:
X    USERFUNC (Widget parent);		// Create and bind all user functions
X
X    ~USERFUNC ();
X
X    UserFuncId translate_key (		// Given a key and a modifier,
X	KeySym key,			//  translate that key to a function
X	unsigned int modifiers);	//  code
X
X    UserFuncId translate_item (		// Given a menu item, translate that 
X	MenuItemId item);		//  item to a function code
X
X    UserFuncId translate_toolbutton (	// Given a tool button number, 
X	int button);			//  translate that button to a 
X					//  function code
X    char* toolbutton_name (		// Return the name of a 
X	int button_no);			//  toolbar button
X
X    XmString funcname (			// Return the function name for a
X	UserFuncId func);		//  user function
X
X    CodePtr code_pointer (		// Return the pointer to the code
X	UserFuncId func);		//  of a user function
X
X    XmString help (UserFuncId func);	// Return the help string of a 
X					//  user function
X
X    void show_dialog (Widget parent);	// Show the function reference dialog
X
X    KEYMAP *plain_keymap, *shift_keymap,	// The seven keymaps used by
X	   *ctrl_keymap, *alt_keymap,		// the editors. Each keymap is
X	   *shift_ctrl_keymap,			// used for a certain modifier
X	   *shift_alt_keymap,			// combination to determine the
X	   *ctrl_alt_keymap;			// meaning of a keypress.
X
X  private:
X    void push_button (			// A Button in the dialog was pressed
X	FuncDlgButton button);
X
X    void menu_select (int item);	// A new item in the categories menu
X					//  was selected
X    void list_select (int item);	// A new item in the function list was
X					//  selected
X    void key_list_select (int item);	// A new item in the key list was
X					//  selected
X    void add_new_key1 (XKeyPressedEvent *event);
X    void add_new_key2 (Boolean cont);
X
X    void key_add ();			// The Add-key button was pressed
X
X    void key_delete ();			// The Delete-key button was pressed
X
X    void set_defaults1 ();		// The Set-defaults button was pressed
X    void set_defaults2 ();
X
X    void close_dialog ();		// Close the function reference dialog
X
X    void clear_list ();			// Clear the function list
X
X    void make_list ();			// Build the function list for the
X					//  current group (category)
X    void add_to_list (UserFuncId func);	// Add one function to the function
X					//   list
X    void clear_keylist ();		// Clear the key list
X
X    void add_to_keylist (KEYMAP* kmap, 	// Add an item to the key list
X	KeySym key);
X
X    void clear_description ();		// Clear the description field
X
X    void display_description (		// Display the help text of func in the
X	UserFuncId func);		//  description field
X
X    void search_keys (UserFuncId func);	// Search for all keys calling func and
X					//  add them to key-list
X    void search_keymap (KEYMAP* km,	// Search one keymap for keys calling
X	UserFuncId func);		//  func and add them to key-list
X
X    UserFuncId get_func (int group,	// Given a group and an item number for
X	int item);			//  that group, find the function
X
X    KEYMAP *find_keymap (		// Given the modifiers, find the corrosponding
X	unsigned int modifiers);	//  keymap;
X
X    void save_key_bindings ();		// save the key bindings to disk
X
X    Boolean load_key_bindings (		// load the key bindings from disk
X		Widget parent);
X
X
X    inline void defun (		 	// define a function
X		UserFuncId index,
X		char *name,
X		CodePtr code,
X		char *help);
X
X    inline void defkey (KEYMAP *kmap,	// define a key->function mapping
X		KeySym key, 
X		UserFuncId func);
X
X    inline void defmenu (MenuItemId item,	// define a menu-item->function mapping
X		UserFuncId func);
X
X    inline void deftool (int button,	// define a tool_button->func mapping
X		char *name,
X		UserFuncId func);
X
X    void init_functions ();		// create all the user functions
X
X    void init_function_bindings (	// initialise key bindings
X		Widget parent);
X
X    void init_menu_bindings ();		// initialise key bindings
X
X    void default_key_bindings ();	// set key bindings to defaults
X
X  // VARIABLES:
X
X    FUNCTION func_table[NR_OF_USERFUNC];	// Array of user functions
X    UserFuncId menu_func[NR_OF_ITEMS];		// Array of indices to menu functions
X    UserFuncId tool_func[NR_OF_BUTTONS];	// Array of indices to tool but funcs
X    char* tool_name[NR_OF_BUTTONS];		// Array of names of tool buttons
X    UserFuncId groups[NR_OF_GROUPS];		// Array of indices to function groups
X    char* group_names[NR_OF_GROUPS];		// Array with names of groups
X
X    int current_group;			// The function group currently shown
X    UserFuncId current_func;		// The function currently shown
X    int current_key;			// The key item currently selected
X    int no_of_keys;			// current number of keys displayed
X    KEYMAP *key_maps[MAX_KEYS];		// The keymaps for the items in the key list
X    KeySym key_syms[MAX_KEYS];		// The keysyms for the items in the key list
X    Boolean keys_modified;		// True, if key bindings have been modified
X    KEYMAP *add_kmap;			// Keymap used in adding a new key
X    KeySym add_keysym;			// Keysym used in adding a new key
X
X    Widget func_dlg;			// The dialog for function and key display
X    Widget func_list;			// The function list in that dialog
X    Widget key_area, key_list;		// The key list in that dialog
X    Widget descr_area, descr_label;	// The description field for function help
X    Widget but_delkey, but_addkey;	// The delete and add key buttons
X    Widget category_menu;
X    Widget button_area, key_button_area;
X};
X
X#endif
SHAR_EOF
  $shar_touch -am 0512222696 'Red/Red/userfunc.h' &&
  chmod 0640 'Red/Red/userfunc.h' ||
  echo 'restore of Red/Red/userfunc.h failed'
  shar_count="`wc -c < 'Red/Red/userfunc.h'`"
  test 8270 -eq "$shar_count" ||
    echo "Red/Red/userfunc.h: original size 8270, current size $shar_count"
  rm -f _sharnew.tmp
fi
# ============= Red/red-manual/about-manual.html ==============
if test ! -d 'Red/red-manual'; then
  echo 'x - creating directory Red/red-manual'
  mkdir 'Red/red-manual'
fi
if test -f 'Red/red-manual/about-manual.html' && test X"$1" != X"-c"; then
  echo 'x - skipping Red/red-manual/about-manual.html (file already exists)'
  rm -f _sharnew.tmp
else
  > _sharnew.tmp
  echo 'x - extracting Red/red-manual/about-manual.html (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'Red/red-manual/about-manual.html' &&
X<html>
X<head>
X<title>Red: About this Manual</title>
X<link rev="made" href="mailto:mik@cs.su.oz.au">
X</head>
X
X<body bgcolor=white>
X
X<center>
X<table>
X<tr>
X	<td valign=top> <IMG SRC="red-logo.GIF" ALT="RED" > </td>
X	<td> <font size=+3>About This Manual</font> </td>
X</tr>
X</table>
X</center>
X
X<hr>
X
XThis manual dscribes the functionality of Red.  It is devided into two parts:
XA <strong>tutorial section</strong> and a <strong>reference section</strong>.
XBoth are accessible from the <A HREF="red-main-page.html">main index</A>.
X<p>
X
XThe tutorial section is still under construction and parts have not yet been
Xwritten.  It will eventually give new users an introduction to the main edit
Xfacilities.
X<p>
X
XThe reference section is more or less complete, although not quite as detailed
Xas it will eventually be.  This should help users find out about functions 
Xavailable in Red.  It is a longer version of the function help that is built
Xin in Red (in the function description/key binding dialog).
X<p>
X
XDevelopment has slowed down quite a bit in the last time and Red has come to
Xsomething close to a stable state.  I am currently trying not to make too
Xmany changes to the editor any more.  But there still are changes every now and
Xthen - bug fixes or implementation of new functionality.  These are announced
Xin the <a href="changes.html">Recent changes</a> section.  If you use Red
Xregularly, have a look in here every now and then.
X
X<HR>
X<A HREF="red-main-page.html">
X   <IMG SRC="../graphics/buttons/back.gif" border=0 align=middle>
X   Main Index</A>
X
X</body>
X</html>
SHAR_EOF
  $shar_touch -am 0512222696 'Red/red-manual/about-manual.html' &&
  chmod 0640 'Red/red-manual/about-manual.html' ||
  echo 'restore of Red/red-manual/about-manual.html failed'
  shar_count="`wc -c < 'Red/red-manual/about-manual.html'`"
  test 1569 -eq "$shar_count" ||
    echo "Red/red-manual/about-manual.html: original size 1569, current size $shar_count"
  rm -f _sharnew.tmp
fi
# ============= Red/red-manual/about-red.html ==============
if test -f 'Red/red-manual/about-red.html' && test X"$1" != X"-c"; then
  echo 'x - skipping Red/red-manual/about-red.html (file already exists)'
  rm -f _sharnew.tmp
else
  > _sharnew.tmp
  echo 'x - extracting Red/red-manual/about-red.html (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'Red/red-manual/about-red.html' &&
X<html>
X<head>
X<title>Red: About Red</title>
X<link rev="made" href="mailto:mik@cs.su.oz.au">
X</head>
X
X<body bgcolor=white>
X
X<center>
X<table>
X<tr>
X	<td> <font size=+3>About</font> </td>
X	<td valign=top> <IMG SRC="red-logo.GIF" ALT="RED" > </td>
X</tr>
X</table>
X</center>
X
X<hr>
X
X
XRed is a text editor running under Unix and X Windows.<p>
X
XIt was developed at the <A HREF="http://www.cs.su.oz.au/basser_home.html">
XBasser Department of Computer Science</A> as part of the 
X<A HREF="http:/~mik/blue/blue.html">Blue Programming Environment</A>.<p>
X
XThe original version of Red runs in the integrated programming environment
Xfor Blue, but a stand-alone version of the editor has been produced.  This
Xstand-alone version lacks all the features that are specificly tailored for 
XBlue, but rather acts as a normal general purpose text editor.<p>
X
XIt was designed to be both easy to use and flexible. The Blue environment
Xis aimed at beginners, so consequently Red is designed with non-expert users
Xin mind.  The basic editing functions are easy to understand and to use.
XSome effort was made to combine this ease of use with some flexibility and
Xpower so that users are not artificially held on a non-expert level over a 
Xlong period of time by the editor. (Some programs that claim to be 
X"idiot-proof" tend to treat the users as idiots and take care that they 
Xremain as such...)<p>
X
XIt is intended that a user with growing expertise can use Red in a more
Xpowerful way, making Red a good choice for expert users as well.<p>
X
XAt the moment, not the complete functionality of Red has been implemented,
Xand features are in some areas somewhat restricted.  But it is in a state 
Xwhere it is usable and I encourage everyone to have a look at it and see 
SHAR_EOF
  : || echo 'restore of Red/red-manual/about-red.html failed'
fi
echo 'End of archive part 7'
echo 'File Red/red-manual/about-red.html is continued in part 8'
echo 8 > _sharseq.tmp
exit 0


