home *** CD-ROM | disk | FTP | other *** search
- /* wimpio.c */
-
- /* Copyright (C) David Elworthy 1991. No warranty is given on this code. */
-
- /*
- 1.0 -- April 1991 -- First working version
- 2.0 -- July 1991 -- Added extra handle to w_event
- -- Allow file drop on read window
- -- Use control block
- 2.1 -- Oct 1991 -- Change drop handler
- Reading from input icon changed
- 2.2 -- Dec 1991 -- Fix menu bug: menus can now only be put up
- when we are waiting for input.
-
- * This file contains code for implementing printf and read via the WIMP.
- * The external functions, create, show, hide and delete the printf
- * window.
- * It would be nice to implement scanf as well, but the lack of a vsscanf function
- * in the ANSI library makes this next to impossible.
- *
- * PRINTF
- * We keep a buffer of text in memory. On a redraw event, we plot it into the
- * window.
- * The text is held as an array of characters, with newlines as character
- * separators. Control characters are kept in the text but not displayed.
- * This allows us to use a buffer supplied by the user.
- *
- * In plotting, we have line breaks on newlines and on filling the width of the
- * work area. To allow for mode changes, the physical line length is
- * recalculated every time. We assume the window work area will not change --
- * reasonable in most cases.
- *
- * READ
- * Input is read through a writeable icon in a window, returning on return or OK.
- * The text is read into a local buffer initially. Close or CANCEL causes the function
- * to return as if there had been no input.
- * If there are any extra icons, then a click on them has the same effect as entering their
- * text into the writeable icon. This applies for icons number > the input icon.
- */
-
-
- #include "wimp.h"
- #include "wimpt.h"
- #include "win.h"
- #include "template.h"
- #include "werr.h"
- #include "bbc.h"
- #include "coords.h"
- #include "event.h"
- #include "heap.h"
- #include "xferrecv.h"
- #include "baricon.h"
- #include "menu.h"
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <stdarg.h>
- #include "wimpio.h"
-
- /*---------------------------------------- CONTROL BLOCK---------------------------------*/
-
- wimpio_cb wimpio_control =
- {
- -1, /* Printf window handle */
- -1 /* Read window handle */
- };
-
- /* Close icon string */
- static char *close_string = NULL;
-
- /*---------------------------------------- COMMON ----------------------------------------*/
-
- /* Show the window */
- static void w_show(BOOL exists, wimp_w handle)
- {
- wimp_wstate state;
-
- if (exists)
- {
- /* Get the state of the window */
- if (wimpt_complain(wimp_get_wind_state(handle, &state)) == 0)
- {
- state.o.behind = -1; /* Make sure window is opened in front */
- wimpt_noerr(wimp_open_wind(&state.o));
- }
- }
- }
-
- /* Hide the window */
- static void w_hide(BOOL exists, wimp_w handle)
- {
- if (exists)
- wimpt_noerr(wimp_close_wind(handle));
- }
-
- /* Delete the window */
- static void w_delete(BOOL *exists, wimp_w *handle)
- {
- if (*exists)
- {
- wimpt_noerr(wimp_delete_wind(*handle));
- *handle = -1;
- *exists = FALSE;
- win_activedec();
- }
- }
-
- void w_event(wimp_eventstr *e, w_redraw_fn redraw, BOOL *closed, void *handle)
- {
- if (closed) *closed = FALSE;
-
- /* Deal with event */
- switch (e->e)
- {
- case wimp_EREDRAW:
- {
- int more;
- wimp_redrawstr r;
-
- wimpt_checkmode(); /* This means wimpt_mode will work */
-
- r.w = e->data.o.w;
- wimpt_noerr(wimp_redraw_wind(&r, &more));
- while (more)
- {
- if (redraw != NULL) redraw(&r, handle);
- wimp_get_rectangle(&r, &more);
- }
- break;
- }
-
- case wimp_EOPEN: /* Pass on open request */
- wimpt_noerr(wimp_open_wind(&e->data.o));
- break;
-
- case wimp_ECLOSE:
- wimpt_noerr(wimp_close_wind(e->data.o.w));
- if (closed) *closed = TRUE;
- break;
-
- default: /* Ignore any other event */
- break;
- }
- }
-
- /* Standard window creator */
- wimp_w w_create(char *title, char *templatename, int *nicons)
- {
- wimp_wind *window; /* Pointer to window definition */
- wimp_w handle;
-
- /* Find template for the window */
- window = template_syshandle(templatename);
- if (window == 0)
- {
- werr(0, "Template not found");
- return -1;
- }
-
- /* Set title string (must be indirect and long enough) */
- strcpy(window->title.indirecttext.buffer, title);
-
- /* Create the window, dealing with errors */
- if (wimpt_complain(wimp_create_wind(window, &handle)))
- return -1;
-
- win_activeinc();
- if (nicons) *nicons = window->nicons;
- return handle;
- }
-
- /*---------------------------------------- PRINTF ----------------------------------------*/
-
- /* Flag - does window exist */
- static BOOL printf_window_exists = FALSE;
-
- /* Bottom left of window work area, and width */
- static int wa_base, wa_left, wa_width;
-
- /* Buffer for text - may be supplied by user */
- static char *main_buffer = NULL;
- static int main_end; /* Index of terminating null */
- static int main_buf_len = 0;
- static BOOL locally_allocated = FALSE;
-
- #define BUF_LEN_DEFAULT 10000
-
- /* Size of the local buffer for wimpio */
- #define MAX_LOCAL_BUFFER 4096
-
- /* Main part of the display routine */
- /* Must be a null at buffer[len], i.e. length including null = len+1 */
- static void low_w_write(char *buffer, int len)
- {
- len += 1; /* Output length including null */
-
- /* Transfer to main buffer. First check it will all fit */
- if (len > main_buf_len)
- {
- /* Replace entire main buffer */
- strcpy(main_buffer, buffer + len - main_buf_len);
- main_end = main_buf_len-1;
- }
- else
- {
- if (main_end + len > main_buf_len)
- { /* Move existing text up */
- char *to = main_buffer;
- char *from = main_buffer + len + main_end - main_buf_len;
- while (*from) *to++ = *from++;
- strcpy(to, buffer);
- main_end = main_buf_len - 1;
- }
- else
- {
- /* Text will fit */
- strcpy(main_buffer+main_end, buffer);
- main_end += len - 1;
- }
- }
-
- /* Force redraw */
- { wimp_redrawstr r;
- r.w = wimpio_control.printf_win_handle;
- r.box.x0 = r.box.y0 = -1000000;
- r.box.x1 = r.box.y1 = 1000000;
- wimpt_noerr(wimp_force_redraw(&r));
- }
- }
-
- /* printf interface */
- int wprintf(const char *format, va_list arg)
- {
- char local_buffer[MAX_LOCAL_BUFFER];
- int chars_out;
-
- chars_out = vsprintf(local_buffer, format, arg);
-
- if (chars_out >= MAX_LOCAL_BUFFER)
- { /* Dangerous error, but untrappable! */
- werr(0, "Warning: local buffer overflowed. Who know what might happen now!");
- }
- else
- low_w_write(local_buffer, chars_out);
- return chars_out;
- }
-
- /* write interface */
- void w_write(char *buffer, int bufflen)
- {
- if (buffer[bufflen - 1] == 0)
- low_w_write(buffer, bufflen-1);
- else
- {
- char local_buffer[MAX_LOCAL_BUFFER];
-
- memcpy(local_buffer, buffer, bufflen);
- local_buffer[bufflen] = 0;
- low_w_write(local_buffer, bufflen);
- }
- }
-
- /* Given an address of the last character in some text (e.g. just before \n),
- this returns the start of the line (at \n, or start of buffer - 1),
- and the number of physical lines it represents, excluding newline effect.
- The number of lines is 1 for an empty line, or for 1...line_length
- characters.
- */
- static char *find_lines(char *end, int line_length, int *lines)
- {
- char *c;
- int chars = 0;
-
- /* Scan back for start, counting non-control characters */
- for (c = end ; c - main_buffer >= 0 && *c != '\n' ; c--)
- if (*c >= ' ') chars += 1;
-
- /* Lines is chars / line_length, rounded up. Special case for zero characters */
- if (chars == 0) *lines = 1;
- else *lines = (chars + line_length - 1) / line_length;
-
- return c;
- }
-
- /* Given the address of the start of a line, returns the the end of it
- as an index: either the line length or where the \n is.
- */
- static int find_line_end(char *c, int line_length)
- {
- int index;
-
- for (index = 0 ; *c != 0 && *c != '\n' && index < line_length ; c++)
- if (*c >= ' ') index += 1;
- return index;
- }
-
- /* Draw the text window */
- static void wprintf_draw(wimp_redrawstr *r, void *handle)
- {
- int xgap, ygap; /* Character spacings */
- coords_pointstr base; /* BL of clip region (widened) */
- coords_cvtstr *cvt = (coords_cvtstr *)&(r->box);
- div_t d;
- int xpos, ypos, maxx, line_length, lines;
- int yline = 0;
- int endy;
- char *c;
-
- handle = handle;
-
- /* Calculate character gaps in OS units */
- xgap = bbc_vduvar(bbc_GCharSpaceX) << bbc_vduvar(bbc_XEigFactor);
- ygap = bbc_vduvar(bbc_GCharSpaceY) << bbc_vduvar(bbc_YEigFactor);
-
- /* Find line length */
- line_length = wa_width / xgap;
-
- /* Convert clip window (screen coords) to work area coords */
- base.x = r->g.x0;
- base.y = r->g.y0;
- coords_point_toworkarea(&base, cvt);
-
- /* Widen to character boundary relative to BL of work area */
- d = div(base.x - wa_left , xgap);
- base.x -= d.rem;
- d = div(base.y - wa_base , ygap);
- base.y -= d.rem;
-
- /* Change BL of widended box to a character position in the buffer */
- xpos = base.x / xgap;
- ypos = base.y / ygap;
-
- /* Set base coordinate - x from clip region, y from work area */
- base.y = wa_base;
- coords_point_toscreen(&base, cvt);
-
- c = main_buffer + main_end;
- if (*(c-1) == '\n') c -= 1;
-
- endy = r->g.y1 + ygap; /* y coord at which to stop */
- maxx = xpos + (r->g.x1 - base.x + xgap) / xgap; /* Max index to display */
-
- /* Display lines until out of clip window */
- while (base.y < endy)
- {
- int len;
- char *end = c - 1;
-
- c = find_lines(end, line_length, &lines);
- yline += lines;
- base.y += ygap * lines;
-
- /* Display if line is not empty ... */
- if (c < end)
- {
- /* ... and if in clip region */
- if (yline >= ypos)
- {
- char *from = c + 1;
- int y1 = base.y;
-
- do
- {
- /* Get a physical line to display, unless empty */
- if (*from != '\n')
- {
- len = find_line_end(from, line_length);
-
- /* Check there is enough text on the line */
- if (len >= xpos)
- {
- int limit = (len < maxx) ? len : maxx;
- int index = xpos - 1;
-
- /* Display text */
- bbc_move(base.x, y1);
- while (++index < limit)
- if (from[index] >= ' ') bbc_vdu(from[index]);
- }
-
- y1 -= ygap;
- from += len;
- }
- } while (--lines > 0);
- }
- }
- }
- }
-
- /***************************** WINDOW FUNCTIONS *****************************/
-
- static void wprintf_event_handler(wimp_eventstr *e, void *handle)
- {
- /* Deal with close event */
- if (e->e == wimp_ECLOSE && close_string != NULL)
- wread_fake_input(close_string);
- else
- w_event(e, wprintf_draw, NULL, handle);
- }
-
- int wprintf_create(char *title, char *buffer, int bufflen)
- {
- wimp_wind *window; /* Pointer to window definition */
-
- /* Do nothing if already initialised */
- if (printf_window_exists) return TRUE;
-
- /* Allocate buffer if asked to */
- if (buffer == NULL)
- {
- if (bufflen == 0) bufflen = BUF_LEN_DEFAULT;
- if ((buffer = heap_alloc(bufflen)) == NULL)
- {
- werr(0, "Unable to allocate memory for buffer");
- return FALSE;
- }
- locally_allocated = TRUE;
- }
- main_buffer = buffer;
- main_buf_len = bufflen;
- main_buffer[0] = 0;
- main_end = 0;
-
- /* Find template for the window */
- window = template_syshandle("wprintf");
- if (window == 0)
- {
- werr(0, "Template wprintf not found");
- return FALSE;
- }
-
- /* Set title string (must be indirect and long enough) */
- strcpy(window->title.indirecttext.buffer, title);
-
- /* Note work area sizes */
- wa_left = window->ex.x0;
- wa_base = window->ex.y0;
- wa_width = window->ex.x1 - window->ex.x0;
-
- /* Create the window, dealing with errors */
- if (wimpt_complain(wimp_create_wind(window, &wimpio_control.printf_win_handle)))
- return FALSE;
-
- win_register_event_handler(wimpio_control.printf_win_handle, wprintf_event_handler, NULL);
- win_activeinc();
- printf_window_exists = TRUE;
-
- /* Let the print window have idle events, so it can update */
- win_claim_idle_events(wimpio_control.printf_win_handle);
- event_setmask(event_getmask() & ~wimp_EMNULL);
- return TRUE;
- }
-
- /* Show the window */
- void wprintf_show(void)
- {
- w_show(printf_window_exists, wimpio_control.printf_win_handle);
- }
-
- /* Hide the window */
- void wprintf_hide(void)
- {
- w_hide(printf_window_exists, wimpio_control.printf_win_handle);
- }
-
- /* Delete the window */
- void wprintf_delete(void)
- {
- if (printf_window_exists && locally_allocated)
- heap_free(main_buffer);
- win_claim_idle_events((wimp_w)-1);
- event_setmask(event_getmask() | wimp_EMNULL);
- w_delete(&printf_window_exists, &wimpio_control.printf_win_handle);
- }
-
- /*---------------------------------------- READ ----------------------------------------*/
-
- /* Number of icons in the read window */
- static int n_read_icons;
-
- /* Flag - does window exist */
- static BOOL read_window_exists = FALSE;
-
- static BOOL read_printf_flag;
-
- #define icon_OK (0)
- #define icon_Input (1)
-
- /* Type ahead buffer - characters are moved here on return */
- #define MaxReadBuffer (1000)
- static char read_buffer[MaxReadBuffer+1];
- static int read_index = 0; /* Where to put next character */
-
- /* Drop handler globals */
- static char read_drop_format[WRead_MaxFormat];
- static w_drop_handler read_drop_handler = NULL;
- static void *read_drop_handle = NULL;
- static w_drop_handler icon_drop_handler = NULL;
- static void *icon_drop_handle = NULL;
-
- /* Place the caret in the read window. */
- static void place_caret()
- {
- wimp_caretstr caretstr;
-
- /* Set the caret into the icon */
- caretstr.w = wimpio_control.read_win_handle;
- caretstr.i = icon_Input;
- caretstr.height = -1;
- caretstr.index = 0;
- wimpt_noerr(wimp_set_caret_pos(&caretstr));
- }
-
- /* Transfer as much data as we can to the local buffer, and add a newline.
- Data from this buffer gets picked up in wread */
- static void buffer_input(char *source)
- {
- int i;
-
- for (i = 0 ; source[i] >= ' ' && read_index < MaxReadBuffer-1 ; i++)
- read_buffer[read_index++] = source[i];
- read_buffer[read_index++] = '\n';
- read_buffer[read_index] = 0;
- }
-
- /* Are menus enabled? */
- static BOOL menu_enable;
-
- /* Menu maker and processor */
- static event_menu_maker read_menu_maker = NULL;
- static event_menu_proc read_menu_proc = NULL;
- static void *read_menu_h = NULL;
-
- /* Local menu routines */
- static menu menu_maker(void *handle)
- {
- return (menu_enable) ? (*read_menu_maker)(read_menu_h) : (menu)-1;
- }
-
- static void menu_proc(void *handle, char *hit)
- {
- if (menu_enable) (*read_menu_proc)(read_menu_h, hit);
- }
-
- /* Enable/disable menus */
- static void enable_menus(BOOL enable)
- {
- menu_enable = enable;
-
- event_attachmenumaker(wimpio_control.printf_win_handle,
- (enable) ? menu_maker : 0,
- (enable) ? menu_proc : 0, read_menu_h);
- event_attachmenumaker(wimpio_control.read_win_handle,
- (enable) ? menu_maker : 0,
- (enable) ? menu_proc : 0, read_menu_h);
- event_attachmenumaker(win_ICONBAR,
- (enable) ? menu_maker : 0,
- (enable) ? menu_proc : 0, read_menu_h);
- }
-
-
- /* The read routine itself. Bytes read if successful, -1 if not. */
- int wread(char *buffer, int bufflen)
- {
- int i, j, to_move;
-
- if (read_index <= 0) enable_menus(TRUE);
- while (read_index <= 0) event_process();
-
- to_move = (bufflen < read_index) ? bufflen : read_index;
- memcpy(buffer, read_buffer, to_move);
-
- for (i = 0, j = bufflen ; j <= read_index ; i++, j++)
- read_buffer[i] = read_buffer[j];
- read_index = i;
-
- /* Echo what we read into the print buffer */
- if (read_printf_flag && to_move > 0)
- {
- w_write(buffer, to_move);
- if (event_anywindows()) event_process();
- }
-
- enable_menus(FALSE);
- return to_move;
- }
-
- /* Return processor - move characters to buffer and clear icon */
- static void process_return()
- {
- char *icontext;
- wimp_icon result;
- int i;
-
- wimpt_noerr(wimp_get_icon_info(wimpio_control.read_win_handle, icon_Input, &result));
- icontext = result.data.indirecttext.buffer;
- buffer_input(icontext);
-
- for (i = 0 ; i < result.data.indirecttext.bufflen ; i++) icontext[i] = 0;
- wimpt_noerr(wimp_set_icon_state(wimpio_control.read_win_handle, icon_Input, 0, 0));
- place_caret();
- }
-
- /* Register a file drop handler for the read window.
- If you drop a file onto the read window, and you have given a non-null
- string to this routine, and the icons are not grayed, then it treats
- the string as a format string, and simulates the effect of typing that
- in to the window. For example, to get "(<filname>) run", you would give
- a string of "(%s) run". The format must be less than 80 characters -
- it is ignored if not. Passing NULL removes the drop handling.
- The second parameter allows a function to be called before this string is
- issued, for example to display menus. At the moment it passes no parameters on:
- future versions might pass the string in, or allow the process to be halted.
- */
- void w_register_drop(char *format, w_drop_handler handler, void *handle)
- {
- if (format == NULL)
- read_drop_format[0] = 0;
- else if (strlen(format) < WRead_MaxFormat)
- strcpy(read_drop_format, format);
- read_drop_handler = handler;
- read_drop_handle = handle;
- }
-
- /* Fake input */
- void wread_fake_input(char *text)
- {
- buffer_input(text);
- }
-
- /***************************** WINDOW FUNCTIONS *****************************/
-
- static void wread_event_handler(wimp_eventstr *e, void *handle)
- {
- switch (e->e)
- {
- case wimp_ECLOSE:
- if (close_string != NULL)
- {
- wread_fake_input(close_string);
- return;
- }
- else
- break;
-
- case wimp_EBUT:
- if (e->data.but.m.i >= icon_Input)
- {
- wimp_icon result;
- char *icontext;
-
- wimpt_noerr(wimp_get_icon_info(wimpio_control.read_win_handle,
- e->data.but.m.i, &result));
- icontext = result.data.indirecttext.buffer;
- buffer_input(icontext);
- return;
- }
- else break;
-
- case wimp_EKEY:
- if (e->data.key.chcode == 13)
- process_return();
- /* Otherwise pass the key on */
- else wimp_processkey(e->data.key.chcode);
- return;
-
- case wimp_ESEND:
- case wimp_ESENDWANTACK:
- if (e->data.msg.hdr.action == wimp_MDATALOAD)
- { /* File load - only respond to DATALOAD because it is the only one
- which guarantees a persistent file name */
- char *filename;
-
- if (xferrecv_checkinsert(&filename) != -1)
- {
- if (read_drop_handler) (*read_drop_handler)(read_drop_handle);
-
- if (read_drop_format[0] != 0)
- {
- /* Fill the icon buffer, so it gets picked up on the next read */
- /* No buffer overflow check */
- char temp_buffer[1000];
-
- sprintf(temp_buffer, read_drop_format, filename);
- wread_fake_input(temp_buffer);
- }
- }
- return;
- }
- }
-
- /* Default and fall through case */
- w_event(e, NULL, NULL, handle);
- }
-
- BOOL wread_create(char *title, BOOL printf_flag)
- {
- /* Do nothing if already initialised */
- if (read_window_exists) return TRUE;
-
- if ((wimpio_control.read_win_handle = w_create(title, "wread", &n_read_icons)) == -1)
- return FALSE;
-
- win_register_event_handler(wimpio_control.read_win_handle, wread_event_handler, NULL);
-
- read_window_exists = TRUE;
- read_printf_flag = printf_flag;
- enable_menus(FALSE);
- return TRUE;
- }
-
- /* Show the window */
- void wread_show(void)
- {
- w_show(read_window_exists, wimpio_control.read_win_handle);
- }
-
- /* Hide the window */
- void wread_hide(void)
- {
- w_hide(read_window_exists, wimpio_control.read_win_handle);
- }
-
- /* Delete the window */
- void wread_delete(void)
- {
- w_delete(&read_window_exists, &wimpio_control.read_win_handle);
- }
-
- char *w_close_string(char *text)
- {
- char *old = close_string;
- close_string = text;
- return old;
- }
-
- /***************************** ICON BAR *************************************/
-
- static void clickproc(wimp_i i)
- {
- /* Open the print and scan windows */
- wprintf_show();
- wread_show();
- place_caret();
- }
-
- /* Call on file drop onto icon bar */
- static void icon_drop(wimp_eventstr *e, void *handle)
- {
- char *command = (char *)handle;
-
- switch (e->e)
- {
- case wimp_ESEND:
- case wimp_ESENDWANTACK:
- {
- if (e->data.msg.hdr.action == wimp_MDATALOAD)
- { /* File load - only respond to DATALOAD because it is the only one
- which guarantees a persistent file name */
- char *filename;
-
- if (xferrecv_checkinsert(&filename) != -1)
- {
- if (icon_drop_handler) (*icon_drop_handler)(icon_drop_handle);
-
- if (command)
- {
- /* Fill the icon buffer, so it gets picked up on the next read */
- /* No buffer overflow check */
- char temp_buffer[1000];
-
- sprintf(temp_buffer, command, filename);
- wread_fake_input(temp_buffer);
- }
- }
- return;
- }
- }
- }
- }
-
- void w_baricon(char *command, w_drop_handler handler, void *handle)
- {
- /* Register icon on icon bar */
- baricon(wimpt_programname(), 1, clickproc);
- win_register_event_handler(win_ICONBARLOAD, icon_drop, command);
- icon_drop_handler = handler;
- icon_drop_handle = handle;
- }
-
- void w_register_menu(event_menu_maker maker, event_menu_proc proc, void *handle)
- {
- read_menu_maker = maker;
- read_menu_proc = proc;
- read_menu_h = handle;
- }
-