/**
 * Wimp_Poll / Wimp code
 * (C) Nettle developers 2000-2003
 *
 * $Id: wimp,v 1.197 2003/12/13 17:42:56 archifishal Exp $
 */

#include "generic.h"
#include "globals.h"

#include <ctype.h>
#include <time.h>
#include "sys/errno.h"

#include "socklib.h"
#include "unixlib.h"

#include "sys/time.h"
#include "netdb.h"

#include "choices.h"
#include "hotlist.h"
#include "keyboard.h"
#include "lineedit.h"
#include "messages.h"
#include "main.h"
#include "misc.h"
#include "mouse.h"
#include "nettle.h"
#include "process.h"
#include "scripts.h"
#include "seln.h"
#include "socket.h"
#include "sockwatch.h"
#include "spool.h"
#include "templ.h"
#include "termlist.h"
#include "url.h"
#include "wimp.h"
#include "wimputil.h"
#include "zapredraw.h"


static int menu_data[96];
static int menu_data2[96];
static int menu_data3[96];
static char indirected[1024];

static struct session_struct *paste_session;

static int old_charw;
static int old_charh;
static struct coords old_eig;

static int msg_ref;
static bool queried_user_filetype=false;
static int shutdown_flag=-1; /* 0 if closedown, not if single quit */

static int taskmanager_taskhandle;

/* Dunno what this is about - AMS 12/09/2001 */
enum ansi_colour_flag
{
  SAVE_NO_ANSI,
  SAVE_FLAG_ANSI,
  SAVE_FORCE_ANSI
};

static enum ansi_colour_flag allow_ansi_colour = SAVE_FLAG_ANSI;

static void create_iconbar_menu(void);
static void open_open_window(bool open_window_flag);
static void open_options_window(bool open_window_flag);
static void create_terminal_menu(void);

static void paste_in_data (int window_handle, char *data, int size);
static bool parse_ant_url (union wimp_poll_block *);
static bool parse_acorn_uri (union wimp_poll_block *);
static bool paste_text_file (union wimp_poll_block *);

static void wimp_pollsocket(struct session_struct *session);

void open_window_centred (int window_handle)
{
  struct wimp_getwindowstate_block block;

  /* get the window state */
  block.window_handle = window_handle;
  _swi (Wimp_GetWindowState, _IN(1), &block);

  /* calculate position */
  block.max.x -= block.min.x;
  block.min.x = screensize.x / 2 - block.max.x / 2;
  block.max.x += block.min.x;
  block.max.y -= block.min.y;
  block.min.y = screensize.y / 2 - block.max.y / 2;
  block.max.y += block.min.y;
  block.handle_behind = -1;	/* open on top of stack */

  open_window((struct wimp_openwindow_block *) &block, window_handle);
}



/**
 * start a connection to the remote host
 *
 * This is called once the dns lookup has succeeded
 */
static void wimp_connectsession(struct session_struct *session)
{
   session->socket_handle =
                  socket_connecttoip(session->socket_ip,
                                     session->socket_port);

  if (session->socket_handle == -1)
  {
    write_out_strings(session, lookup_static("nosocket"),
                       "\r\n", 0);
    session->socket_state=NETTLE_SESSION_NONE;
    return;
  }

  session->socket_state = NETTLE_SESSION_CONNECT;
  write_out_strings(session,
                    lookup_static_var("connecting", 1,
                                      socket_ip_string(session->socket_ip)),
                    "\r\n",
                    0);
  /* take a null so the state machine gets kicked and wimp_pollconect
   * starts taking null polls */
  main_requirenull = true;
}


static void wimp_pollresolve(struct session_struct *session)
{
  struct hostent *dnsanswer;

  switch (dns_check(session->dns))
  {
    default:
      /* still in progress */
      main_requirenull = true;
      break;

    case dns_complete_failure:
      set_title_bar(session->window_handle,
                    lookup_static("title_disconnect"));
      write_out_strings(session, lookup_static("noresolv"), "\r\n", 0);
      session->socket_state=NETTLE_SESSION_NONE;
      break;

    case dns_complete_success:
      dnsanswer = dns_getanswer(session->dns);
      assert(dnsanswer != NULL);
      session->socket_ip = (unsigned int) *((int *)(void *)(*dnsanswer).h_addr);
      dns_dispose(session->dns);
      session->dns = NULL;

      wimp_connectsession(session);
      break;
  }
}



static void wimp_pollconnect(struct session_struct *session)
{
  int retval;

  retval = socket_connected(session->socket_handle);

  switch (retval)
  {
    case 0:
      {
        char string[1024];

        sprintf(string,APPLICATION_NAME"/%s (%s%s%s,%d)",
                terminal_name[session->terminal_type],
                session->login_user ? session->login_user : "",
                session->login_user ? "@" : "",
                session->socket_host,
                session->socket_port);

        set_title_bar(session->window_handle,string);

        session->socket_state = NETTLE_SESSION_CONNECTED;
        write_out_strings(session,
                          lookup_static_var("connected", 1,
                                       socket_ip_string(session->socket_ip)),
                          "\r\n",
                          0);

        main_requirenull = true; /* read any data waiting on the socket */
        break;
      }

    case -1:
      /* still connecting, take null polls */
      main_requirenull = true;
      break;

    default:
    {
      /* error, retval contains errno */
      set_title_bar(session->window_handle,
                     lookup_static("title_noconn"));
      write_out_strings(session,
                        lookup_static_var("noconn", 1,
                                          socket_strerror(retval)),
                        "\r\n", 0);

      socket_close(session->socket_handle);

      session->socket_handle=-1;
      session->socket_state=NETTLE_SESSION_NONE;
      break;
    }
  }
}



static void wimp_pollsocket(struct session_struct *session)
{
  int packet_length=-1;
  char receive_block[RECEIVE_BLOCK_LENGTH];

  switch (session->connection_type)
  {
    case NETTLE_TELNET:
      packet_length = recv(session->socket_handle, receive_block,
                           RECEIVE_BLOCK_LENGTH, 0);
      break;

    case NETTLE_SSH:
      break;
  }

  if (packet_length == -1 && errno == EWOULDBLOCK)
    /* we've read all the available data */
    return;

  if (packet_length == 0 || packet_length == -1)
  {
    /* EOF or error condition */
    set_title_bar(session->window_handle, lookup_static("title_disconnect"));
    reset_terminal(session);
    if (packet_length == 0)
    {
      /* EOF received */
      write_out_strings(session, "\r\n", lookup_static("closed"), "\r\n", 0);
    }
    else
    {
      write_out_strings(session, "\r\n",
                        lookup_static_var("socketerror", 1,
                                          socket_strerror(errno)),
                        "\r\n", 0);
    }

    write_out_strings(session, lookup_static("disconn"), "\r\n", 0);
    socket_close(session->socket_handle);
    session->socket_handle=-1;
    session->socket_state=NETTLE_SESSION_NONE;

    return;
  }

  write_out_data(session, receive_block, packet_length);

  /* take a null poll as there may be either more data waiting to be read
   * (and sockwatch won't notify us again about data that's already been
   * received), or there may be an EOF condition waiting for us to read
   * it. We don't try reading again straight away, as we might end up taking
   * lots of processor time if data is arriving faster than we can process
   * it.
   */
  main_requirenull = true;
}



static void wimp_pollsessions(void)
{
  struct session_struct  *session;
  int                     fdupperbound; /* (exclusive) */
  fd_set                  readfds;
  struct timeval          timeval;
  int                     numberready;

  FD_ZERO(&readfds);
  fdupperbound = 0;

  /* for each session, either poll it if necessary, or add its socket to
   * the list of ones to check with select() */
  for (session = sessions; session != NULL; session = session->next)
  {
    switch (session->socket_state)
    {
      case NETTLE_SESSION_RESOLVE:
        wimp_pollresolve(session);
        break;

      case NETTLE_SESSION_CONNECT:
        wimp_pollconnect(session);
        break;

      case NETTLE_SESSION_CONNECTED:
        if (session->connection_type != NETTLE_TASKWINDOW)
        {
          assert(session->socket_handle >= 0 && session->socket_handle < 256);
          FD_SET(session->socket_handle, &readfds);
          if (session->socket_handle >= fdupperbound)
            fdupperbound = session->socket_handle + 1;
        }

        /* Anything to be pasted in...? */
        if (session->paste)
        {
          int sent = nettle_senddata (session, session->paste->data,
                                      session->paste->length);

          if (sent == -1)
          {
            generror("nettle_senddata returned -1, this is probably bad... "
                     "report it to the authors.",false);
          }
          else if (sent == session->paste->length)
          {
            struct paste_buffer *paste = session->paste;
            session->paste = paste->next;
            if (! paste->next)
              session->paste_head = NULL;
            free (paste);
          } else {
            memmove (session->paste->data, session->paste->data+sent,
                     session->paste->length - sent);
            session->paste->length -= sent;
            main_requirenull = true;
          }
        }

        /* Try and close the spool (if open for 5 secs without output currently) */
        if (session->spool_file_name!=NULL)
          spool_close(session, false);

        break;

      case NETTLE_SESSION_NONE:
        /* do nothing */
        break;

      default:
        assert(0);
    }
  }

  if (fdupperbound == 0)
    /* no sockets to be polled */
    return;

  timeval.tv_sec  = 0;
  timeval.tv_usec = 0;

  numberready = select(fdupperbound, &readfds, NULL, NULL, &timeval);
  if (numberready == 0)
    /* no sockets need attention */
    return;

  if (numberready == -1 && errno != EBADF)
    /* error other than an invalid descriptor, nothing we can do */
    return;

  for (session = sessions; session != NULL; session = session->next)
  {
    if (session->connection_type == NETTLE_TASKWINDOW)
      continue;

    if (session->socket_state != NETTLE_SESSION_CONNECTED)
      continue;

    if (FD_ISSET(session->socket_handle, &readfds))
      wimp_pollsocket(session);
  }
}


void wimp_nullreasoncode(void)
{
  if (socketwatch_pollword != NULL)
    *socketwatch_pollword = 0;

  wimp_pollsessions();

  close_log(false);

  if (resize_handle)
  {
    int buttons;
    _swi(OS_Mouse,_OUT(2),&buttons);
    if( !buttons )
    {
      resize_handle = 0;
      _swi(Wimp_CloseWindow,_IN(1),&win_sizebar);
    }
  }

  if (selection_in_progress)
    handle_selection_drag();

  if (mouse_handling_session)
  {
    struct wimp_getpointerinfo_block block;

    _swi(Wimp_GetPointerInfo, _IN(1), &block);

    if (block.buttons==0 || block.window_handle!=mouse_handling_session->window_handle)
    {
      mouse_handle_event(mouse_handling_session,
                         block.pos.x,
                         block.pos.y,
                         0);
    }

    /* Need to poll regularly until event finished */
    main_requirenull = true;
  }

  if (cursor_session || (blinking && want_blink) )
  {
    int now = _swi (OS_ReadMonotonicTime, 0);
    if (now - nextcursortime >= 0)
    {
      nextcursortime = now + CURSOR_BLINK_DELAY;
      cursor_state = cursor_state ? false : true;

      if (cursor_session)
      {
        force_redraw (cursor_session->window_handle,
                      cursor_session->pos.x * redraw.r_charw << eig.x,
                      (-cursor_session->pos.y - 1) * redraw.r_charh << eig.y,
                      (cursor_session->pos.x + 1) * redraw.r_charw << eig.x,
                       -cursor_session->pos.y * redraw.r_charh << eig.y);
      }

      if (blinking && want_blink)
      {
        struct session_struct *session = sessions;

        for(;session;session=session->next)
        {
          if (session->want_blink)
          {
            force_redraw (session->window_handle,
                        0,
                        (-session->terminal_size.y-session->scrollback) * redraw.r_charh <<eig.y,
                        session->terminal_size.x * redraw.r_charw << eig.x,
                        0);
          }
        }
      }
    }
  }
}



/* Redraw window */

void redraw_window(union wimp_poll_block *wimp_block)
{
  struct session_struct *session = sessions;
  int window_handle = wimp_block->redraw_window.window_handle;

  while (session && window_handle != session->window_handle)
  {
    session = session->next;
  }

  if (session)
  {
    int first_line;
    int last_line;

    struct wimp_getwindowstate_block block;
    block.window_handle = wimp_block->redraw_window.window_handle;
    _swi (Wimp_GetWindowState, _IN(1), &block);

    first_line=-(block.scroll.y >> eig.y) / redraw.r_charh;
    last_line=(((block.max.y - block.min.y- block.scroll.y) >> eig.y) + redraw.r_charh - 1)
	      / redraw.r_charh;

    if (first_line<0)
      first_line=0;

    if (last_line>session->terminal_size.y+session->scrollback)
      last_line=session->terminal_size.y+session->scrollback;

    zapgen_code(zapredraw_area,
                session->assigned_area,
                session->terminal_size.x,
		first_line,
		last_line,
                session->other_session_flags,
                session==selection_session ? selection_start / session->terminal_size.x : -1,
                session==selection_session ? selection_start % session->terminal_size.x : -1,
                session==selection_session ? selection_end   / session->terminal_size.x : -1,
                session==selection_session ? selection_end   % session->terminal_size.x : -1
                );

    redraw.r_data = zapredraw_area;

    _swi(ZapRedraw_RedrawWindow, _INR(0,1), wimp_block, &redraw);
  }
  else if (colourpicker_handle == 0 || window_handle != menu_window)
  {
    /* *DON'T* redraw the ColourPicker window */
    int more;

    more = _swi(Wimp_RedrawWindow, _IN(1)|_RETURN(0), wimp_block);
    while (more)
    {
      if (window_handle == win_hotpane)
        hotlist_draw((struct wimp_getrectangle_block *) wimp_block);

      more = _swi(Wimp_GetRectangle, _IN(1)|_RETURN(0), wimp_block);
    }
  }
}



/* Open window */

void open_window(struct wimp_openwindow_block *wimp_block, int window_handle)
{
  struct wimp_getwindowstate_block local_block, *block=&local_block;
  struct session_struct *session = sessions;

  /* find out which session this window is - if any! */

  while (session && window_handle != session->window_handle)
  {
    session = session->next;
  }

  if (wimp_block)
  {
    /* Called from Wimp_Poll, so use that block, which contains an OpenWindow struct */
    block = (struct wimp_getwindowstate_block *) wimp_block;
  }
  else
  {
    /* Called from our own code, so fetch the OpenWindow block */
    block->window_handle = window_handle;

    _swi(Wimp_GetWindowState, _IN(1), block);

    block->handle_behind = -1;  /* Open on top of stack */
  }

  if (window_handle==win_choices)
  {
    /* Find out which pane we should be opening */
    struct wimp_whichicon_block whichicon_block;
    int window_id_to_open=0;
    int counter=0;

    _swi(Wimp_WhichIcon, _INR(0,3), window_handle, &whichicon_block, WIMP_ICON_SELECTED_BIT,
                                                                     WIMP_ICON_SELECTED_BIT);

    while (whichicon_block.icons[counter]!=-1)
    {
      switch (whichicon_block.icons[counter])
      {
        case icon_choices_size:
          if (window_id_to_open==0)
            window_id_to_open=win_paneterm;
          else
            generror("More than one 'window' icon set in choices.", false);
          break;

        case icon_choices_colour:
          if (window_id_to_open==0)
            window_id_to_open=win_panecols;
          else
            generror("More than one 'window' icon set in choices.", false);
          break;

        case icon_choices_hotlist:
          if (window_id_to_open==0)
            window_id_to_open=win_panehot;
          else
            generror("More than one 'window' icon set in choices.", false);
          break;

        case icon_choices_misc:
          if (window_id_to_open==0)
            window_id_to_open=win_panemisc;
          else
            generror("More than one 'window' icon set in choices.", false);
          break;
      }
      counter++;
    }

    if (window_id_to_open!=0)
    {
      struct wimp_getwindowinfo_block window;
      window.window_handle=window_id_to_open;

      _swi(Wimp_GetWindowInfo, _IN(1), 1 + (int) &window); /* no icons */

      window.max.x += block->min.x - (window.min.x-4);
      window.min.x = block->min.x+4;
      window.min.y = block->min.y+4;
      window.max.y = block->max.y-4;
      window.handle_behind=block->handle_behind;

      if (window_id_to_open == win_panehot)
      {
        /* Uhoh. Need to open the hotlist pane's list pane! */

        struct wimp_getwindowinfo_block window2;
        window2.window_handle=win_hotpane;

        _swi(Wimp_GetWindowInfo, _IN(1), 1 + (int) &window2); /* no icons */

        window2.max.x += window.max.x - (40+12);  /* Guessing the scrollbar width... */
        window2.min.x = window.min.x+12;
        window2.min.y = (window.max.y-12) - (window2.max.y - window2.min.y);
        window2.max.y = window.max.y-12;
        window2.handle_behind=block->handle_behind;

        _swi(Wimp_OpenWindow, _IN(1), &window2);

        window.handle_behind=win_hotpane;

      }

      _swi(Wimp_OpenWindow, _IN(1), &window);

      block->handle_behind=window_id_to_open;
    }
    else
    {
      generror("No 'window' selected in choices.", false);
    }
  }
  else if (session)
  {
    struct wimp_getwindowinfo_block window;
    int height = block->max.y - block->min.y;
    int maxheight = (session->terminal_size.y * redraw.r_charh) << eig.y;

    if (session->window_needs_resized)
    {
      /* If we need to be resized, do it here - opening also happens here
       * -NB this function is also used by the font change code */
      reopen_and_size_window(session, session->terminal_size.x, session->terminal_size.y,
                             session->scrollback, old_charw, old_charh, old_eig);

      /* Refetch the window state so that it's up to date, and as the Wimp may have changed it if it
       * didn't fit on the screen etc.
       */
      _swi(Wimp_GetWindowState, _IN(1), block);

      session->window_needs_resized=false;

      /* We might need to show the resizer here?  Unsure. */
      /* resize_handle = block->window_handle; */
    }

    /* This is a main "session" window, so handle the pane, if any */
    window.window_handle = block->window_handle;
    _swi (Wimp_GetWindowInfo, _IN(1), 1 + (int) &window); /* no icons */

    if (dont_require_ctrl || inkey(KEYBOARD_CTRL) )
    {
      /* Ctrl pressed (or not) - allow dynamic resizing */
      int new_x=(((block->max.x-block->min.x)>>eig.x) + redraw.r_charw/2)/redraw.r_charw;
      int new_y=(((block->max.y-block->min.y)>>eig.y) + redraw.r_charh/2)/redraw.r_charh;

      if ( (session->terminal_size.x != new_x) || (session->terminal_size.y != new_y) )
      {
        resize_terminal(session, new_x, new_y, session->scrollback, true);

        /* Refetch the window state as the Wimp may have changed it if it
         * didn't fit on the screen etc.
         */
        _swi(Wimp_GetWindowState, _IN(1), block);

        resize_handle = block->window_handle;
      }

      block->max.x = block->min.x+(session->terminal_size.x)*(redraw.r_charw << eig.x);
      block->min.y = block->max.y-(session->terminal_size.y)*(redraw.r_charh << eig.y);

      /*  Why did we do this?  */
      /* block->scroll.y =-(session->terminal_size.y+session->scrollback)*(redraw.r_charh << eig.y); */
    }
    else
    {
      /* limit to the number of rows */

      /* if the window size is unchanged, DON'T apply this limit
       * (allow for moving the window)
       */
      if (height > maxheight &&
	  (window.max.x - window.min.x != block->max.x - block->min.x ||
	   window.max.y - window.min.y != block->max.y - block->min.y))
      {
        block->min.y = block->max.y - maxheight;
        /* if the v scrollbar was at the bottom and its vpos is unchanged
         * force it to the bottom
         */
        if (window.scroll.y == block->scroll.y &&
            window.scroll.y - window.max.y + window.min.y == window.work_min.y)
        {
          block->scroll.y = window.work_min.y;
        }
      }
    }

    if (session->line_editor_type!=LINEEDIT_NONE)
    {
      /* open the pane window and set the 'behind' word to behind the pane */
      open_pane_window(session, (struct wimp_openwindow_block *) block);
      block->handle_behind = session->pane_handle;
    }

    _swi(Wimp_OpenWindow, _IN(1), block);

    if ( block->window_handle == resize_handle )
    {
       struct wimp_getwindowstate_block bar;
       char size[16];
       int barwidth;

       sprintf(size, "%dx%d", session->terminal_size.x, session->terminal_size.y);
       set_icon_data(win_sizebar, icon_sizebar_size, size);

       bar.window_handle = win_sizebar;
       _swi (Wimp_GetWindowState, _IN(1), &bar);
       barwidth = bar.max.x - bar.min.x ;

       bar.min.x = block->max.x - barwidth;
       bar.max.x = block->max.x;
       bar.min.y = block->min.y - toolheight;
       bar.max.y = block->min.y - ( 2<<eig.y ) ;

       _swix(Wimp_ResizeIcon, _INR(0,5), win_sizebar, icon_sizebar_size, 0, -(toolheight - (2<<eig.y)), barwidth, 0);
       _swi(Wimp_OpenWindow, _INR(1,4), &bar, 0x4b534154 /* "TASK" */, resize_handle, 1<<24 );
    }

    return;
  }

  _swi(Wimp_OpenWindow, _IN(1), block);
}



/* Close window */

void close_window(int window_handle)
{
  struct wimp_closewindow_block block;
  struct session_struct *session = sessions;

  while (session && window_handle != session->window_handle)
  {
    session = session->next;
  }

  if (session)
  {
    if (session->socket_state == NETTLE_SESSION_CONNECTED)
    {
      bool status = generror_question("ConnectionOpenButtons","ConnectionOpen",0);

      if (!status)
        return;
    }
    remove_session(session);
  }
  else
  {
    block.window_handle = window_handle;

    _swi(Wimp_CloseWindow, _IN(1), &block);

    if (window_handle == win_panehot)
    {
      block.window_handle = win_hotpane;
      _swi(Wimp_CloseWindow, _IN(1), &block);
    }
  }
}



/* Determine if there are any sessions open.  If so, query the user as to whether */
/* to really quit the program. set_quit_flag is true if the user is quitting the task, */
/* or false if the Wimp is quitting us */

static void close_program(void)
{
  struct session_struct *session = sessions;
  bool connections_open=false;

  while (session)
  {
    if (session->socket_state == NETTLE_SESSION_CONNECTED)
      connections_open=true;

    session = session->next;
  }

  quit_flag=true;

  if (connections_open)
    quit_flag = generror_question("ConnectionMultiOpenButtons","ConnectionMultiOpen",0);

  if (quit_flag && shutdown_flag==0)
  {
    union wimp_poll_block message;

    /* start of key press message is same as block returned by get caret position */
    _swi(Wimp_GetCaretPosition, _IN(1), &message);

    message.key_pressed.code = 0x1fc; /* scF12 */
    message.key_pressed.extra = 0;
    _swi(Wimp_SendMessage, _INR(0,3), 8, &message, taskmanager_taskhandle, 0);
  }
}



static void iconbar_click(int x, int y, int buttons, int icon_handle)
{
  if (icon_handle==iconbar_handle)
  {
     switch (buttons)
     {
       case 4:
  	 open_open_window(true);
  	 break;
       case 2:
  	 create_iconbar_menu();
  	 break;
       case 1:
         {
           struct wimp_getpointerinfo_block block;
           int items = hotlist_num_items();
           if (items)
           {
             _swi(Wimp_GetPointerInfo, _IN(1), &block);
             _swi(Wimp_CreateMenu, _INR(1,3), create_hotlist_menu(), block.pos.x-64, 96+(items*44));
             menu_open = MENU_HOTLIST;
           }
         }
         break;
     }
  }
}

static void win_info_click(int x, int y, int buttons, int icon_handle)
{
  switch (icon_handle)
  {
    case icon_info_website:
      url_launch(NETTLE_HOME_PAGE);

      if ((buttons & 1)==0)
        _swi(Wimp_CreateMenu, _IN(1), -1);

      break;
  }
}

static void win_open_click(int x, int y, int buttons, int icon_handle)
{
  switch (icon_handle)
  {
    case icon_open_cancel:
      close_window(win_open);
      break;

    case icon_open_connect:
      if (start_connection() && (buttons & 1)==0)
        close_window(win_open);
      break;

    case icon_open_contypebut:
      create_connection_type_menu(win_open, icon_open_contype, icon_open_contypebut);
      break;

    case icon_open_termtypebut:
      create_terminal_type_menu(win_open, icon_open_termtype, icon_open_termtypebut);
      break;
  }
}

static void win_save_click(int x, int y, int buttons, int icon_handle)
{
  switch (icon_handle)
  {
    case icon_save_cancel:
      _swi(Wimp_CreateMenu, _IN(1), -1);
      break;

    case icon_save_save:
      {
	char save_data[1024];

	read_icon_data(win_save, icon_save_filename, save_data, sizeof(save_data));

	if ((instr(save_data, ":")>=0))
	{
	  /* if icon ticked, then we save with ANSI colour */
	  if (read_icon_ticked(win_save, icon_save_ansi))
	  {
	    save_selection(save_data, true);
	  }
	  else
	  {
	    save_selection(save_data, false);
	  }

	  if (buttons!=1)
	  {
	    _swi(Wimp_CreateMenu, _IN(1), -1);
	  }
	}
	else
	{
	  generror("ToSaveDrag", true);
	}
      }
      break;

    case icon_save_drag:
      switch (buttons)
      {
	case 64: case 16:
	  /* Drag */
	  drag_start(win_save, icon_save_drag);
	  break;
      }
      break;
  }
}


static void win_spoolsave_click(int x, int y, int buttons, int icon_handle)
{
  switch (icon_handle)
  {
    case icon_spoolsave_cancel:
      _swi(Wimp_CreateMenu, _IN(1), -1);
      break;

    case icon_spoolsave_save:
      if ((buttons & 1)==0 && spool_save_event())
      {
        _swi(Wimp_CreateMenu, _IN(1), -1);
      }
      break;

    case icon_spoolsave_drag:
      switch (buttons)
      {
	case 64: case 16:
	  /* Drag */
	  drag_start(win_spoolsave, icon_spoolsave_drag);
	  break;
      }
      break;
  }
}

/*
 * Description: Perform the 'set' action on the resize dialogue,
 *              with NO closing of the dialogue
 * Parameters:  none
 * Returns:     none
 * Note:        Assumes that terminal_menu_session is set to the
 *              correct terminal window, as it should be whilst
 *              the menu is active
 */

static void win_resize_set(void)
{
  struct coords new_size;
  int new_scrollback;
  if (read_termsize_icons (win_resize,
                           icon_resize_width, icon_resize_height,
                           icon_resize_scroll,
                           &new_size, &new_scrollback))
  {
    resize_terminal(terminal_menu_session, new_size.x, new_size.y, new_scrollback, true);
  }
}


static void win_resize_click(int x, int y, int buttons, int icon_handle)
{
  switch (icon_handle)
  {
    case icon_resize_cancel:
      if (buttons & 1)
      {
        set_termsize_icons(win_resize, icon_resize_width, icon_resize_height, icon_resize_scroll,\
                terminal_menu_session->terminal_size, terminal_menu_session->scrollback);
      }
      else
      {
        _swi(Wimp_CreateMenu, _IN(1), -1);
      }

      break;

    case icon_resize_set:
      win_resize_set();
      if ((buttons & 1)==0)
        _swi(Wimp_CreateMenu, _IN(1), -1);
      break;
  }
}

static void win_termterm_click(int x, int y, int buttons, int icon_handle)
{
  switch (icon_handle)
  {
    case icon_termterm_cancel:
      if ((buttons & 1)==0)
      {
        close_window(win_termterm);
      }
      else
      {
        open_options_window(false);
      }

      break;

    case icon_termterm_set:
      options_session->line_editor_type=read_lineeditor_type(win_termterm, icon_termterm_lineedittype);
      lineedit_free_space(options_session, true);
      lineedit_allocate_space(options_session, true);

      reopen_and_size_window(options_session, options_session->terminal_size.x, options_session->terminal_size.y,
                             options_session->scrollback, redraw.r_charw, redraw.r_charh, eig);

      if (read_icon_ticked(win_termterm, icon_termterm_localecho))
        options_session->local_echo=true;
      else
        options_session->local_echo=false;

      if ((buttons & 1)==0)
      {
        close_window(win_termterm);
      }

      break;

    case icon_termterm_lineeditbut:
      create_lineeditor_type_menu(win_termterm, icon_termterm_lineedittype, icon_termterm_lineeditbut);
      break;
  }
}

/**
 * Set the caret to be in the given session
 *
 * (Either the window or line editor, if lineeditor is on)
 *
 * @param session   Session to place caret into
 */
static void wimp_claimcaret(struct session_struct *session)
{
  struct wimp_getcaretposition_block block;

  _swi(Wimp_GetCaretPosition, _IN(1), &block);

  if (line_editor_active(session))
  {

    if (session->line_editor_type==LINEEDIT_CHECKBOX_OFF || session->line_editor_type==LINEEDIT_CHECKBOX_ON)
    {
      if (block.window_handle!=session->pane_handle)
      {
        set_caret_position(session->pane_handle, 0, -1,
          get_icon_data_length(session->pane_handle, 0));
      }
    }
    else
    {
      if (block.window_handle!=session->window_handle)
      {
        set_caret_position(session->window_handle, -1, -1, -1);
      }
    }
  }
  else
  {
    if (block.window_handle!=session->window_handle)
    {
      /* no line editor, place in window */
      set_caret_position(session->window_handle,-1,-1,-1);
    }
  }
}


static void wimp_launchurl(struct session_struct *session, int xinit, int yinit)
{
#define ICHR(x,y) ((int) read_assigned_character(session,x,y))

  char   *url;
  char   *urlptr;
  char   *urlend;
  const char *urlchars;
  int     ymax = session->terminal_size.y + session->scrollback;
  int     xmax = session->terminal_size.x;
  int     url_size=0;
  int     x;
  int     y;

  struct  wimp_getwindowstate_block block;

  block.window_handle=session->window_handle;

  _swi(Wimp_GetWindowState, _IN(1), &block);

  yinit = -( (yinit - (block.max.y-block.scroll.y)) >> eig.y ) / redraw.r_charh;
  xinit =  ( (xinit - (block.min.x-block.scroll.x)) >> eig.x ) / redraw.r_charw;

  misc_logf("\nclicked on '%c'\n", (char) ICHR(xinit,yinit) );

  urlchars = lookup_static("URL_Chars");

  misc_logf("\nURLChars = %s\n", urlchars);

  if ( strchr( urlchars, ICHR(xinit,yinit) ) )
    return;

  /* go back to the start of the clicked on 'word' */
  /* NOTE! We deliberately do not wrap-backwards over lines */
  while ( xinit > 0 && strchr( urlchars, ICHR(xinit-1,yinit) )==NULL )
    xinit--;

  /* skip any initial space or colon or anything */
  if (!isalnum( ICHR(xinit,yinit) ))
    xinit++;

  x=xinit;
  y=yinit;

  /* go forward to the end of the form [ie. until we find whitespace] */
  /* NOTE! We currently allow the URL to flow to the next line */
  while ( strchr( urlchars, ICHR(x,y) )==NULL )
  {
    url_size++;
    x++;
    if ( x >= xmax )
    {
      x=0;
      y++;
      if ( y >= ymax )
        break;
    }
  }

  misc_logf("url_size=%d\n", url_size);

  url=malloc(url_size+sizeof(char)+8); /* leave space for a '\0', and 'http://' */
  if (!url)
  {
    generror(lookup_inbuffer("OutOfMem"), false);
    return;
  }

  x=xinit;
  y=yinit;
  urlptr = url;
  urlend = url+url_size;
  while ( urlptr<urlend )
  {
    *urlptr++ = (char) ICHR(x,y);
    x++;
    if ( x >= xmax )
    {
      x=0;
      y++;
      if ( y >= ymax )
        break;
    }
  }
  *urlptr = '\0';
  misc_logf("url = '%s'\n", url);

  /* lower-case the protocol (if present) and site name */
  {
    const char *endptr = strchr (url, '/');
    if (endptr > url && endptr[-1] == ':')
      /* we have a protocol; find the end of the site name */
      endptr = strchr (endptr + 1 + (endptr[1] == '/'), '/');
    if (!endptr)
      endptr = urlend;
    for (urlptr = url; urlptr < endptr; urlptr++)
      *urlptr = tolower (*urlptr);
  }

  /* we've got a url, check for full formedness */
  urlptr = url;
  while (isalpha((int) (*urlptr)))
    urlptr++;
  if (*urlptr != ':')
  {
    const char *scheme;
    /* there's no scheme section */
    if (strncmp(url, "ftp.", 4) == 0)
      scheme = "ftp://";
    else if (strchr(url, '@') != NULL)
      scheme = "mailto:";
    else
      scheme = "http://";

    /* prefix string we have with the scheme we've guessed */
    memmove(url + strlen(scheme), url, strlen(url) + 1);
    memcpy(url, scheme, strlen(scheme));
  }

  url_launch (url);

  /* Clean up as we shouldn't need this copy any more */
  free(url);
}



static void session_window_click(int x, int y, int buttons, int icon_handle, struct session_struct *session)
{
  struct wimp_getcaretposition_block caret;

  _swi (Wimp_GetCaretPosition, _IN(1), &caret);

  /* if we're not pressing MENU or Ctrl */
  /* (may want input focus to remain elswhere) */
  if (buttons != ClickMenu && buttons != DragMenu && !inkey(KEYBOARD_CTRL))
    wimp_claimcaret(session);


  if (session->mouse_mode!=MOUSE_MODE_OFF && session->mouse_terminal_attached)
  {
    mouse_handle_event(session, x, y, buttons);
  }
  else
  {
    int x_pos;
    int y_pos;
    struct wimp_getwindowstate_block block;

    block.window_handle=session->window_handle;

    _swi(Wimp_GetWindowState, _IN(1), &block);

    x_pos=block.min.x-block.scroll.x;
    y_pos=block.max.y-block.scroll.y;

    switch (buttons)
    {
      case DragSelect:
        {
          int seln_start;
          int seln_end;
          int click_point;

          /* check to see if the drag is starting inside the current selection */

          seln_start = MIN(selection_start, selection_end);
          seln_end   = MAX(selection_start, selection_end);

          click_point = -( ( (y - y_pos) >> eig.y ) / redraw.r_charh )
                            * session->terminal_size.x
                            + ( ( (x - x_pos) >> eig.x ) / redraw.r_charw );

          if (selection_session == session && click_point >= seln_start &&
              click_point <= seln_end)
          {
            int dragbox[4];
            /* drag is inside selection, do a save */
            dragbox[0] = x - 48 / 2;
            dragbox[1] = y - 48 / 2;
            dragbox[2] = x + 48 / 2;
            dragbox[3] = y + 48 / 2;
            _swi(DragASprite_Start, _INR(0, 4), 0xC5, 1 /* wimp sprite area */ ,
                 "file_fff", &dragbox, NULL);
            drag_window_handle = selection_session->window_handle;
          }
          else
          {
            /* if SELECT drag, then clear the selection and work out what where the selection */
            /* start is */
            force_redraw_selection();

            selection_session = session;
            selection_start = click_point;

            if (selection_start > (selection_session->terminal_size.x*
                                  (selection_session->terminal_size.y+
                                          selection_session->scrollback)))
            {
              selection_start=(selection_session->terminal_size.x*
                                  (selection_session->terminal_size.y+
                                          selection_session->scrollback));
            }

            if (selection_start<0)
            {
              selection_start=0;
            }

            selection_end=selection_start;
            selection_in_progress=true;
          }
        }
        break;

      case DragAdjust:
        /* if ADJUST drag, then if there is no selection or if it's not in this */
        /* window, then start one */
        if (selection_session != session)
        {
  	  force_redraw_selection();

	  selection_session = session;
          selection_start = -( ( (y - y_pos) >> eig.y ) / redraw.r_charh )
                            * session->terminal_size.x
                            + ( ( (x - x_pos) >> eig.x ) / redraw.r_charw );

	  if (selection_start>(selection_session->terminal_size.x*
  			      (selection_session->terminal_size.y+
				      selection_session->scrollback)))
	  {
	    selection_start=(selection_session->terminal_size.x*
  			      (selection_session->terminal_size.y+
				      selection_session->scrollback));
	  }

	  if (selection_start<0)
	  {
	    selection_start = 0;
	  }

	  selection_end=selection_start;
	  selection_in_progress=true;
	  selection_adjust_dragging=SELECTION_ADJUST_END;
        }
        else
        {
	  selection_in_progress=true;
        }
        break;

      case DoubleSelect:
        wimp_launchurl(session, x, y);
        break;

      case ClickSelect:
        /* if SELECT click, then clear the selection if the caret is already in the window */
        if (selection_session &&
            (caret.window_handle == session->window_handle ||
             caret.window_handle == session->pane_handle))
        {
          selection_click(x,x_pos,y,y_pos);
        }
        break;

      case ClickMenu: /* menu click */
        terminal_menu_session = session;
        create_terminal_menu();
        break;

      case ClickAdjust:
        /* if ADJUST click, then extend the selection */
        if (selection_session == session)
          selection_adjust(x,x_pos,y,y_pos);
        break;
    }
  }
}



static void session_pane_click(int x, int y, int buttons, int icon_handle, struct session_struct *session)
{
  switch (icon_handle)
  {
    case 1:
    {
      struct wimp_geticonstate_block block;

      block.window_handle=session->pane_handle;
      block.icon_handle  =icon_handle;

      _swi(Wimp_GetIconState, _IN(1), &block);

      if (block.icon_flags & WIMP_ICON_SELECTED_BIT)
      {
        set_icon_state(session->pane_handle, 0, 0, WIMP_ICON_DELETED_BIT);
        check_set_caret_position(session->pane_handle, 0);

        {
          struct wimp_getcaretposition_block caret_pos;

          _swi(Wimp_GetCaretPosition, _IN(1), &caret_pos);

          if (caret_pos.window_handle==session->window_handle)
          {
            set_caret_position(session->pane_handle, 0, -1,
                               get_icon_data_length(session->pane_handle, 0));
          }
        }
      }
      else
      {
        struct wimp_getcaretposition_block caret_pos;

        _swi(Wimp_GetCaretPosition, _IN(1), &caret_pos);

        if (caret_pos.window_handle==session->pane_handle)
        {
          set_caret_position(session->window_handle,-1,-1,-1);
        }

        set_icon_state(session->pane_handle, 0, WIMP_ICON_DELETED_BIT, WIMP_ICON_DELETED_BIT);
      }

      block.window_handle=session->pane_handle;
      block.icon_handle  =0;

      _swi(Wimp_GetIconState, _IN(1), &block);

      force_redraw(session->pane_handle, block.min.x, block.min.y,
                   block.max.x, block.max.y);

    }
  }
}



/* Mouse click */

void wimp_mouse_click(int x, int y, int buttons, int window_handle, int icon_handle)
{
  static int win_iconbar = -2;
  static const struct { int *window; void (*handler)(int,int,int,int); } mousehandlers[] =
  {
    {&win_iconbar,   iconbar_click       },
    {&win_info,      win_info_click      },
    {&win_open,      win_open_click      },
    {&win_save,      win_save_click      },
    {&win_resize,    win_resize_click    },
    {&win_choices,   win_choices_click   },
    {&win_paneterm,  win_paneterm_click  },
    {&win_panecols,  win_panecols_click  },
    {&win_panehot,   win_panehot_click   },
    {&win_hotpane,   win_hotpane_click   },
    {&win_hotedit,   win_hotedit_click   },
    {&win_panemisc,  win_panemisc_click  },
    {&win_spoolsave, win_spoolsave_click },
    {&win_termterm,  win_termterm_click  },
    {NULL,NULL}
  };
  int i;

  for(i=0; mousehandlers[i].window; i++)
  {
    if (window_handle==*mousehandlers[i].window)
    {
      mousehandlers[i].handler(x, y, buttons, icon_handle);
      return;
    }
  }

  {
    struct session_struct *session = sessions;

    while (session)
    {
      if (window_handle == session->window_handle)
      {
        session_window_click(x,y,buttons,icon_handle,session);
	return;
      }
      else if ((session->line_editor_type==LINEEDIT_CHECKBOX_OFF ||
                session->line_editor_type==LINEEDIT_CHECKBOX_ON) &&
               (window_handle == session->pane_handle))
      {
        session_pane_click(x,y,buttons,icon_handle,session);
	return;
      }

      session = session->next;
    }
  }
}



static void wimp_initiatesave(const char *filename)
{
  union wimp_poll_block block;
  struct wimp_getpointerinfo_block gpi_block;

  _swi(Wimp_GetPointerInfo, _IN(1), &gpi_block);

  block.user_message.contents.data_save.window_handle=gpi_block.window_handle;
  block.user_message.contents.data_save.icon_handle  =gpi_block.icon_handle;
  block.user_message.contents.data_save.pos          =gpi_block.pos;
  block.user_message.contents.data_save.size         =selection_end-selection_start;
  block.user_message.contents.data_save.filetype     =0xFFF;

  strcpy(block.user_message.contents.data_save.file_name, filename);

  block.user_message.length      =(48+strlen(filename)) & ~3;
  block.user_message.your_ref    =0;
  block.user_message.message_code=WIMP_MESSAGE_DATASAVE;

  _swi(Wimp_SendMessage, _INR(0,3), 18, &block,
                         block.user_message.contents.data_save.window_handle,
                         block.user_message.contents.data_save.icon_handle);
  msg_ref = block.user_message.my_ref;
}

static void reply_to_dataload (union wimp_poll_block *block)
{
  assert (block->user_message.message_code == WIMP_MESSAGE_DATALOAD);

  block->user_message.your_ref     = block->user_message.my_ref;
  block->user_message.message_code = /*WIMP_MESSAGE_DATASAVE*/WIMP_MESSAGE_DATALOADACK; /*surely?*/

  _swi (Wimp_SendMessage, _INR(0,2), 18, block, block->user_message.sender_ref);
  msg_ref = block->user_message.my_ref;
}

/* Drag finished */

void drag_finished(void)
{
  if (drag_window_handle==win_save)
  {
    char string[1024];
    int loop;

    read_icon_data(win_save, icon_save_filename, string, sizeof(string));
    loop = strlen(string);

    while (loop>=0 && string[loop]!='.' && string[loop]!=':')
    {
      loop--;
    }
    allow_ansi_colour = SAVE_FLAG_ANSI;
    wimp_initiatesave(string+loop+1);
  }
  else if (drag_window_handle==win_hotpane)
  {
    hotlist_dragdone();
  }
  else if (drag_window_handle==win_spoolsave)
  {
    char string[1024];
    int loop;

    read_icon_data(win_spoolsave, icon_spoolsave_filename, string, sizeof(string));
    loop = strlen(string);

    while (loop>=0 && string[loop]!='.' && string[loop]!=':')
    {
      loop--;
    }
    wimp_initiatesave(string+loop+1);
  }
  else if (selection_session &&
           drag_window_handle == selection_session->window_handle)
  {
    allow_ansi_colour = SAVE_NO_ANSI; /* This is debatable IMO */
    wimp_initiatesave("Selection");
  }
}



/* Key pressed */

void key_pressed(int window_handle, int icon_handle, int key, int extra)
{
  int ekey = extra >> 16;
  bool key_status=false;
  char message[MESSAGE_MAX_CONNECTION];

  if (window_handle == win_open)
  {
    char string[MESSAGE_MAX_CONNECTION];
    int connection_type;

    read_icon_data(win_open, icon_open_contype, string, sizeof(string));

    if (strcmp(string, lookup("SSH", message, MESSAGE_MAX_CONNECTION))==0)
      connection_type=NETTLE_SSH;
    else if (strcmp(string,lookup("Taskwindow", message, MESSAGE_MAX_CONNECTION))==0)
      connection_type=NETTLE_TASKWINDOW;
    else
      connection_type=NETTLE_TELNET;

    switch (key)
    {
      case 13:
        switch (icon_handle)
        {
          case icon_open_host:
            switch (connection_type)
            {
              case NETTLE_TELNET:
                if (start_connection())
                  close_window(win_open);

                break;
              case NETTLE_SSH:
                set_caret_position(win_open,icon_open_command,-1, get_icon_data_length(win_open, icon_open_command));
                break;
            }
            key_status=true;
            break;

          case icon_open_command:
            switch (connection_type)
            {
              case NETTLE_SSH: case NETTLE_TASKWINDOW:
                if (start_connection())
                  close_window(win_open);

                break;
            }
            key_status=true;
            break;
        }
        break;

      case 27:
        close_window(win_open);
        break;
    }
  }
  else if (window_handle==win_resize)
  {
    switch (icon_handle)
    {
      case icon_resize_scroll: /* The final icon in the window - scrollback */
        switch (key)
        {
          case 13:
            win_resize_set();
            /* We close the menu ourselves */
            _swi(Wimp_CreateMenu, _IN(1), -1);
            key_status=true;
            break;
        }
        break;
    }
  }
  else if (colourpicker_handle && window_handle == menu_window)
  {
    /* Keypresses in the ColourPicker window have already been handled */
    key_status = true;
  }
  else if (window_handle == win_spoolsave)
  {
    switch (icon_handle)
    {
      case icon_spoolsave_filename:
        switch (key)
        {
          case 13:
            if (spool_save_event())
              _swi(Wimp_CreateMenu, _IN(1), -1);

            break;
        }
        break;
    }
  }

  if (key_status==false)
  {
    {
      {
        struct session_struct *session = sessions;

        while (session && window_handle != session->window_handle)
        {
          session = session->next;
        }

        if (session)
        {
          switch (key)
          {
            case 0x18E:           /* Down, sPageDown */
              if (ekey == 54)
              {
                scroll_term (session, -1);
                key_status=true;
              }
              break;
            case 0x18F:           /* Up, sPageUp */
              if (ekey == 33)
              {
                scroll_term (session, 1);
                key_status=true;
              }
              break;
            case 0x1A2:          /* Ctrl+F2 - only if session isn't alive */
              if (session->socket_state != NETTLE_SESSION_CONNECTED)
              {
                remove_session(session);
                key_status=true;
              }
              break;
          }

          if (key_status==false)
          {
            if (session->socket_state==NETTLE_SESSION_CONNECTED)
            {
              if (pass_f12)
              {
                process_wimp_key(session, key, extra);
                key_status=true;
              }
              else
              {
                if (key!=0x1CC && key!=0x1DC && key!=0x1EC && key!=0x1FC)
                {
                  process_wimp_key(session, key, extra);
                  key_status=true;
                }
              }

              if ( key_status && scroll_on_keypress )
              {
                struct wimp_getwindowstate_block block;

                block.window_handle = session->window_handle;
                _swi (Wimp_GetWindowState, _IN(1), &block);
                block.scroll.y = -1<<30;
                open_window((struct wimp_openwindow_block *) &block, session->window_handle);
              }
            }
          }
        }
      }
    }
  }

  if (key_status==false)
  {
    struct session_struct *session = sessions;

    while (session && window_handle != session->pane_handle)
    {
      session = session->next;
    }

    if (session && session->line_editor_type!=LINEEDIT_NONE)
    {
      int line_editor_icon = 0;

      switch (key)
      {
        case 13:
          if (session->socket_state==NETTLE_SESSION_CONNECTED)
          {
            process_line_editor(session);
            key_status=true;
          }
          break;
        case 0x18E: /* Down, sPageDown */
          key_status=true;

          if (ekey == 54)
          {
            scroll_term(session, -1);
            break;
          }

          if (session->line_editor_position==0)
          {
            _swi(OS_WriteI + 7, 0);
          }
          session->line_editor_position--;

          if (session->line_editor_position<0)
          {
            session->line_editor_position=0;
          }

          {
            char icon_data[1024];

            set_icon_data(session->pane_handle, line_editor_icon,
                read_mem((char *)icon_data, session->line_editor_history +
                (session->line_editor_position * MAX_LINEEDITOR_LENGTH), sizeof(icon_data)));
          }

          set_caret_position(session->pane_handle, line_editor_icon, -1,
                             get_icon_data_length(session->pane_handle, line_editor_icon));

          break;
        case 0x18F: /* Up, sPageUp */
          key_status=true;

          if (ekey == 33)
          {
            scroll_term(session, 1);
            break;
          }

          if (session->line_editor_position==session->line_editor_total-1)
          {
            _swi(OS_WriteI + 7, 0);
          }
          session->line_editor_position++;

          if (session->line_editor_position>session->line_editor_total-1)
          {
            session->line_editor_position=session->line_editor_total-1;
          }

          {
            char icon_data[1024];

             set_icon_data(session->pane_handle,line_editor_icon,
                read_mem(icon_data, session->line_editor_history+
                 (session->line_editor_position * MAX_LINEEDITOR_LENGTH), sizeof(icon_data)));
          }

          set_caret_position(session->pane_handle, line_editor_icon, -1,
                             get_icon_data_length(session->pane_handle, line_editor_icon));

          break;
        case 0x1A2:          /* Ctrl+F2 */
        {
          bool status=true;

          if (session->socket_state == NETTLE_SESSION_CONNECTED)
          {
            status = generror_question("ConnectionOpenButtons","ConnectionOpen",0);
          }

          if (status)
          {
            remove_session(session);
          }

          break;
        }
      }

      if ( ( key == 13 || key_status == false ) && scroll_on_keypress )
      {
        if ( !( !pass_f12 && ( key==0x1CC || key==0x1DC || key==0x1EC || key==0x1FC ) ) )
        {
          struct wimp_getwindowstate_block block;

          block.window_handle = session->window_handle;
          _swi (Wimp_GetWindowState, _IN(1), &block);
          block.scroll.y = -1<<30;
          open_window((struct wimp_openwindow_block *) &block, session->window_handle);
        }
      }
    }
  }

  if (key_status==false)
  {
    _swi(Wimp_ProcessKey, _IN(0), key);
  }
}



/* Menu selection */

void wimp_menu_selection(int *selection)
{
  switch (menu_open)
  {
    case MENU_ICONBAR:
      switch (selection[0])
      {
        case 1:
          open_choices_window();
          break;

        case 2:
          /* Open connect window */
          open_open_window(true);
          break;

        case 3:
          if (selection[1] >= 0)
            fire_hotlist(selection[1]);
          break;

        case 4:
          /* Script handling - not implemented yet */
          break;

        case 5:
          /* Bring a session window to the front */
          if (selection[1] >= 0)
          {
            struct session_struct *session = get_termlist_entry(selection[1]);
            if (session)
              open_window(NULL, session->window_handle);
          }
          break;

        case 6:
          /* Quit */
          shutdown_flag=-1; /* Just make sure we don't shut down the computer by mistake */
          close_program();
      }
      break;

    case MENU_TERMINAL:
      switch (selection[0])
      {
        case 0: /* selection menu */
          switch (selection[1])
          {
            case 0: /* save selection */
              break;

            case 1: /* launch url */
              if (selection_session!=NULL)
              {
                char url[1024];
                int i;
                int start=(selection_start<selection_end) ? selection_start : selection_end;
                int end=(selection_start<selection_end) ? selection_end : selection_start;
                int url_pos=0;

                /* Limit the URL to 1023 chars */
                if (end>start+1023)
                  end=start+1023;

                for (i=start; i<end; i++)
                {
                  char byte=read_assigned_character(selection_session, i % selection_session->terminal_size.x, i / selection_session->terminal_size.x);

                  if (byte!=32)
                    url[url_pos++]=byte;
                }

                url[url_pos]='\0';

                url_launch(url);
              }
              break;

            case 2: /* send */
              selection_paste(terminal_menu_session);
              break;

            case 3: /* copy to clipboard */
              clipboard_claim();
              break;

            case 4:
              {
                /* Clear selection */
                force_redraw_selection();
                selection_session = NULL;
              }
              break;
          }
          break;
        case 2: /* paste from clipboard */
          clipboard_datarequest(terminal_menu_session);
          break;
        case 3: /* options */
          options_session=terminal_menu_session;
          open_options_window(true);

          break;
        case 4: /* spool menu */
          switch (selection[1])
          {
            case 0: /* open spool */
              break;

            case 1: /* close spool */
              spool_close(terminal_menu_session, true);
              break;
          }
          break;
        case 5: /* reconnect */
          nettle_reconnect(terminal_menu_session);
      }
      break;

    case MENU_CONNECTION:
      if (selection[0] != -1)
      {
        switch (selection[0])
        {
          case 0:
            set_icon_data(menu_window, menu_icon, lookup_static("Telnet"));
            break;
          case 1:
            set_icon_data(menu_window, menu_icon, lookup_static("SSH"));
            break;
          case 2:
            set_icon_data(menu_window, menu_icon, lookup_static("Taskwindow"));
            break;
        }
        if (menu_window == win_open)
          open_open_window(false);
        else if (menu_window == win_hotedit)
          hotlist_inform(menu_icon, selection[0]);
      }
      break;

    case MENU_TERMINAL_TYPE:
      if (selection[0] != -1)
      {
        char string[8];
        sprintf(string, "Term%d", selection[0]);

        set_icon_data(menu_window, menu_icon, lookup_static (string));
        if (menu_window == win_hotedit)
          hotlist_inform(menu_icon, selection[0]);
      }
      break;

    case MENU_HOTLIST:
      fire_hotlist(selection[0]);
      break;

    case MENU_LINEED_TYPE:
      if (selection[0] != -1)
      {
        char string[8];
        sprintf(string, "LineEd%d", selection[0]);
	set_icon_data(menu_window, menu_icon, lookup_static (string));
        if (menu_window == win_hotedit)
          hotlist_inform(menu_icon, selection[0]);
      }
      break;

    case MENU_CURSOR_TYPE:
      if (selection[0] != -1)
      {
        char string[8];
        sprintf(string, "Cursor%d", selection[0]);
	set_icon_data(menu_window, menu_icon, lookup_static (string));
      }
      break;

    case MENU_ZAPFONTLIST:
      if (selection[0] != -1)
      {
	char string[256];
	if (read_zap_font_selection (selection, string, sizeof (string)))
	  set_icon_data (win_paneterm, menu_icon, string);
      }
  }

  {
    struct wimp_getpointerinfo_block block;

    _swi(Wimp_GetPointerInfo, _IN(1), &block);

    /* If ADJUST, then re-open menu where it was before */
    if (block.buttons==1)
    {
      switch (menu_open)
      {
        case MENU_ICONBAR:
          create_iconbar_menu();
          break;

        case MENU_TERMINAL:
          create_terminal_menu();
          break;

        case MENU_CONNECTION:
          create_connection_type_menu(menu_window, menu_icon, menu_icon);
          break;

        case MENU_TERMINAL_TYPE:
          create_terminal_type_menu(menu_window, menu_icon, menu_icon);
          break;

        case MENU_HOTLIST:
          iconbar_click(0,0,1,iconbar_handle);
          break;

	case MENU_LINEED_TYPE:
	  create_lineeditor_type_menu(menu_window, menu_icon, menu_icon);
	  break;

	case MENU_CURSOR_TYPE:
	  create_cursor_type_menu(menu_window, menu_icon, menu_icon);
	  break;

	case MENU_ZAPFONTLIST:
	  reopen_zap_font_menu();
	  break;
      }
    }
  }
}



/* Lose caret */

void lose_caret(int window_handle)
{
  struct session_struct *session = sessions;
  struct coords pos;

  while (session && window_handle != session->window_handle)
  {
    session = session->next;
  }

  if (session)
  {
    struct wimp_getcaretposition_block block;

    _swi(Wimp_GetCaretPosition, _IN(1), &block);

    /* if lineeditor is off, or the caret isn't in the pane, then make the cursor look like */
    /* we have no input */
    if (!(line_editor_active(session)) || block.window_handle != session->pane_handle)
    {
      pos=get_cursor_position(session);
      write_assigned_flags(session, pos.x, pos.y,
      			   read_assigned_flags(session, pos.x, pos.y) | NETTLE_FLAG_NO_INPUT);
      cursor_session = NULL;

      force_redraw(session->window_handle,
      		   pos.x*redraw.r_charw << eig.x,
		   (-pos.y-1)*redraw.r_charh << eig.y,
		   (pos.x+1)*redraw.r_charw << eig.x,
		   (-pos.y)*redraw.r_charh << eig.y);
    }
    return;
  }

  session = sessions;

  while (session && window_handle != session->pane_handle)
  {
    session = session->next;
  }

  if (session)
  {
    /* if the caret is being lost from the pane, then deselect the cursor too */
    pos=get_cursor_position(session);
    write_assigned_flags(session, pos.x, pos.y,
    			 read_assigned_flags(session, pos.x, pos.y) | NETTLE_FLAG_NO_INPUT);

    cursor_session = NULL;

    force_redraw(session->window_handle,
    		 pos.x*redraw.r_charw << eig.x,
		 (-pos.y-1)*redraw.r_charh << eig.y,
		 (pos.x+1)*redraw.r_charw << eig.x,
		 (-pos.y)*redraw.r_charh << eig.y);
  }
}

static void set_cursor_session (const struct session_struct *session)
{
  cursor_session = session;
  if (cursor_blink)
  {
    nextcursortime = _swi (OS_ReadMonotonicTime, 0) + CURSOR_BLINK_DELAY;
    cursor_state = true;
  }
}


/* Gain caret */

void gain_caret(int window_handle)
{
  struct session_struct *session = sessions;
  struct coords pos;

  while (session && window_handle != session->window_handle)
  {
    session = session->next;
  }

  if (session)
  {
    /* if the handle is the window handle, get input back for the cursor */
    set_cursor_session (session);
    pos=get_cursor_position(session);

    write_assigned_flags(session, pos.x, pos.y,
                         read_assigned_flags(session, pos.x, pos.y) & ~NETTLE_FLAG_NO_INPUT);

    force_redraw(session->window_handle,
                 pos.x*redraw.r_charw << eig.x,
                 (-pos.y-1)*redraw.r_charh << eig.y,
                 (pos.x+1)*redraw.r_charw << eig.x,
                 (-pos.y)*redraw.r_charh << eig.y);

    if (line_editor_active(session) && (session->line_editor_type==LINEEDIT_CHECKBOX_OFF || session->line_editor_type==LINEEDIT_CHECKBOX_ON))
    {
      /* if line editor and doesn't already have caret, then set the caret in pane */
      struct wimp_getcaretposition_block block;

      _swi(Wimp_GetCaretPosition, _IN(1), &block);

      if (block.window_handle!=session->pane_handle &&
          (session->line_editor_type==LINEEDIT_CHECKBOX_OFF ||
           session->line_editor_type==LINEEDIT_CHECKBOX_ON) &&
          block.index!=get_icon_data_length(session->pane_handle,0))
      {
        set_caret_position(session->pane_handle,0,-1,
               		   get_icon_data_length(session->pane_handle,0));
      }
    }

    return;
  }

  session = sessions;

  while (session && window_handle != session->pane_handle)
  {
    session = session->next;
  }

  if (session)
  {
    set_cursor_session (session);
    pos=get_cursor_position(session);

    /* unset no input flag again */
    write_assigned_flags(session, pos.x, pos.y,
    			 read_assigned_flags(session, pos.x, pos.y) & ~NETTLE_FLAG_NO_INPUT);

    force_redraw(session->window_handle,
    		 pos.x*redraw.r_charw << eig.x,
		 (-pos.y-1)*redraw.r_charh << eig.y,
		 (pos.x+1)*redraw.r_charw << eig.x,
	         (-pos.y)*redraw.r_charh << eig.y);

    {
      struct wimp_getcaretposition_block block;

      _swi(Wimp_GetCaretPosition, _IN(1), &block);

      /* again, set caret if it's not already in the pane */
      if (block.window_handle!=session->pane_handle &&
          (session->line_editor_type==LINEEDIT_CHECKBOX_OFF ||
           session->line_editor_type==LINEEDIT_CHECKBOX_ON) &&
          block.index!=get_icon_data_length(session->pane_handle,0))
      {
        set_caret_position(session->pane_handle, 0, -1,
           		   get_icon_data_length(session->pane_handle,0));
      }
    }
  }
}



/**
 * Note - url has already had the 'telnet:' removed
 */
static int wimp_handletelneturl(const char *url)
{
  return nettle_start_telnet_connection_friedport(url);
}


#ifdef INSECURE_STUFF
/**
 * Note - url has already had the 'ansitaskwindow:' removed
 */
static int wimp_handleansiurl(const char *url)
{
  return nettle_start_taskwindow(url);
}
#endif


int wimp_handleurl(const char *url, int checkonly)
{
  int (*handler)(const char *);

  if (strncmp(url, "telnet:", strlen("telnet:")) == 0)
  {
    url += strlen("telnet:");
    handler = wimp_handletelneturl;
  }
#ifdef INSECURE_STUFF
  else if (strncmp(url, "ansitaskwindow:", strlen("ansitaskwindow:")) == 0)
  {
    url += strlen("ansitaskwindow:");
    handler = wimp_handleansiurl;
  }
#endif /* INSECURE_STUFF */
  else
  {
    handler = NULL;
  }

  if (handler == NULL)
    return 0;

  if (checkonly != 0)
    return 1;

  return handler(url);
}

/**
 * Find session that matches given window handle
 *
 * @return appropriate session, or NULL if session was found that relates
 * to the window */
static struct session_struct *get_session_from_winhan(int window)
{
  struct session_struct *session = sessions;

  while (session != NULL &&
         session->window_handle != window &&
         session->pane_handle != window)
  {
    session = session->next;
  }

  return session;
}

/* query_non_text_file */
/* Purpose: ask the user about loading unrecognised filetypes */

static bool query_non_text_file (union wimp_poll_block *wimp_block)
{
  /* Prompt the user if they try to paste something other than those above */
  /* We get the filetype textual form via OS_FSControl 18 */

  int buff[3];
  char *buf=(char *) &buff[0];

  _swi(OS_FSControl, _IN(0)|_IN(2)|_OUTR(2,3),
       18,
       wimp_block->user_message.contents.data_save.filetype,
       &buff[0],
       &buff[1] );

  buf[8]=0;
  while (buf[0] && buf[ strlen(buf)-1 ] == ' ' )
    buf[ strlen(buf)-1 ] = 0;

  return (generror_question("SureFileTypeLoadButtons",
                        "SureFileTypeLoad",
                        1,
                        buf));
}


/* Wimp message */

void wimp_message(union wimp_poll_block *wimp_block)
{
  int message_number = wimp_block->user_message.message_code;
  int sender_ref     = wimp_block->user_message.sender_ref;
  int my_ref         = wimp_block->user_message.my_ref;
  int your_ref       = wimp_block->user_message.your_ref;

  switch (message_number)
  {
    case WIMP_MESSAGE_QUIT:
      quit_flag=true;
      break;
    case WIMP_MESSAGE_DATASAVE:
      if (msg_ref && your_ref != msg_ref && my_ref != msg_ref)
        break;

      {
        bool paste_flag=false;

        msg_ref = 0;
        switch (wimp_block->user_message.contents.data_save.filetype)
        {
          case 0xFAF: /* HTML */
          case 0xFFF: /* Text */
            /* text must be dragged to a terminal window or its input pane */
            {
              struct session_struct *session = sessions;
              int window_handle = wimp_block->user_message.contents.data_save.window_handle;

              while (session
                     && session->window_handle != window_handle
                     && session->pane_handle != window_handle)
              {
                session = session->next;
              }

              if (!session)
                break;

              paste_flag=true;
            }
            break;

          case 0xB28: /* ANT URL */
          case 0xF91: /* Acorn URI */
            /* we don't care where URL and URI files are dropped */
            paste_flag=true;
            break;
          default:
            /* For all other filetypes, ask the user */
            if (query_non_text_file(wimp_block))
            {
              paste_flag=true;
              queried_user_filetype=true;
            }
            break;
        }

        if (paste_flag)
        {
          /* Common DataSaveAck reply code */
          wimp_block->user_message.contents.data_save_ack.size = -1;
          strcpy (wimp_block->user_message.contents.data_save_ack.file_name, "<Wimp$Scrap>");
          wimp_block->user_message.length = 60;
          wimp_block->user_message.your_ref = my_ref;
          wimp_block->user_message.message_code = WIMP_MESSAGE_DATASAVEACK;

          _swi(Wimp_SendMessage, _INR(0,2), 18, wimp_block, sender_ref);
          msg_ref = wimp_block->user_message.my_ref;
        }
      }
      break;

    case WIMP_MESSAGE_DATASAVEACK:
      {
        char string[1024];

        if (!msg_ref || (your_ref != msg_ref && my_ref != msg_ref))
          break;

        msg_ref = 0;

        read_mem(string, wimp_block->user_message.contents.data_save_ack.file_name,
                 sizeof(string));

        if (drag_window_handle == win_spoolsave)
        {
          if (wimp_block->user_message.contents.data_save_ack.size==-1)
          {
            generror("SpoolFailNonPermanent", true);
            break;
          }
          else
          {
            spool_open(terminal_menu_session, string,
                       read_icon_ticked(win_spoolsave, icon_spoolsave_raw));

            if (instr(string,"Wimp$Scrap")==-1)
            {
              set_icon_data(drag_window_handle, icon_spoolsave_filename, string);
            }
          }
        }
        else if (selection_session)
        {
          switch (allow_ansi_colour)
          {
            case SAVE_NO_ANSI:
              save_selection(string, false);
              break;

            case SAVE_FORCE_ANSI:
              save_selection(string, true);
              break;

            default:
              save_selection(string, read_icon_ticked(win_save, icon_save_ansi));
              if (instr(string,"Wimp$Scrap")==-1 && drag_window_handle == win_save)
              {
                set_icon_data(drag_window_handle, icon_save_filename, string);
              }
              break;
          }
        }

        wimp_block->user_message.your_ref    =my_ref;
        wimp_block->user_message.message_code=WIMP_MESSAGE_DATALOAD;

        _swi(Wimp_SendMessage, _INR(0,2), 18, wimp_block, sender_ref);
        msg_ref = wimp_block->user_message.my_ref;

        _swi(Wimp_CreateMenu, _IN(1), -1);
      }
      break;

    case WIMP_MESSAGE_DATALOAD:
    {
      struct session_struct *session;
      int window_handle;
      char *filename=wimp_block->user_message.contents.data_load.file_name;
      char *tmp_filename;
      bool is_scrap = false;

      /* You'd hope this wasn't necessary, but there's lots of broken apps out there! */
      misc_zero_terminate_ctrl_string( filename );

      /* Now we (crudely) work out if we've been given a scrap transfer */
      tmp_filename = malloc( strlen( filename )+1 );
      if (tmp_filename)
      {
        strcpy( tmp_filename, filename );
        if ( strstr( misc_string_lower( tmp_filename ),"wimp$scrap" ) )
          is_scrap = true;
        free( tmp_filename );
      }

      if (msg_ref && your_ref != msg_ref && my_ref != msg_ref)
        break;

      msg_ref = 0;
      window_handle = wimp_block->user_message.contents.data_load.window_handle;
      session = get_session_from_winhan(window_handle);

      if (inkey(KEYBOARD_SHIFT) && is_scrap==false)
      {
        /* Don't acknowledge as we just want the filename */

        if (session)
        {
          paste_in_data (window_handle, filename, strlen(filename));
        }
      }
      else
      {
        switch (wimp_block->user_message.contents.data_save.filetype)
        {
          case 0xB28: /* ANT URL */
            if (parse_ant_url (wimp_block))
              reply_to_dataload (wimp_block);
            break;
          case 0xF91: /* Acorn URI */
            if (parse_acorn_uri (wimp_block))
              reply_to_dataload (wimp_block);
            break;
          case 0xFAF: /* HTML */
          case 0xFFF: /* Text */
            if (paste_text_file (wimp_block))
              reply_to_dataload (wimp_block);
            break;
          default:
            if (session)
            {
              if (queried_user_filetype || query_non_text_file (wimp_block))
              {
                if (paste_text_file (wimp_block))
                  reply_to_dataload (wimp_block);

                queried_user_filetype=false;
              }
            }
            else
            {
              if (wimp_block->user_message.contents.data_save.window_handle==win_open &&
                  wimp_block->user_message.contents.data_save.icon_handle==icon_open_command)
              {
                char string[MESSAGE_MAX_CONNECTION];
                char type[MESSAGE_MAX_CONNECTION];

                read_icon_data(win_open, icon_open_contype, string, MESSAGE_MAX_CONNECTION);

                if (strcmp(string, lookup("Taskwindow", type, MESSAGE_MAX_CONNECTION)) == 0)
                {
                  struct wimp_getcaretposition_block block;

                  _swi(Wimp_GetCaretPosition, _IN(1), &block);

                  set_icon_data(win_open, icon_open_command,
                                wimp_block->user_message.contents.data_save.file_name);

                  if (block.window_handle==win_open && block.icon_handle==icon_open_command)
                  {
                    set_caret_position(win_open, icon_open_command, -1,
                                       get_icon_data_length(win_open, icon_open_command));
                  }
                }
              }
            }
            break;
        }
      }
      break;
    }

    case WIMP_MESSAGE_DATALOADACK:
      msg_ref = 0;
      break;

    case WIMP_MESSAGE_PREQUIT:
      {
        struct session_struct *session = sessions;
        bool can_we_safely_quit=true;

        while (session && can_we_safely_quit)
        {
          if (session->socket_state == NETTLE_SESSION_CONNECTED)
          {
            can_we_safely_quit=false;
          }
          session = session->next;
        }

        if (!can_we_safely_quit)
        {
          if (wimp_block->user_message.length<20)
            shutdown_flag=0;
          else
            shutdown_flag=wimp_block->user_message.contents.prequit.shutdown_flag;

          taskmanager_taskhandle = wimp_block->user_message.sender_ref;
          wimp_block->user_message.your_ref    =my_ref;
          _swi(Wimp_SendMessage, _INR(0,2), 19, wimp_block, sender_ref);

          close_program();
        }
      }
      break;

    case WIMP_MESSAGE_CLIP_CLAIMENTITY:
      if (wimp_block->user_message.sender_ref != task_handle)
        we_own_clipboard = false;
      break;

    case WIMP_MESSAGE_CLIP_DATAREQUEST:
      if (we_own_clipboard)
      {
        wimp_block->user_message.contents.data_save.size = selection_end-selection_start;
        wimp_block->user_message.contents.data_save.filetype = 0xFFF;
        strcpy (wimp_block->user_message.contents.data_save.file_name, "Clipboard");
        wimp_block->user_message.length = 60;
        wimp_block->user_message.your_ref = wimp_block->user_message.my_ref;
        wimp_block->user_message.message_code = WIMP_MESSAGE_DATASAVE;
        _swi (Wimp_SendMessage, _INR(0,2), 18, wimp_block,
              wimp_block->user_message.sender_ref);
        msg_ref = wimp_block->user_message.my_ref;
        allow_ansi_colour = SAVE_NO_ANSI;
      }
      break;

    case WIMP_MESSAGE_MODECHANGE:
      mode_change();
      break;

    case WIMP_MESSAGE_TASKWINDOW_OUTPUT:
      {
        struct session_struct *session = sessions;

        if (!session) {
          return;
        }

        while (session && session->socket_handle != sender_ref) {
          session = session->next;
        }

        if (session)
        {
          write_out_data(session,
                         wimp_block->user_message.contents.task_window_output.data,
                         wimp_block->user_message.contents.task_window_output.size);
        }
        wimp_block->user_message.my_ref=wimp_block->user_message.your_ref;

        _swi(Wimp_SendMessage, _INR(0,2), 19, wimp_block, sender_ref);
      }
      break;

    case WIMP_MESSAGE_TASKWINDOW_EGO:
      {
        struct session_struct *session = sessions;
        int txt_handle = wimp_block->user_message.contents.task_window_ego.txt_handle;

        while (session && session != (struct session_struct *) txt_handle) {
          session = session->next;
        }

        if (session)
        {
          char string[256];

          sprintf(string, "Nettle/%s (%s)", terminal_name[session->terminal_type],
                  lookup_static("Taskwindow"));

          session->socket_state = NETTLE_SESSION_CONNECTED;
          session->socket_handle = sender_ref;
          set_title_bar (session->window_handle, string);
        }
      }
      break;

    case WIMP_MESSAGE_TASKWINDOW_MORIO:
      {
        struct session_struct *session = sessions;

        if (!session)
          return;

        while (session && session->socket_handle != sender_ref)
        {
          session = session->next;
        }

        if (session)
        {
          set_title_bar (session->window_handle, lookup_static ("title_closed"));
          session->socket_state=NETTLE_SESSION_NONE;
          session->socket_handle=-1;
          reset_terminal (session);
          write_out_strings (session, "\r\n", lookup_static ("twclosed"), "\r\n", 0);
        }
      }
      break;

    case WIMP_MESSAGE_MENUWARNING:
      switch (wimp_block->user_message.contents.menu_warning.submenu)
      {
        case MENU_ZAPFONTLIST:
          create_zap_font_submenu (wimp_block->user_message.contents.menu_warning.selection,
                                    wimp_block->user_message.contents.menu_warning.pos);
	  break;
      }
      break;

    case WIMP_MESSAGE_MENUSDELETED:
      switch (menu_open)
      {
	case MENU_ZAPFONTLIST:
	  delete_zap_font_menu ();
	  break;
      }
      menu_open = MENU_NONE;
      colourpicker_handle = 0;
      break;

    case COLOURPICKER_COLOUR_CHOICE:
      if (wimp_block->user_message.contents.colourpicker_choice.picker_handle == colourpicker_handle)
        set_colour_icon (menu_icon, wimp_block->user_message.contents.colourpicker_choice.colour, true);
      break;

    case COLOURPICKER_CLOSE_REQUEST:
      if (wimp_block->user_message.contents.colourpicker_close.picker_handle == colourpicker_handle)
      {
        _swi (ColourPicker_CloseDialogue, _INR(0,1), 0, colourpicker_handle);
        colourpicker_handle = 0;
      }
      break;

    case URI_MReturnResult:
      url_bounce(&wimp_block->user_message);
      break;

    case Wimp_MOpenUrl:
    case URI_MProcess:
      url_recvbroadcast(&wimp_block->user_message, wimp_handleurl);
      break;

    case WIMP_MESSAGE_WINDOWINFO:
      {
        struct session_struct *session = sessions;

        while (session && session->window_handle!=wimp_block->user_message.contents.window_info.window_handle)
        {
          session=session->next;
        }

        if (session->window_handle==wimp_block->user_message.contents.window_info.window_handle)
        {
          int counter=0;

          wimp_block->user_message.your_ref    =my_ref;

          strcpy(wimp_block->user_message.contents.window_info.icon_string, "nettle");

          if (session->icon_name==NULL || strlen(session->icon_name)==0)
          {
            /* Copy session label */
            while (counter<220 && session->label[counter]!='\0')
            {
              wimp_block->user_message.contents.window_info.title[counter]=session->label[counter];
              counter++;
            }

            wimp_block->user_message.contents.window_info.title[counter]='\0';
            wimp_block->user_message.length = (40+counter) & ~3;
          }
          else
          {
            /* Copy icon name */
            strcpy(wimp_block->user_message.contents.window_info.title, session->icon_name);
            wimp_block->user_message.length = (40+strlen(session->icon_name)) & ~3;
          }

          _swi(Wimp_SendMessage, _INR(0,2), 18, wimp_block, sender_ref);
        }
      }
      break;
  }
}

/* Wimp message bounce/ack */

void wimp_message_ack (union wimp_poll_block *wimp_block)
{
  switch (wimp_block->user_message.message_code)
  {
    case WIMP_MESSAGE_DATASAVE:
    case WIMP_MESSAGE_DATALOAD:
    case WIMP_MESSAGE_DATASAVEACK:
    case WIMP_MESSAGE_DATALOADACK:
      /* receiver not there or not responding? */
      msg_ref = 0;
      break;

    case WIMP_MESSAGE_CLIP_DATAREQUEST:
      /* nobody owns the clipboard */
      if (msg_ref && wimp_block->user_message.your_ref == msg_ref)
        selection_paste (paste_session);
      msg_ref = 0;
      break;

    case Wimp_MOpenUrl:
      url_bounce(&wimp_block->user_message);
      break;
  }
}


void mode_change(void)
{
  struct session_struct *session = sessions;
  struct wimp_gadgetsizes sizes;

  old_eig=eig;

  eig.x = _swi (OS_ReadModeVariable, _INR (0,1) | _RETURN (2), -1, 4);
  eig.y = _swi (OS_ReadModeVariable, _INR (0,1) | _RETURN (2), -1, 5);

  screensize.x = _swi (OS_ReadModeVariable, _INR (0,1) | _RETURN (2), -1, 11);
  screensize.y = _swi (OS_ReadModeVariable, _INR (0,1) | _RETURN (2), -1, 12);

  screensize.x++;
  screensize.y++;

  screensize.x <<= eig.x;
  screensize.y <<= eig.y;

  /* Work out the height of toolbars - 40 is a nominal default */
  toolheight = 40;

  sizes.window_handle = 0;
  if (wimp_version>400 && _swix(Wimp_Extend, _INR(0,1), 11 /* Read furniture sizes */, &sizes)==NULL)
  {
    /* Great we're running on a WIMP that can tell us toolsizes cleanly (RISC OS 4) */
    toolheight = sizes.bottom_border;
  }
  else
  {
    /* Fall back to reading the tool heights */
    int spr_area;
    if (_swix(Wimp_ReadSysInfo, _IN(0)|_OUT(0), 9 /* Read toolsprites area */, &spr_area)==NULL)
    {
      /* We have the tools sprite area */
      char sprite[16];
      char *suffix;
      if (_swix(Wimp_ReadSysInfo, _IN(0)|_OUT(0), 2 /* Read sprite suffix */, &suffix)==NULL)
      {
        /* We know what the suffix is on sprites */
        int mode,height;

        sprintf(sprite,"sicon%s",suffix);
        if (_swix(OS_SpriteOp,_INR(0,2)|_OUT(4)|_OUT(6),
                               256+40 /* Read sprite info */,
                               spr_area,
                               sprite,
                               &height,
                               &mode))
        {
           /* Didn't find a tool specific to this mode, so try generic */
           if (_swix(OS_SpriteOp,_INR(0,2)|_OUT(4)|_OUT(6),
                               256+40 /* Read sprite info */,
                               spr_area,
                               "sicon",
                               &height,
                               &mode))
           {
             height = -1;
           }
        }

        if (height > 0)
        {
          /* The sprite was found and we have its size */
          /* And we can work out the height of the toolbar! */
          toolheight = height<<eig.y;
        }
      }
    }
  }

  old_charw=redraw.r_charw;
  old_charh=redraw.r_charh;

  /* Always recache zapredraw bitmaps */
  recache_bitmaps();

  /* Set all of the terminals to be resized to fit the new font size */
  while (session)
  {
    session->window_needs_resized=true;
    session = session->next;
  }
}


void set_caret_position(int window_handle, int icon_handle, int flags, int string_index)
{
  struct session_struct *session = sessions;

  while (session)
  {
    if (window_handle == session->window_handle ||
        window_handle == session->pane_handle)
    {
      cursor_session=session;
      break;
    }
    session = session->next;
  }

  _swi(Wimp_SetCaretPosition, _INR(0,5), window_handle, icon_handle, -1, -1, flags, string_index);
}

void check_set_caret_position (int window_handle, int icon_handle)
{
  /* Update the caret position if it's in the given icon. */
  struct wimp_getcaretposition_block caret_pos;

  _swi(Wimp_GetCaretPosition, _IN(1), &caret_pos);

  if (caret_pos.window_handle == window_handle &&
      caret_pos.icon_handle == icon_handle)
  {
    set_caret_position (window_handle, icon_handle, -1,
			get_icon_data_length (window_handle, icon_handle));
  }
}



static void create_iconbar_menu(void)
{
  struct wimp_getpointerinfo_block block;

  char *menu_title   = get_menu_item("AppName");
  char *menu_info    = get_menu_item("Info");
  char *menu_choices = get_menu_item("Choices");
  char *menu_connect = get_menu_item("Connect");
  char *menu_session;
  char *menu_hotlist;
  char *menu_scripts;
  char *menu_quit    = get_last_menu_item("Quit");

  const int *hotlist_menu=create_hotlist_menu();
  const int *termlist_menu=create_termlist_menu();
  const int *scriptlist_menu=create_scriptlist_menu();

  /* Just so it points somewhere (so the arrow doesn't disppear) */
  if (hotlist_menu==NULL)
    hotlist_menu=menu_data2;

  if (termlist_menu==NULL)
    termlist_menu=menu_data2;

  if (scriptlist_menu==NULL)
    scriptlist_menu=menu_data2;

  if (sessions)
    menu_session = get_menu_item("Session]");
  else
    menu_session = get_grey_menu_item("Session]");

  if (hotlist_num_items())
    menu_hotlist = get_menu_item("Hotlist]");
  else
    menu_hotlist = get_grey_menu_item("Hotlist]");

  if (script_exist_items())
    menu_scripts = get_menu_item("Scripts]");
  else
    menu_scripts = get_grey_menu_item("Scripts]");

  if (create_menu(menu_data, indirected+768, menu_title,
                  menu_info, win_info,
                  menu_choices,
                  menu_connect,
                  menu_hotlist, hotlist_menu,
                  menu_scripts, scriptlist_menu,
                  menu_session, termlist_menu,
                  menu_quit))
  {
    _swi(Wimp_GetPointerInfo, _IN(1), &block);

    _swi(Wimp_CreateMenu, _INR(1,3), menu_data,
                                     block.pos.x-64, 96+(24*2) + (7*44) );
  }

  free(menu_title);
  free(menu_info);
  free(menu_choices);
  free(menu_connect);
  free(menu_session);
  free(menu_scripts);
  free(menu_hotlist);
  free(menu_quit);

  menu_open=MENU_ICONBAR;
}



static void create_terminal_menu(void)
{
  struct wimp_getpointerinfo_block block;

  char *menu_title          =get_menu_item("Terminal");
  char *menu_selection;
  char *menu_resize         =get_menu_item("Resize");
  char *menu_paste_clipboard=get_menu_item("PasteClip");
  char *menu_options        =get_menu_item("Options");
  char *menu_spool          =get_menu_item("Spool>");
  char *menu_reconnect      =get_last_menu_item("Reconnect");

  char *menu_selection_title=get_menu_item("Selection");
  char *menu_save           =get_menu_item("Save");
  char *menu_launch_url     =get_menu_item("LaunchURL");
  char *menu_send           =get_menu_item("Send");
  char *menu_copy_clipboard =get_menu_item("CopyClip");
  char *menu_clear          =get_last_menu_item("Clear");

  char *menu_spool_title    =get_menu_item("Spool");
  char *menu_open_right;
  char *menu_close;

  if (terminal_menu_session->spool_file_name==NULL)
  {
    menu_open_right=get_menu_item("Open>");
    menu_close     =get_grey_last_menu_item("Close");
  }
  else
  {
    menu_open_right=get_grey_menu_item("Open>");
    menu_close     =get_last_menu_item("Close");
  }

  set_termsize_icons(win_resize, icon_resize_width, icon_resize_height, icon_resize_scroll,\
          terminal_menu_session->terminal_size, terminal_menu_session->scrollback);

  if (!selection_session || (selection_start==selection_end))
  {
    menu_selection=get_grey_menu_item("Selection>");
  }
  else
  {
    menu_selection=get_menu_item("Selection>");
  }

  if (create_menu(menu_data, indirected,
                  menu_title,
                  menu_selection, menu_data2,
                  menu_resize, win_resize,
                  menu_paste_clipboard,
                  menu_options,
                  menu_spool, menu_data3,
                  menu_reconnect))
  {
    if (create_menu(menu_data2, indirected+256,
                    menu_selection_title,
                    menu_save, win_save,
                    menu_launch_url,
                    menu_send,
                    menu_copy_clipboard,
                    menu_clear))
    {
      if (create_menu(menu_data3, indirected+512,
                      menu_spool_title,
                      menu_open_right, win_spoolsave,
                      menu_close))
      {
        _swi(Wimp_GetPointerInfo, _IN(1), &block);

        _swi(Wimp_CreateMenu, _INR(1,3), menu_data, block.pos.x-64, block.pos.y);
      }
    }
  }

  free(menu_title);
  free(menu_selection);
  free(menu_resize);
  free(menu_paste_clipboard);
  free(menu_options);
  free(menu_spool);
  free(menu_reconnect);

  free(menu_selection_title);
  free(menu_save);
  free(menu_launch_url);
  free(menu_send);
  free(menu_copy_clipboard);
  free(menu_clear);

  free(menu_spool_title);
  free(menu_open_right);
  free(menu_close);

  menu_open=MENU_TERMINAL;
}



void create_connection_type_menu(int window_handle, int text_icon, int by_icon)
{
  struct wimp_getpointerinfo_block block;

  char *menu_title     =get_menu_item("Connection");
  char *menu_telnet    =get_menu_item("Telnet");
  char *menu_ssh       =get_grey_menu_item("SSH");
  char *menu_taskwindow=get_last_menu_item("Taskwindow");

  if (create_menu(menu_data,0,menu_title,menu_telnet,menu_ssh,menu_taskwindow))
  {
    _swi(Wimp_GetPointerInfo, _IN(1), &block);

    tick_menu_for_icon_text(menu_data, window_handle, text_icon);

    create_menu_by_icon (menu_data, window_handle, by_icon);
  }

  free(menu_title);
  free(menu_telnet);
  free(menu_ssh);
  free(menu_taskwindow);

  menu_open=MENU_CONNECTION;
  menu_window = window_handle;
  menu_icon = text_icon;
}



void create_terminal_type_menu(int window_handle, int text_icon, int by_icon)
{
  struct wimp_getpointerinfo_block block;

  char *menu_title     =get_menu_item("Terminal");
  char *menu_term0     =get_menu_item("Term0");
  char *menu_term1     =get_menu_item("Term1");
  char *menu_term2     =get_menu_item("Term2");
  char *menu_term3     =get_grey_menu_item("Term3");
  char *menu_term4     =get_grey_menu_item("Term4");
  char *menu_term5     =get_grey_menu_item("Term5");
  char *menu_term6     =get_grey_menu_item("Term6");
  char *menu_term7     =get_menu_item("Term7");
  char *menu_term8     =get_menu_item("Term8");
  char *menu_term9     =get_last_menu_item("Term9");

  if (create_menu(menu_data,0,menu_title,menu_term0,menu_term1,menu_term2,menu_term3,
    		  menu_term4,menu_term5,menu_term6,menu_term7,menu_term8,menu_term9))
  {
    _swi(Wimp_GetPointerInfo, _IN(1), &block);

    tick_menu_for_icon_text(menu_data, window_handle, text_icon);

    create_menu_by_icon (menu_data, window_handle, by_icon);
  }

  free(menu_title);
  free(menu_term0);
  free(menu_term1);
  free(menu_term2);
  free(menu_term3);
  free(menu_term4);
  free(menu_term5);
  free(menu_term6);
  free(menu_term7);
  free(menu_term8);
  free(menu_term9);

  menu_open=MENU_TERMINAL_TYPE;
  menu_window = window_handle;
  menu_icon = text_icon;
}



/*
 * Description:  Create a menu for the cursor type and place it beside
 *               the icon designated
 * Parameters:   window_handle = handle of the window we're called from
 *               text_icon = the icon that holds the current text for
 *                           this menu (info&menu style)
 *               by_icon = the icon beside which the menu should be
 *                         placed (the menu icon)
 * Returns:      none
 */
void create_cursor_type_menu(int window_handle, int text_icon, int by_icon)
{
  char *menu_title     =get_menu_item("CursorType");
  char *menu_cursor0   =get_menu_item("Cursor0");
  char *menu_cursor1   =get_last_menu_item("Cursor1");

  if (create_menu(menu_data,0,menu_title,menu_cursor0,menu_cursor1))
  {
    tick_menu_for_icon_text(menu_data, window_handle, text_icon);
    create_menu_by_icon (menu_data, window_handle, by_icon);
  }

  free(menu_title);
  free(menu_cursor0);
  free(menu_cursor1);

  menu_open=MENU_CURSOR_TYPE;
  menu_window = window_handle;
  menu_icon = text_icon;
}



void create_lineeditor_type_menu(int window_handle, int text_icon, int by_icon)
{
  char *menu_title = get_menu_item("LineEd");
  char *menu_line0 = get_menu_item("LineEd0");
  char *menu_line1 = get_menu_item("LineEd1");
  char *menu_line2 = get_menu_item("LineEd2");
  char *menu_line3 = get_last_menu_item("LineEd3");

  if (create_menu (menu_data, indirected, menu_title, menu_line0, menu_line1, menu_line2, menu_line3))
  {
    tick_menu_for_icon_text(menu_data, window_handle, text_icon);

    create_menu_by_icon (menu_data, window_handle, by_icon);
  }

  free (menu_title);
  free (menu_line0);
  free (menu_line1);
  free (menu_line2);
  free (menu_line3);

  menu_open=MENU_LINEED_TYPE;
  menu_window = window_handle;
  menu_icon = text_icon;
}


static void open_open_window(bool open_window_flag)
{
  char string[MESSAGE_MAX_CONNECTION];
  int connection_type;

  if (open_window_flag)
  {
    /* Only do this if first opening window, otherwise terminal type changes when */
    /* connection type is changed */
    set_icon_data(win_open, icon_open_termtype, terminal_name[default_terminal]);
  }

  read_icon_data(win_open, icon_open_contype, string, MESSAGE_MAX_CONNECTION);

  {
    char type[MESSAGE_MAX_CONNECTION];

    if (strcmp(string, lookup("SSH", type, MESSAGE_MAX_CONNECTION)) == 0)
      connection_type = NETTLE_SSH;
    else if (strcmp(string, lookup("Taskwindow", type, MESSAGE_MAX_CONNECTION)) == 0)
      connection_type = NETTLE_TASKWINDOW;
    else
      connection_type = NETTLE_TELNET;
  }

  switch (connection_type)
  {
    case NETTLE_TELNET:
       /* unshade the host icons, shade the command icons */
       set_icon_state (win_open, icon_open_host, 0, WIMP_ICON_SHADED_BIT);
       /*set_icon_state (win_open, icon_open_hostbut, 0, WIMP_ICON_SHADED_BIT);*/
       set_icon_state (win_open, icon_open_command, WIMP_ICON_SHADED_BIT, WIMP_ICON_SHADED_BIT);
       /*set_icon_state (win_open, icon_open_commandbut, WIMP_ICON_SHADED_BIT, WIMP_ICON_SHADED_BIT);*/
       break;

    case NETTLE_SSH:
       /* unshade the host and command icons */
       set_icon_state (win_open, icon_open_host, 0, WIMP_ICON_SHADED_BIT);
       /*set_icon_state (win_open, icon_open_hostbut, 0, WIMP_ICON_SHADED_BIT);*/
       set_icon_state (win_open, icon_open_command, 0, WIMP_ICON_SHADED_BIT);
       /*set_icon_state (win_open, icon_open_commandbut, 0, WIMP_ICON_SHADED_BIT);*/
       break;

    case NETTLE_TASKWINDOW:
      /* shade the host icons, unshade the command icons */
      set_icon_state (win_open, icon_open_host, WIMP_ICON_SHADED_BIT, WIMP_ICON_SHADED_BIT);
      /*set_icon_state (win_open, icon_open_hostbut, WIMP_ICON_SHADED_BIT, WIMP_ICON_SHADED_BIT);*/
      set_icon_state (win_open, icon_open_command, 0, WIMP_ICON_SHADED_BIT);
      /*set_icon_state (win_open, icon_open_commandbut, 0, WIMP_ICON_SHADED_BIT);*/
      break;
  }

  if (open_window_flag)
  {
    struct wimp_getwindowstate_block window;

    window.window_handle = win_open;
    _swi(Wimp_GetWindowState, _IN(1), &window);

    window.handle_behind = -1;  /* Open on top of stack */

    if ((window.window_flags & (1 << 16)) == 0)
    {
      /* window is not open, open it centred on the pointer taking care to
       * not obscure the icon bar */
      struct wimp_getpointerinfo_block pointer;
      int width = window.max.x - window.min.x;
      int height = window.max.y - window.min.y;

      _swi(Wimp_GetPointerInfo, _IN(1), &pointer);

      window.min.x = pointer.pos.x - width / 2;
      window.min.y = pointer.pos.y - height / 2;

      /* '134 + 2' is a cheap approximation of the height of the icon bar */
      if (window.min.y < 134 + 2)
        window.min.y = 134 + 2;

      window.max.x = window.min.x + width;
      window.max.y = window.min.y + height;
    }
    _swi(Wimp_OpenWindow, _IN(1), &window);
  }

  switch (connection_type)
  {
    case NETTLE_TELNET:
    case NETTLE_SSH:
      set_caret_position(win_open, icon_open_host, -1,
                         get_icon_data_length(win_open, icon_open_host));
      break;

    case NETTLE_TASKWINDOW:
      set_caret_position(win_open, icon_open_command, -1,
                         get_icon_data_length(win_open, icon_open_command));
      break;
  }
}

static void open_options_window(bool open_window_flag)
{
  /* set icon contents */
  {
    char string[32];
    char string_lineedit[32];

    sprintf(string_lineedit,"LineEd%d",options_session->line_editor_type);
    set_icon_data(win_termterm, icon_termterm_lineedittype, lookup(string_lineedit, string, sizeof(string)));
  }

  if (options_session->local_echo)
  {
    set_icon_state(win_termterm, icon_termterm_localecho, WIMP_ICON_SELECTED_BIT, WIMP_ICON_SELECTED_BIT);
  }
  else
  {
    set_icon_state(win_termterm, icon_termterm_localecho, 0, WIMP_ICON_SELECTED_BIT);
  }


  if (open_window_flag)
  {
    struct wimp_getwindowstate_block window;
    struct wimp_getpointerinfo_block pointer;
    int width;
    int height;

    window.window_handle = win_termterm;
    _swi(Wimp_GetWindowState, _IN(1), &window);

    window.handle_behind = -1;  /* Open on top of stack */

    _swi(Wimp_GetPointerInfo, _IN(1), &pointer);

    width = window.max.x - window.min.x;
    height = window.max.y - window.min.y;

    window.min.x = pointer.pos.x - width / 2;
    window.min.y = pointer.pos.y - height / 2;

    window.max.x = window.min.x + width;
    window.max.y = window.min.y + height;

    _swi(Wimp_OpenWindow, _IN(1), &window);
  }
}


void set_termsize_icons (int window_handle, int xicon, int yicon, int sicon,
			 struct coords size, int scrollback)
{
  char string[8];
  struct wimp_getcaretposition_block block;

  sprintf (string, "%d", size.x);
  set_icon_data (window_handle, xicon, string);

  sprintf (string, "%d", size.y);
  set_icon_data (window_handle, yicon, string);

  sprintf (string, "%d", scrollback);
  set_icon_data (window_handle, sicon, string);

  _swi(Wimp_GetCaretPosition, _IN(1), &block);

  if (block.window_handle == window_handle &&
      (block.icon_handle == xicon ||
       block.icon_handle == yicon ||
       block.icon_handle == sicon))
  {
    set_caret_position (window_handle, block.icon_handle, -1,
			get_icon_data_length (window_handle, block.icon_handle));
  }
}


bool read_termsize_icons (int window_handle, int xicon, int yicon, int sicon,
			  struct coords *size, int *scrollback)
{
  char string[8];
  struct coords tmpsize;
  int tmpscroll;

  tmpsize.x = atoi (read_icon_data (window_handle, xicon, string, sizeof (string)));
  tmpsize.y = atoi (read_icon_data (window_handle, yicon, string, sizeof (string)));
  tmpscroll = atoi (read_icon_data (window_handle, sicon, string, sizeof (string)));

  if (tmpsize.x < MIN_TERMINAL_WIDTH || tmpsize.x > MAX_TERMINAL_WIDTH ||
      tmpsize.y < MIN_TERMINAL_HEIGHT || tmpsize.y > MAX_TERMINAL_HEIGHT ||
      tmpscroll < 0 || tmpscroll > MAX_SCROLLBACK_SIZE)
  {
    generror ("BadTermSize", true);
    return false;
  }

  *size = tmpsize;
  *scrollback = tmpscroll;
  return true;
}

int read_lineeditor_type (int window_handle, int icon_handle)
{
  char string[32]; /* overkill */
  char token[] = "LineEd0";
  int loop;

  read_icon_data (window_handle, icon_handle, string, sizeof (string));
  for (loop = 0; loop < LINEEDIT_NUM_TYPES; loop++)
  {
    token[6] = '0' + loop;
    if (! strcmp (string, lookup_static (token)))
      return loop;
  }
  return LINEEDIT_NONE;
}

enum nettle_terminal read_terminal_type (int window_handle, int icon_handle)
{
  char string[32];
  int loop = 0;

  read_icon_data (window_handle, icon_handle, string, sizeof(string));

  while (loop < NO_OF_TERMINAL_TYPES)
  {
    if (! strcmp(string, terminal_name[loop]))
      return (enum nettle_terminal) loop;

    loop++;
  }

  return default_terminal;
}



void clipboard_claim (void)
{
  if (selection_session && selection_start != selection_end)
  {
    union wimp_poll_block block;
    we_own_clipboard = true;
    block.user_message.contents.clip_claimentity.flags = 4;
    block.user_message.length       = 24;
    block.user_message.your_ref     = 0;
    block.user_message.message_code = WIMP_MESSAGE_CLIP_CLAIMENTITY;
    _swi(Wimp_SendMessage, _INR(0,3), 18, &block, 0, 0);
  }
}


void clipboard_datarequest (struct session_struct *session)
{
  user_message block;

  paste_session = session;
  block.length = 48;
  block.your_ref = 0;
  block.message_code = WIMP_MESSAGE_CLIP_DATAREQUEST;
  block.contents.clip_datarequest.window_handle = session->window_handle;
  block.contents.clip_datarequest.internal_handle = -1;
  block.contents.clip_datarequest.pos.x = 0;
  block.contents.clip_datarequest.pos.y = 0;
  block.contents.clip_datarequest.opcode = 4;
  block.contents.clip_datarequest.types[0] = 0xFFF;
  block.contents.clip_datarequest.types[1] = -1;

  _swi (Wimp_SendMessage, _INR(0,3), 18, &block, 0, 0);
}


static char *load_file (const char *name, int *size)
{
  int filesize;
  int objtype;
  char *data;
  _kernel_oserror *err;

  err = _swix (OS_File, _INR(0,1) | _OUT(0) | _OUT(4), 17, name, &objtype, &filesize);
  if (err)
  {
    generror (err->errmess, false);
    return NULL;
  }

  if (objtype!=1 && objtype!=3)
  {
    err=_swix(OS_File,_INR(0,2),19, name, objtype);
    generror (err->errmess, false);
    return NULL;
  }

  data = malloc (filesize + 1);
  if (!data)
  {
    generror ("CantAlloc", true);
    return NULL;
  }

  err = _swix (OS_File, _INR(0,3), 16, name, data, 0);
  if (err)
  {
    free (data);
    generror (err->errmess, false);
    return NULL;
  }

  if (size)
    *size = filesize;

  data[filesize] = 0;
  return data;
}

/*
 * paste_in_data
 *
 * On entry:
 *          window_handle = window handle for data to be pasted into
 *          data          = data to be pasted
 *          size          = size of data to be pasted
 *
 * Description:
 *             This pastes the data specified into the session and handles it depending
 *             whether it was a drag into the terminal or into the lineeditor
 */

static void paste_in_data (int window_handle, char *data, int size)
{
  struct session_struct *session = get_session_from_winhan(window_handle);
  if (session == NULL)
    return;

  if (session->pane_handle==window_handle && line_editor_active(session))
  {
    struct wimp_getcaretposition_block block;
    char line_editor_buffer[MAX_LINEEDITOR_LENGTH];
    int position_in_lineeditor;
    int position_of_return=0;
    int position_in_data=0;

    /* First we find out if the caret is in the pane */
    _swi(Wimp_GetCaretPosition, _IN(1), &block);

    printf("block.window_handle=%x, session->pane_handle=%x\n", block.window_handle, session->pane_handle);
    if (block.window_handle==session->pane_handle)
    {
      /* If so, we want the pasted data to be inserted at the relevant position */
      position_in_lineeditor=block.index;
    }
    else
    {
      /* Otherwise, we just want it added on the end */
      position_in_lineeditor=strlen(session->line_editor_buffer);
    }

    /* So, let's iterate through the data file */
    while (position_in_data<size)
    {
      printf("position_in_data=%d, size=%d, position_in_lineeditor=%d\n", position_in_data, size, position_in_lineeditor);
      printf("position_of_return before loop=%d\n", position_of_return);

      /* Find the next return character, or til we reach the end of the file */
      while (position_of_return<size && data[position_of_return]!=10)
      {
        position_of_return++;
      }

      printf("position_of_return after loop=%d\n", position_of_return);

      /* If the return+the length of the existing lineeditor is less than the max length then */
      if (position_of_return+strlen(session->line_editor_buffer)<MAX_LINEEDITOR_LENGTH)
      {
        int i;

        printf("copying 0 to %d in session->line_editor_buffer to 0 to %d in line_editor_buffer\n", position_in_lineeditor, position_in_lineeditor);
        /* Copy the chunk before the caret into line_editor_buffer */
        for (i=0; i<position_in_lineeditor; i++)
        {
          line_editor_buffer[i]=session->line_editor_buffer[i];
        }

        printf("copying %d to %d in data to %d to %d in line_editor_buffer\n", position_in_data, position_of_return, position_in_lineeditor, position_in_lineeditor+position_of_return-position_in_data);
        /* Copy the chunk from the data file to the line_editor_buffer */
        for (i=0; i<position_of_return-position_in_data; i++)
        {
          line_editor_buffer[i+position_in_lineeditor]=data[position_in_data+i];
        }

        printf("copying %d to %d in session->line_editor_buffer to %d to %d in line_editor_buffer\n", position_in_lineeditor, strlen(session->line_editor_buffer), position_in_lineeditor+position_of_return-position_in_data, strlen(session->line_editor_buffer)+position_of_return-position_in_data);
        /* And finally copy the chunk from after the caret to the line_editor_buffer */
        for (i=position_in_lineeditor; i<strlen(session->line_editor_buffer); i++)
        {
          line_editor_buffer[i+position_of_return-position_in_data]=session->line_editor_buffer[i];
        }

        printf("setting line_editor_buffer[%d] to '\\0'\n", position_of_return+strlen(session->line_editor_buffer));
        line_editor_buffer[position_of_return+strlen(session->line_editor_buffer)-position_in_data]='\0';
      }

      /* Plonk it into the lineeditor */
      strcpy(session->line_editor_buffer, line_editor_buffer);

      /* If the character we found was actually a return as opposed to the end of the file */
      /* then call the line editor processor */
      if (position_of_return<size && data[position_of_return]==10)
      {
        process_line_editor(session);
      }

      /* Set the position in data for next time to the position after the return */
      /* and set the lineeditor length appropriately */
      position_of_return++;
      position_in_data=position_of_return;
      position_in_lineeditor=strlen(session->line_editor_buffer);
      printf("position_in_data is %d, position_in_lineeditor is %d\n", position_in_data, position_in_lineeditor);
    }

    if (block.window_handle==session->pane_handle)
    {
      /* Update the writable icon position */
      _swi(Wimp_SetCaretPosition, _INR(0,5), block.window_handle, block.icon_handle, -1, -1, -1, position_in_lineeditor);
    }

    set_icon_state(session->pane_handle, 0, 0, 0);
  }
  else
  {
    process_data_transmission (session, data, size);
  }
}


/**
 * 'type' in an ant url file
 *
 * @return true if url was sent successfully
 */
static bool parse_ant_url (union wimp_poll_block *block)
{
  struct session_struct *session;
  int window_handle;
  char *url;
  int size;

  window_handle = block->user_message.contents.data_load.window_handle;

  session = get_session_from_winhan(window_handle);
  if (session == NULL)
    return false;

  url = load_file (block->user_message.contents.data_load.file_name, &size);

  if (url)
  {
    paste_in_data (window_handle, url, size);
    free (url);
    return true;
  }
  return false;
}



/**
 * 'type' in an acorn url file
 *
 * @return true if url was sent successfully
 */
static bool parse_acorn_uri (union wimp_poll_block *block)
{
  int size;
  struct session_struct *session;
  int window_handle;
  char *uri;

  window_handle = block->user_message.contents.data_load.window_handle;
  session = get_session_from_winhan(window_handle);
  if (session == NULL)
    return false;

  uri = load_file (block->user_message.contents.data_load.file_name, &size);

  if (uri)
  {
    int offset = -1;
    int lf = 3;

    /* skip the first three LFs */
    while (lf && ++offset < size)
      if (uri[offset] == '\n')
        lf--;
    if (!lf)
    {
      /* then past any whitespace */
      while (++offset < size && isspace ((int) uri[offset]))
        ;
      if (offset < size)
      {
        /* now find end of url */
        char *eol = strchr (uri + offset, '\n');

        /* finally (!) send it */
        paste_in_data (window_handle, uri + offset, eol - uri - offset);
        free(uri);
        return true;
      }
    }
    free (uri);
  }
  return false;
}




/**
 * 'type' in a text file
 *
 * @return true if text was sent successfully
 */
static bool paste_text_file (union wimp_poll_block *block)
{
  int size;
  char *text;
  struct session_struct *session;
  int window_handle;

  window_handle = block->user_message.contents.data_load.window_handle;
  session = get_session_from_winhan(window_handle);

  if (!session)
    return false;

  text = load_file (block->user_message.contents.data_load.file_name, &size);

  if (text)
  {
    if (session->pane_handle==window_handle && line_editor_active(session))
    {
      /* Handling for drags to the lineeditor pane */
      paste_in_data(window_handle, text, size);
    }
    else
    {
      /* Handling for drags to the terminal window */
      int loop;

      for (loop=0; loop<size; loop++)
      {
        switch (text[loop])
        {
          case 10:
            text[loop]=13;
            break;
          default:
            if (text[loop]<32)
              text[loop]=' ';

            break;
        }
      }

      process_data_transmission (session, text, size);
    }

    free (text);

    return true;
  }

  return false;
}
