/* Copyright 1997 Acorn Computers Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/***************************************************/
/* File   : Find.c                                 */
/*                                                 */
/* Purpose: Functions relating to the Find         */
/*          dialogue box.                          */
/*                                                 */
/* Author : A.D.Hodgkinson                         */
/*                                                 */
/* History: 17-Apr-97: Created.                    */
/*          13-Mar-98: Working code implemented.   */
/***************************************************/

#include <stdlib.h>
#include <string.h>

#include "swis.h"

#include "wimp.h"
#include "wimplib.h"
#include "event.h"

#include "toolbox.h"
#include "window.h"

#include "svcprint.h"
#include "Global.h"
#include "Utils.h"

#include "Browser.h"
#include "Fetch.h"
#include "Toolbars.h"
#include "Windows.h"

#include "Find.h"

/* Static function prototypes */

static int  find_from_start_toggled (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle);

static int  find_ok                 (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle);
static int  find_cancel             (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle);

static int  find_string_has_changed (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle);

/* Local statics */

static ObjectId       id     = NULL_ObjectId;

static int            fs_so  = 0;

static browser_data * over_b = NULL;
static HStream      * last_t = NULL;
static int            last_o = 0;

/*************************************************/
/* find_to_be_shown()                            */
/*                                               */
/* Called when the EFindToBeShown event is       */
/* generated, typically when the Find window is  */
/* about to be shown. Handles any icon           */
/* processing commands in the writable.          */
/*                                               */
/* Parameters are as standard for a Toolbox      */
/* event handler.                                */
/*************************************************/

int find_to_be_shown(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  char text[Limits_FindWrit];

  id = idb->self_id;

  ChkError(windows_process_component_text(idb->self_id, FindWrit, text, sizeof(text), 0, 1));

  /* 'Local' event handlers */

  ChkError(event_register_toolbox_handler(idb->self_id,
                                          EFindOK,
                                          find_ok,
                                          NULL));

  ChkError(event_register_toolbox_handler(idb->self_id,
                                          EFindCancel,
                                          find_cancel,
                                          NULL));

  ChkError(event_register_toolbox_handler(idb->self_id,
                                          EFindFromStart,
                                          find_from_start_toggled,
                                          NULL));

  ChkError(event_register_toolbox_handler(idb->self_id,
                                          WritableField_ValueChanged,
                                          find_string_has_changed,
                                          NULL));

  /* Animation handler, if required */

  if (
       controls.dbox_anims &&
       !gadget_get_type(0, idb->self_id, StatusBarAnimAnim, NULL)
     )
     register_null_claimant(Wimp_ENull,
                            toolbars_animate_slow,
                            (void *) idb->self_id);

  /* Try to find the browser we were opened over */

  if (toolbox_get_client_handle(0, idb->ancestor_id, (void *) &over_b))
  {
    #ifdef TRACE

      erb.errnum = Utils_Error_Custom_Message;
      strcpy(erb.errmess, "Can't find ancestor browser in find_to_be_shown");
      show_error_ret(&erb);

    #endif

    over_b = last_browser;
  }

  if (!is_known_browser(over_b)) over_b = last_browser;

  /* Can't do much if we end up with no browser! */

  if (!over_b)
  {
    #ifdef TRACE

      erb.errnum = Utils_Error_Custom_Message;
      strcpy(erb.errmess, "Ended up with a NULL browser pointer in find_to_be_shown");
      show_error_ret(&erb);

    #endif

    return 0;
  }

  /* Make sure the last found token / data offset stores are cleared */

  last_t = NULL;
  last_o = 0;

  /* If From Start was automatically toggled off, turn it */
  /* back on again.                                       */

  if (fs_so)
  {
    optionbutton_set_state(0,
                           idb->self_id,
                           FindFromStart,
                           1);
  }

  /* Make sure the forwards/backwards radios are up to date */

  find_from_start_toggled(eventcode, event, idb, handle);

  return 1;
}

/*************************************************/
/* find_hidden()                                 */
/*                                               */
/* Called when the EFindHidden event is          */
/* generated, typically when the Find window     */
/* is closed.                                    */
/*                                               */
/* Parameters are as standard for a Toolbox      */
/* event handler.                                */
/*************************************************/

int find_hidden(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  /* Remove any 'local' event handlers */

  if (
       controls.dbox_anims &&
       !gadget_get_type(0, idb->self_id, StatusBarAnimAnim, NULL)
     )
     deregister_null_claimant(Wimp_ENull,
                              toolbars_animate_slow,
                              (void *) idb->self_id);

  ChkError(event_deregister_toolbox_handlers_for_object(idb->self_id));

  return 1;
}

/*************************************************/
/* find_close()                                  */
/*                                               */
/* Closes the Find dialogue, assuming its Object */
/* ID is stored in 'id'.                         */
/*************************************************/

void find_close(void)
{
  over_b = NULL;
  last_t = NULL;
  last_o = 0;

  toolbox_hide_object(0, id);
}

/*************************************************/
/* find_from_start_toggled()                     */
/*                                               */
/* Called when the EFindFromStart event is       */
/* generated, typically when the 'From start'    */
/* option in the Find dialogue box is toggled.   */
/*                                               */
/* Parameters are as standard for a Toolbox      */
/* event handler.                                */
/*************************************************/

static int find_from_start_toggled(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  int state;

  /* Clear the 'automatically switched off' flag */

  fs_so = 0;

  /* Get the option button state */

  if (!optionbutton_get_state(0,
                              idb->self_id,
                              FindFromStart,
                              &state))
  {
    /* If switched on, grey out the 'Forwards' and 'Backwards' radios */

    if (state)
    {
      set_gadget_state(idb->self_id, FindForwards,  1);
      set_gadget_state(idb->self_id, FindBackwards, 1);
    }

    /* Otherwise, ungrey them */

    else
    {
      set_gadget_state(idb->self_id, FindForwards,  0);
      set_gadget_state(idb->self_id, FindBackwards, 0);
    }
  }

  return 1;
}

/*************************************************/
/* find_ok()                                     */
/*                                               */
/* Called when the EFindOK event is generated,   */
/* typically from the 'OK' button in the Find    */
/* dialogue box.                                 */
/*                                               */
/* Parameters are as standard for a Toolbox      */
/* event handler.                                */
/*************************************************/

static int find_ok(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  WimpGetWindowStateBlock   state;
  reformat_cell           * cell;
  int                       line;

  HStream                 * found = NULL;
  int                       offset;

  char                      find[Limits_FindWrit];
  int                       cs;
  int                       forwards, from_start;

  /* Get the search string */

  ChkError(writablefield_get_value(0,
                                   idb->self_id,
                                   FindWrit,
                                   find,
                                   sizeof(find),
                                   NULL));

  /* Searching from the start? If we can't read the value, */
  /* default to 'from start' for this, unless we'd turned  */
  /* that off deliberately.                                */

  if (
       optionbutton_get_state(0,
                              idb->self_id,
                              FindFromStart,
                              &from_start)
     )
     from_start = !fs_so;

  /* If searching from the start, forwards is implicit; */
  /* otherwise, read the 'Forwards' radio, and default  */
  /* to 'forwards' if this can't be read.               */

  if (
       from_start ||
       radiobutton_get_state(0,
                             idb->self_id,
                             FindForwards,
                             &forwards,
                             NULL)
     )
     forwards = 1;

  /* Case insensitive, unless we can find out otherwise */

  if (
       optionbutton_get_state(0,
                              idb->self_id,
                              FindCaseSensitive,
                              &cs)
     )
     cs = 0;

  /* If we haven't found a token before, try to find */
  /* one closest to the top left of the window.      */

  if (!last_t)
  {
    state.window_handle = over_b->window_handle;
    ChkError(wimp_get_window_state(&state));

    line = browser_top_line(over_b,
                            &cell,
                            &state,
                            0);

    if (!cell) last_t = 0;
    else
    {
      int chunk;

      if (line < 0) line = 0;

      if (line > cell->nlines) last_t = 0;
      else
      {
        chunk = cell->ldata[line].chunks;

        if (chunk < 0) last_t = 0;
        else           last_t = cell->cdata[chunk].t;
      }
    }

    /* If there's no token, must start at the top - */
    /* otherwise, it's up to the user.              */

    if (!last_t)
    {
      optionbutton_set_state(0,
                             idb->self_id,
                             FindFromStart,
                             1);

      last_t = over_b->stream;

      set_gadget_state(idb->self_id, FindFromStart, 1);
    }
    else set_gadget_state(idb->self_id, FindFromStart, 0);

    last_o = 0;
  }

  /* Try to find the search string */

  if (is_known_browser(over_b) && last_t)
  {
    found = fetch_find_text_token(over_b,
                                  find,
                                  from_start ? NULL : last_t,
                                  from_start ? 0    : last_o,
                                  &offset,
                                  cs,
                                  forwards);
  }

  /* Beep if nothing is found... */

  if (!found) Beep; /* (See Utils.h) */

  /* Otherwise, show the item */

  else
  {
    last_t = found;
    last_o = offset;

    if (
         !browser_show_token(over_b,
                             last_t,
                             last_o)
       )
       Beep;

    else
    {
      /* If the item is successfully shown, there may be another. */
      /* In that case, we want to turn off 'From start', if it is */
      /* turned on, and make sure we're continuing forwards.      */

      if (from_start)
      {
        optionbutton_set_state(0,
                               idb->self_id,
                               FindFromStart,
                               0);

        find_from_start_toggled(eventcode, event, idb, handle);

        /* Flag that From Start was switched off automatically */

        fs_so = 1;

        /* Set 'Forwards' */

        radiobutton_set_state(0,
                              idb->self_id,
                              FindForwards,
                              1);

        /* The above button may not exist, so we still need to */
        /* turn off 'Backwards' explicitly.                    */

        radiobutton_set_state(0,
                              idb->self_id,
                              FindBackwards,
                              0);
      }
    }
  }

  return 1;
}

/*************************************************/
/* find_cancel()                                 */
/*                                               */
/* Called when the EFindCancel event is          */
/* generated, typically from the 'Cancel' button */
/* in the Find dialogue box.                     */
/*                                               */
/* Parameters are as standard for a Toolbox      */
/* event handler.                                */
/*************************************************/

static int find_cancel(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  find_close();

  return 1;
}

/*************************************************/
/* find_string_has_changed()                     */
/*                                               */
/* Called when a WritableField_ValueChanged      */
/* event is raised from the Find dialogue box.   */
/* Invalidates the record of last item found.    */
/*                                               */
/* Parameters are as standard for a Toolbox      */
/* event handler.                                */
/*************************************************/

static int find_string_has_changed(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle)
{
  last_t = NULL;
  last_o = 0;

  return 1;
}