/* Simple BBC News ticker client
 * (c) Darren Salt
 * GPL applies
 * $Id: wimp.c,v 1.28 2004/09/16 23:17:49 ds Exp $
 */

/* System includes */

#include <assert.h>
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <swis.h>

#include <sys/errno.h>
#include <unixlib.h>
#include <riscos.h>

/* Program includes */

#include "globals.h"

#include "configure.h"
#include "fetch.h"
#include "save.h"
#include "wimp.h"
#include "wimpmenu.h"
#include "wimpticker.h"


union wimp_poll_block poll_block;

/* Iconbar icon: handle & sprite */
static int iconbar_icon_handle = -1;
static char iconbar_sprite[] = "\xD7ticker";
static struct wimp_createicon_block iconbar_icon = { -1, {0, 0}, {68, 68},
icn_BGCOL (1) | icn_FGCOL (7) | icn_TYPE (3) | icn_SPRITE_BIT |
  icn_INDIRECTED_BIT | icn_HELP_BIT
};

/* Title bar icon: sprite */
static char *titlebar_sprite;

/* Sprites area */
int *sprites;
int spr22[3], spr11[3];

/* WIMP message chain validation */
static int msg_ref = 0;

/* From where was the current drag started? */
/*static*/ int drag_window = 0;

/* Time at which we switch to the active state */
static long state_switch_time = 0;

/* Forward declarations */

static int mouse_click (void);
static void end_drag (void);
static void key_pressed (void);
static int wimp_message (void);
static void bounce_message (void);


/* Delete all of our windows */

static void
delete_windows (void)
{
  int *w = (int *) &wind;
  int i;
  for (i = 0; i < sizeof (wind) / sizeof (int); i++)
    if (w[i])
      _swix (Wimp_DeleteWindow, _IN (1), &w[i]);
}


/* Create or remove our iconbar icon */

void
wimp_create_iconbar_icon (void)
{
  if (opt.no_ibar_icon)
  {
    if (iconbar_icon_handle != -1)
    {
      wimp_delete_icon (-2, iconbar_icon_handle);
      if (ticker.state == is_CLOSED)
      {
	pinboard_remove_ticker ();
	open_ticker_window_minimised (NULL, -1);
      }
      iconbar_icon_handle = -1;
    }
  }
  else if (iconbar_icon_handle == -1)
    erx (wimp_create_icon (&iconbar_icon, -1, &iconbar_icon_handle));
}


/* Init code for desktop use.
 * Declares this program as a WIMP task; creates the iconbar icon; loads
 * templates and sprites; fills in menu submenu pointers.
 */

void
wimp_init (void)
{
  static const int messages[] =
    { WIMP_MESSAGE_DATASAVE, WIMP_MESSAGE_DATASAVEACK,
    WIMP_MESSAGE_DATALOAD, WIMP_MESSAGE_DATALOADACK,
//    WIMP_MESSAGE_HELPREQUEST,
    WIMP_MESSAGE_MENUWARNING, WIMP_MESSAGE_MODECHANGE,
    WIMP_MESSAGE_TASKINITIALISE, WIMP_MESSAGE_MENUSDELETED,
    WIMP_MESSAGE_WINDOWICONISE, WIMP_MESSAGE_LAUNCH_URL,
    WIMP_MESSAGE_URI_MRETURNRESULT, 0
  };
  static const char Sprites22[] = "<Ticker$Dir>.Sprites";
  static const char Sprites11[] = "<Ticker$Dir>.Sprites11";

  {
    int type, length22, length11;
    erx (_swix (OS_File, _INR (0, 1) | _OUT (0) | _OUT (4), 17, Sprites22,
		&type, &length22));
    if (type != 1)
      erx (_swix (OS_File, 19, _INR (0, 2), Sprites22, type));
    erx (_swix (OS_File, _INR (0, 1) | _OUT (0) | _OUT (4), 17, Sprites11,
		&type, &length11));
    if (type != 1)
      erx (_swix (OS_File, 19, _INR (0, 2), Sprites11, type));
    sprites = malloc (length22 + length11 + 4);
    if (!sprites)
    {
      report_error ("Out of memory");
      exit (2);
    }
    sprites[0] = (length22 > length11 ? length22 : length11) + 16;
    erx (_swix (OS_File, _INR (0, 3), 16, Sprites22, sprites + 1, 0));
    erx (_swix
	 (OS_File, _INR (0, 3), 16, Sprites11, sprites + 1 + length22 / 4,
	  0));
    spr22[0] = sprites[1];
    spr22[1] = sprites[2];
    spr22[2] = sprites[3];
    spr11[0] = sprites[length22 / 4 + 1];
    spr11[1] = sprites[length22 / 4 + 2] + length22;
    spr11[2] = sprites[length22 / 4 + 3] + length22;
  }

  erx (_swix
       (Wimp_Initialise, _INR (0, 3) | _OUTR (0, 1), 380, 0x4B534154,
	taskname, messages, &wimp_version, &task_handle));

  iconbar_icon.contents.is.sprite = iconbar_sprite;
  iconbar_icon.contents.is.sprite_area = (void *) 1;
  iconbar_icon.contents.is.sprite_len = 8;
  wimp_create_iconbar_icon ();

  atexit (delete_windows);

  erx (_swix (Wimp_OpenTemplate, _IN (1), "<Ticker$Dir>.Templates"));
  {
    /* *Must* be kept in step with struct window_handles */
    static const char names[][16] =
      { "Info", "Config0Main", "Config1Conn", "Config2Tick", "Config3Init",
      "Config4Show", "Config5BBC",
      "Servers", "Ticker", "TickerTbar", "TickerDesc",
      "SaveLink", "SaveText", "ProxyAuth"
    };
    int *w = (int *) &wind;
    int i;
    assert (sizeof (wind) / sizeof (int) ==
	    sizeof (names) / sizeof (names[0]));
    for (i = 0; i < sizeof (wind) / sizeof (int); i++)
      if (names[i][0])
	w[i] = load_template (names[i]);
  }
  _swix (Wimp_CloseTemplate, 0);

  ticker_menu_no_ibar.items[6].submenu = iconbar_menu.items[0].submenu =
    wind.info;
  iconbar_menu.items[1].submenu = wind.ticker;
  ticker_menu_no_ibar.items[0].submenu = ticker_menu.items[0].submenu =
    (int) &ticker_control_menu;
  ticker_menu_no_ibar.items[1].submenu = ticker_menu.items[1].submenu =
    wind.save_link;
  ticker_menu_no_ibar.items[2].submenu = ticker_menu.items[2].submenu =
    wind.save_text;
  ticker_control_menu.items[1].submenu = wind.ticker;

  if (_swix (Wimp_SpriteOp, _IN (0) | _IN (2), 24, "grightc"))
    wimp_delete_icon (wind.config_pane[2], 12);

  {
    struct wimp_getwindowinfo_block block;
    wimp_get_window_info (&block, wind.ticker_tbar);
    titlebar_sprite = block.data.title.is.sprite;
  }

  tick_icon (wind.save_link, 4, 1);
  tick_icon (wind.save_text, 4, 1);
  if (wimp_version < 400)
    shade_icon (wind.config_pane[4], 12, 1);

  sprintf (get_icon_text (wind.info, 7), "%s (%s)", version, date);
}


/* Shutdown code for desktop use. */

void
wimp_quit (void)
{
  int i, mod;
  static const _kernel_oserror err = { 0, "You have modified the tickers list but you have not saved it. Do you wish to do so?" };

  switch (mod = servers_modified ())
  {
  case 1: /* user mods */
    i = wimp_report_error_ext (&err, 0x990, "Ticker", 0, 0, "Yes,No");
    if (i == 3)
      save_servers ();
    break;
  case 3: /* user mods plus automatic mods */
    i = wimp_report_error_ext (&err, 0x990, "Ticker", 0, 0, "Yes,No");
    if (i != 3)
    { /* Not saving user mods: merge in whatever's saved. */
      read_servers (1);
      /* Any remaining user mods aren't in the saved file; discard them. */
      for (i = num_servers - 1; i; --i)
        if (servers[i].modified == MODIFIED_BY_USER)
          delete_server (i);
    }
    save_servers ();
    break;
  case 2: /* automatic mods */
    save_servers ();
    break;
  }

  free_indirected_icons ();
  free (sprites);
  free (servers_menu);
  free (servers_submenu);
  sprites = 0;
  _swix (Wimp_CloseDown, _INR (0, 1), task_handle, 0x4B534154);
}


/* Sets the content of the status icon (in the ticker window). */

void
set_status_text (const char text[], ...)
{
  struct wimp_seticonstate_block block;
  char *status_text;

  if (!task_handle)
    return;

  status_text = get_icon_text (wind.ticker, 0);
  assert (status_text);

  if (text)
  {
    va_list list;
    const char *c = text;
    int ptr = 0;

    va_start (list, text);
    do
    {
      strncpy (status_text + ptr, c, 255 - ptr);
      ptr += strlen (c);
      c = va_arg (list, const char *);
    }
    while (c && ptr < 255);
    status_text[ptr] = 0;
    va_end (list);
  }
  else
    status_text[0] = '\0';

  block.window_handle = wind.ticker;
  block.icon_handle = 0;
  block.clear = block.eor = 0;
  wimp_set_icon_state (&block);
}


/* Sets the current status (ticker unavailable, data being fetched, ticker
 * active). Reflects this in the iconbar icon.
 */

void
set_status (enum state state)
{
  struct wimp_seticonstate_block block = { -2 };

  if (!task_handle)
    return;

  block.icon_handle = iconbar_icon_handle;
  ticker_data.state = state;

  switch (state)
  {
  default:
    ticker_data.state = state_UNAVAILABLE;
  case state_UNAVAILABLE:
    titlebar_sprite[0] = iconbar_sprite[0] = 0xD7;
    break;
  case state_LOADING:
    set_status_text ("Loading...", 0);
    titlebar_sprite[0] = iconbar_sprite[0] = '?';
    break;
  case state_ACTIVE:
    set_status_text (0);
    titlebar_sprite[0] = iconbar_sprite[0] = '/';
    break;
  case state_NOT_CONNECTED:
    set_status_text ("Not connected", 0);
    {
      long now = read_monotonic_time ();
      if (ticker.done_first_fetch)
      {
	state_switch_time = now + 500;
	titlebar_sprite[0] = iconbar_sprite[0] = '?';
      }
      else
      {
	state_switch_time = now + (1U << 31);
	titlebar_sprite[0] = iconbar_sprite[0] = 0xD7;
      }
    }
    break;
  }

  _swix (Wimp_ForceRedraw, _INR (0, 2), wind.ticker_tbar, 0x4B534154, 3);
  if (block.icon_handle != -1)
    wimp_set_icon_state (&block);

  if (current_menu.handle == &ticker_menu
      || current_menu.handle == &ticker_menu_no_ibar)
    reopen_menu ();
}


static int
go_do_save (int window)
{
  char *name = get_icon_text (window, 1);
  if (strchr (name, ':') || strchr (name, '.'))
    return do_save (window, name, current_menu.story, 0);
  report_error ("To save, drag the icon to a directory display");
  return 1;
}


/******************
 * Save/drag code *
 ******************/


/* Start a drag */

void
start_drag (int window, int icon)
{
  struct coords start;
  union
  {
    struct wimp_getwindowstate_block w;
    struct wimp_geticonstate_block i;
  }
  block;
  struct wimp_dragbox_block drag;

  drag_window = window;

  wimp_get_window_state (&block.w, window);
  start.x = block.w.min.x - block.w.scroll.x;
  start.y = block.w.max.y - block.w.scroll.y;

  wimp_get_icon_state (window, icon, &block.i);

  drag.window_handle = window;
  drag.drag_type = 5;
  drag.min.x = block.i.min.x + start.x;
  drag.min.y = block.i.min.y + start.y;
  drag.max.x = block.i.max.x + start.x;
  drag.max.y = block.i.max.y + start.y;

  if (_swi (OS_Byte, _INR (0, 1) | _RETURN (2), 161, 28) & (1 << 1))
    _swi (DragASprite_Start, _INR (0, 4), 0xC5, 1,
	  get_icon_text (window, icon), &drag.min.x, 0);
  else
  {
    drag.parent_min.x = 0;
    drag.parent_min.y = 0;
    drag.parent_max = mode.max;
    _swi (Wimp_DragBox, _IN (1), &drag);
  }
}


/* End a drag */

static void
end_drag (void)
{
  if (drag_window == wind.save_text || drag_window == wind.save_link)
  {
    struct wimp_getpointerinfo_block block;
    const char *name = get_icon_text (drag_window, 1);
    const char *colon = strchr (name, ':');
    const char *dot = strrchr (name, '.');
    if (!dot)
      dot = colon ? colon : name - 1;

    wimp_get_pointer_info (&block);
    poll_block.user.contents.data_save.window_handle = block.window_handle;
    poll_block.user.contents.data_save.icon_handle = block.icon_handle;
    poll_block.user.contents.data_save.pos = block.pos;
    poll_block.user.contents.data_save.size = -1;
    poll_block.user.contents.data_save.filetype = save_type (drag_window);

    strcpy (poll_block.user.contents.data_save.file_name, dot + 1);

    poll_block.user.length =
      (48 + strlen (poll_block.user.contents.data_save.file_name)) & ~3;
    poll_block.user.msg_code = WIMP_MESSAGE_DATASAVE;

    wimp_send_message (18, &poll_block,
		       poll_block.user.contents.data_save.window_handle,
		       poll_block.user.contents.data_save.icon_handle);
    msg_ref = poll_block.user.my_ref;
  }
  // drag_window = 0;
}


/*************
 * Poll code *
 *************/


static int
dopoll (int allow_null)
{
  /* Call Wimp_Poll and process it. Return non-0 if we're quitting. */
  int code = 0;
  int swinum, mask = 0x3802;

  if (!task_handle)
    return 0;

  if (ticker.state == is_OPEN || allow_null)
    swinum = Wimp_PollIdle;
  else
  {
    mask |= 1;
    swinum = Wimp_Poll;
  }

  {
    long ret =
      ticker_data.state ==
      state_NOT_CONNECTED ? state_switch_time : get_next_update_time ();
    if (ret - next_scroll_time > 0)
      ret = next_scroll_time;
    er (_swix
	(swinum, _INR (0, 3) | _OUT (0), mask, &poll_block, ret, 0, &code));
  }

  switch (code)
  {
  case 2:			/* Open window */
    if (poll_block.open_window.window_handle == wind.ticker_tbar)
    {
      if (poll_block.open_window.handle_behind == -3)
	ticker.pinned = 1;
      else if (poll_block.open_window.handle_behind != ticker.pinned)
	ticker.pinned = 0;
      if (mode.max.x != prevmode.max.x || mode.max.y != prevmode.max.y)
      {
	reposition_ticker_window (&poll_block.open_window);
	open_ticker_window_current (&poll_block.open_window, 0, 0);
	prevmode = mode;
      }
    }
    wimp_open_window (&poll_block.open_window);
    if (ticker.pinned == 1)
      ticker.pinned = poll_block.open_window.handle_behind;
    break;
  case 3:			/* Close window; quit if no iconbar icon */
    if (poll_block.open_window.window_handle == wind.ticker
	|| poll_block.open_window.window_handle == wind.ticker_tbar)
    {
      struct wimp_getpointerinfo_block block;
      wimp_get_pointer_info (&block);
      if (block.buttons == 4 && opt.no_ibar_icon)
	return 1;
      if (block.buttons == 1)
      {
	toggle_ticker_minimise ();
	break;
      }
    }
    set_ticker_state (is_CLOSED);
    close_window (poll_block.open_window.window_handle);
    break;
  case 4:			/* Pointer leaving window */
    if (poll_block.open_window.window_handle == wind.ticker)
    {
      clear_highlight ();
      ticker.has_pointer = 0;
    }
    break;
  case 5:			/* Pointer entering window */
    if (poll_block.open_window.window_handle == wind.ticker)
      ticker.has_pointer = 1;
    break;
  case 6:			/* Mouse click */
    if (mouse_click ())
      return 1;
    break;
  case 7:			/* Drag end */
    end_drag ();
    break;
  case 8:			/* Keypress */
    key_pressed ();
    break;
  case 9:			/* Menu selection */
    if (menu_click ())
      return 1;
    break;
  case 17:			/* User message */
  case 18:
    if (wimp_message ())
      return 1;
    break;
  case 19:			/* Bounce message */
    bounce_message ();
    break;
  }

  if (ticker_data.state == state_NOT_CONNECTED
      && read_monotonic_time () - state_switch_time >= 0)
  {
    if (ticker.done_first_fetch)
    {
      set_status (state_ACTIVE);
      pinboard_remove_ticker ();
      open_ticker_window (NULL, 0, 1);
    }
    else
      set_status (state_UNAVAILABLE);
  }

  if (!ticker.pinned && ticker.state == is_OPEN
      && ticker_data.state == state_ACTIVE)
  {
    if (ticker.has_pointer)
      check_highlight ();
    if (!ticker.paused
	&&
	((current_menu.handle != &ticker_menu
	  && current_menu.handle != &ticker_menu_no_ibar)
	 || (ticker_menu.items[1].icon_flags & icn_SHADED_BIT) ==
	 icn_SHADED_BIT) && read_monotonic_time () - next_scroll_time >= 0)
    {
      /* If we've waited too long since the last ticker window update,
       * temporarily adjust the scroll step size.
       */
      long now = read_monotonic_time ();
      int step = 1;
      next_scroll_time += 2;
      if (next_scroll_time - now < 0)
      {
	step = (int) (now - next_scroll_time + 3) / 2;
	next_scroll_time = (now + 2) & ~1;
      }
      scroll_ticker (step);
    }
  }
  else if (ticker.done_first_fetch && ticker.state == is_MINIMISED
	   && opt.iconised_update && !is_save_window_open ()
	   && time_to_update ())
    go_fetch_ticker (0);

  if (force_reopen_menu)
  {
    force_reopen_menu = 0;
    if (current_menu.handle)
    {
      update_menu (current_menu.handle);
      wimp_create_menu (current_menu.handle, current_menu.pos.x,
			current_menu.pos.y);
    }
  }

  return 0;
}


/* Polling, without null polls */

int
poll (void)
{
  return dopoll (0);
}


/* Polling, with null polls */

int
nullpoll (void)
{
  return dopoll (1);
}


/************************
 * Error reporting code *
 ************************/


void
status_errno (const char prefix[])
{
  if (task_handle && ticker_data.state != state_ACTIVE)
    set_status_text (prefix, strerror (errno), 0);
  else
    report_errno ();
}


void
status_error (const char text[])
{
  if (task_handle && ticker_data.state != state_ACTIVE)
    set_status_text (text, 0);
  else
    report_error (text);
}


void
status_oserror (const char prefix[], const _kernel_oserror * block)
{
  if (task_handle && ticker_data.state != state_ACTIVE)
    set_status_text (prefix, block->errmess, 0);
  else
    report_oserror (block);
}


/******************
 * Event handlers *
 ******************/


void
close_window (int handle)
{
  wimp_close_window (&handle);
  if (handle == wind.ticker_tbar)
  {
    set_ticker_state (is_CLOSED);
    ticker.has_pointer = 0;
    wimp_close_window (&wind.ticker_tbar);
  }
  else if (handle == wind.config)
    close_config_window ();
}


static void
click_iconbar (void)
{
  switch (poll_block.mouse_click.buttons)
  {
  case 4:			/* open ticker */
    reopen_ticker_window (0, 2);
    break;
  case 2:			/* open iconbar menu */
    open_menu (&iconbar_menu, poll_block.mouse_click.pos.x - 64,
	       ICONBAR_MENU_Y);
    break;
  case 1:			/* open options */
    open_config_window (1);
    break;
  }
}


static void
click_save_window (int window)
{
  if (poll_block.mouse_click.buttons == 2)
    return;
  switch (poll_block.mouse_click.icon_handle)
  {
  case 0:			/* file icon */
    start_drag (window, 0);
    break;
  case 3:			/* save icon */
    if (go_do_save (window))
      break;
    /* else fall through */
  case 2:			/* cancel icon */
    if (poll_block.mouse_click.buttons != 1)
      close_menu ();
    break;

  case 4:			/* (text, link only) link type */
  case 5:
    if (window == wind.save_text)
    {
      tick_icon (window, poll_block.mouse_click.icon_handle, 1);
      set_icon_text (window, 0,
		     poll_block.mouse_click.icon_handle == 4
		     ? "file_faf" : "file_fff");
    }
    else if (window == wind.save_link)
    {
      tick_icon (window, poll_block.mouse_click.icon_handle, 1);
      set_icon_text (window, 0,
		     poll_block.mouse_click.icon_handle == 4
		     ? "file_f91" : "file_b28");
    }
    break;
  }
}


static void
click_info_window (void)
{
  /* If there's more than one clickable icon in this window, it's a bug */
  if (poll_block.mouse_click.buttons != 2)
    launch_url
      ("http://www.youmustbejoking.demon.co.uk/progs.apps.html#ticker", 0);
}


static int
mouse_click (void)
{
  typedef void (*clickfn) (void);
  static const clickfn click_conf[] =
    { click_config_connection, click_config_ticker, click_config_startup,
    click_config_appearance, click_config_bbc
  };

  if (poll_block.mouse_click.window_handle == -2)
    click_iconbar ();
  else if (poll_block.mouse_click.window_handle == wind.ticker)
    click_ticker ();
  else if (poll_block.mouse_click.window_handle == wind.config)
    click_config_window ();
  else if (poll_block.mouse_click.window_handle == wind.servers)
    click_servers_window ();
  else if (poll_block.mouse_click.window_handle == wind.proxy_auth)
    click_proxy_auth ();
  else if (poll_block.mouse_click.window_handle == wind.save_link
	   || poll_block.mouse_click.window_handle == wind.save_text)
    click_save_window (poll_block.mouse_click.window_handle);
  else if (poll_block.mouse_click.window_handle == wind.info)
    click_info_window ();
//  else if (poll_block.mouse_click.window_handle == wind.ticker_desc)
//    wimp_close_window (&wind.ticker_desc);
  else
  {
    int i;
    for (i = NUM_CONFIG_PANES - 1; i >= 0; i--)
    {
      if (poll_block.mouse_click.window_handle != wind.config_pane[i])
	continue;
      click_conf[i] ();
      break;
    }
  }
  return 0;
}


static int
key_save (int window)
{
  switch (poll_block.key_pressed.code)
  {
  case 13:
    if (!go_do_save (window))
  case 27:
      close_menu ();
    return 1;
  }

  return 0;
}


static void
key_pressed (void)
{
  int done = 0;
  if (poll_block.key_pressed.window_handle == wind.proxy_auth)
    done = key_proxy_auth ();
  else if (poll_block.key_pressed.window_handle == wind.save_link
	   || poll_block.key_pressed.window_handle == wind.save_text)
    done = key_save (poll_block.key_pressed.window_handle);
  else if (poll_block.key_pressed.window_handle == wind.servers)
    done = key_servers ();

  if (!done)
    _swi (Wimp_ProcessKey, _IN (0), poll_block.key_pressed.code);
}


static const char *
which_label (void)
{
  return (current_menu.window == CONFIG_PANE (CONN))
	 ? get_icon_text (CONFIG_PANE (CONN), 11)
	   : (current_menu.window == wind. servers)
	   ? get_icon_text (wind.servers, 1) : fetcher.label;
}


static int
wimp_message (void)
{
  switch (poll_block.user.msg_code)
  {
  case WIMP_MESSAGE_QUIT:
    return 1;

  case WIMP_MESSAGE_DATASAVE:
    if (poll_block.user.contents.data_save.filetype == 0xB28
        || poll_block.user.contents.data_save.filetype == 0xF91)
    {
      poll_block.user.length = 256;
      poll_block.user.your_ref = poll_block.user.my_ref;
      poll_block.user.msg_code = WIMP_MESSAGE_DATASAVEACK;
      poll_block.user.contents.data_save.size = -1;
      strcpy (poll_block.user.contents.data_save.file_name, "<Wimp$Scrap>");
      wimp_reply_message (&poll_block);
      msg_ref = poll_block.user.my_ref;
    }
    break;

  case WIMP_MESSAGE_DATASAVEACK:
    if (!msg_ref || poll_block.user.your_ref != msg_ref)
      break;
    msg_ref = 0;
    if (!do_save
	(drag_window, poll_block.user.contents.data_save_ack.file_name,
	 current_menu.story,
	 (poll_block.user.contents.data_save_ack.size ==
	  -1) ? poll_block.user.sender_ref : 0))
    {
      poll_block.user.msg_code = WIMP_MESSAGE_DATALOAD;
      wimp_reply_message (&poll_block);
      msg_ref = poll_block.user.my_ref;
      close_menu ();
    }
    break;

  case WIMP_MESSAGE_DATALOAD:
    {
      int window = poll_block.user.contents.data_load.window_handle;
      switch (poll_block.user.contents.data_load.filetype)
      {
      case 0xB28:
	if (!parse_url
	    (window, poll_block.user.contents.data_load.file_name, 0))
	  goto reply_url;
	break;
      case 0xF91:
	if (!parse_url
	    (window, poll_block.user.contents.data_load.file_name, 1))
	  goto reply_url;
	break;
      }
      break;

    reply_url:
      poll_block.user.msg_code = WIMP_MESSAGE_DATALOADACK;
      wimp_reply_message (&poll_block);
      if (window != wind.servers && ticker_data.state != state_LOADING)
	go_fetch_ticker (1);
    }
    break;

  case WIMP_MESSAGE_DATALOADACK:
    if (poll_block.user.your_ref == msg_ref)
      msg_ref = 0;
    break;

  case WIMP_MESSAGE_HELPREQUEST:
    break;

  case WIMP_MESSAGE_MENUWARNING:
    {
      int m = poll_block.user.contents.menu_warning.submenu;
      if (m == wind.save_link)
	set_icon_text (wind.save_link, 1, "NewsURL");
      else if (m == wind.save_text)
	set_icon_text (wind.save_text, 1, "NewsTicker");
      else if (m == wind.ticker)
      {
	create_servers_menu (0, which_label ());
	m = (int) servers_menu;
      }
      else if (m == 1)
	m = (int) find_goto_menu (1);
      else if (m == 2)
      {
	int i = 0;
	while (poll_block.user.contents.menu_warning.selection[++i] != -1)
	  ;
	create_servers_menu (servers_menu->
				items[poll_block.user.contents.menu_warning.
				      selection[i - 1]].text.it.text,
			     which_label ());
	m = (int) servers_submenu;
      }
      _swi (Wimp_CreateSubMenu, _INR (1, 3), m,
	    poll_block.user.contents.menu_warning.pos.x,
	    poll_block.user.contents.menu_warning.pos.y);
    }
    break;

  case WIMP_MESSAGE_MODECHANGE:
    cache_mode_vars ();
    if (ticker.state != is_CLOSED)
    {
      wimp_get_window_state (&poll_block.open_window, wind.ticker_tbar);
      reposition_ticker_window (&poll_block.open_window);
      open_ticker_window_current (&poll_block.open_window, 0, 0);
    }
    break;

  case WIMP_MESSAGE_TASKINITIALISE:
    if ((poll_block.user.sender_ref ^ task_handle) & 0xFFFF)
    {
      if (!strcmp
	  (poll_block.user.contents.task_initialise.taskname, taskname))
      {
	poll_block.user.length = 20;
	poll_block.user.msg_code = 0;
	wimp_send_message (17, &poll_block, poll_block.user.sender_ref, 0);
      }
    }
    else
    {
      cache_mode_vars ();
      set_ticker_top ();
      switch (opt.initial_state)
      {
      case start_CLOSED:
	if (opt.no_ibar_icon)
      case start_MINIMISED:
	  open_ticker_window_minimised (NULL, 0);
	else if (!opt.delay_init_fetch)
      case start_OPEN:
	  go_fetch_ticker (1);
	break;
      }
    }
    break;

  case WIMP_MESSAGE_MENUSDELETED:
    mark_menu_closed ();
    break;

  case WIMP_MESSAGE_WINDOWICONISE:
    poll_block.user.length = 256;
    poll_block.user.contents.window_info.reserved = 0;
    if (poll_block.user.contents.window_iconise.window_handle ==
	wind.ticker_tbar)
      strcpy (poll_block.user.contents.window_info.title, "Ticker");
    else
      memmove (poll_block.user.contents.window_info.title,
	       poll_block.user.contents.window_iconise.title,
	       sizeof (poll_block.user.contents.window_iconise.title));
    strcpy (poll_block.user.contents.window_info.sprite, "\xD7ticker");
    wimp_reply_message (&poll_block);
    break;

  case WIMP_MESSAGE_URI_MRETURNRESULT:
    if (poll_block.user.contents.uri_mreturnresult.flags & 1)
    {
      /* URI wasn't launched; try the ANT protocol instead. */
      int length = _swi (URI_RequestURI, _INR (0, 3) | _RETURN (2), 0, 0, 0,
			 poll_block.user.contents.uri_mreturnresult.handle);
      char *url = malloc (++length);
      if (url)
      {
	_swi (URI_RequestURI, _INR (0, 3), 0, url, length,
	      poll_block.user.contents.uri_mreturnresult.handle);
	launch_url_ant (url);
	free (url);
      }
    }
    break;
  }
  return 0;
}


static void
bounce_message (void)
{
  switch (poll_block.user.msg_code)
  {
  case WIMP_MESSAGE_LAUNCH_URL:
    {
      char *var;
      int length = strchr (poll_block.user.contents.bytes,
			   ':') - poll_block.user.contents.bytes;
      if (length < 1)
	break;
      var = malloc (16 + length + strlen (poll_block.user.contents.bytes));
      if (var)
      {
	strcpy (var, "Alias$URLOpen_");
	memcpy (var + 14, poll_block.user.contents.bytes, length);
	var[14 + length] = ' ';
	strcpy (var + 15 + length, poll_block.user.contents.bytes);
	if (getenv (var))
	  er (_swix (Wimp_StartTask, _IN (0), var + 6));
	free (var);
      }
    }
  }
}
