jens-maus/yam

View on GitHub
src/mui/SearchControlGroup.c

Summary

Maintainability
Test Coverage
/***************************************************************************

 YAM - Yet Another Mailer
 Copyright (C) 1995-2000 Marcel Beck
 Copyright (C) 2000-2022 YAM Open Source Team

 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.   See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

 YAM Official Support Site : http://www.yam.ch
 YAM OpenSource project     : http://sourceforge.net/projects/yamos/

 $Id$

 Superclass:  MUIC_ObjectListitem
 Description: Provides some GUI elements for filter/search controls

***************************************************************************/

#if defined(__AROS__)
#define MUI_OBSOLETE 1
#endif

#include "SearchControlGroup_cl.h"

#include <string.h>
#include <proto/asl.h>
#include <proto/muimaster.h>
#include <mui/BetterString_mcc.h>

#include "YAM.h"
#include "YAM_find.h"
#include "YAM_utilities.h"

#include "mui/ObjectList.h"
#include "mui/ObjectListitem.h"

#include "Config.h"
#include "Locale.h"
#include "MUIObjects.h"

#include "Debug.h"

/* CLASSDATA
struct Data
{
  Object *PG_SRCHOPT;
  Object *PG_MODE;
  Object *CY_MODE[2];
  Object *ST_FIELD;
  Object *CY_COMP[5];
  Object *ST_MATCH[5];
  Object *BT_FILE[5];
  Object *BT_EDIT[5];
  Object *RA_ADRMODE;
  Object *CY_STATUS;
  Object *CH_CASESENS[5];
  Object *CH_SUBSTR[5];
  Object *CH_DOSPATTERN[5];
  Object *CH_SKIPENCRYPTED[5];
  Object *RT_BUTTONS;
  Object *BT_ADDRULE;
  Object *BT_REMRULE;
  Object *activeObject;

  BOOL remoteFilterMode;

  const char *statusCycleEntries[11];
};
*/

enum ModePage
{
  MP_FROM = 0,
  MP_TO,
  MP_CC,
  MP_REPLYTO,
  MP_SUBJECT,
  MP_DATE,
  MP_OTHER,
  MP_SIZE,
  MP_HEADER,
  MP_BODY,
  MP_WHOLE,
  MP_STATUS,
  MP_COUNT
};

static const int Mode2Group[MP_COUNT] = { 0,0,0,0,1,2,1,2,4,4,4,3 };

/* Overloaded Methods */
/// OVERLOAD(OM_NEW)
OVERLOAD(OM_NEW)
{
  static const char *fldopt[2][MP_COUNT+1];
  static const char *compopt[14];
  static const char *statopt[11];
  static const char *amode[3];
  struct Data *data;
  struct Data *tmpData;
  struct TagItem *tags = inittags(msg), *tag;
  BOOL singleRule = FALSE;

  ENTER();

  amode[0] = tr(MSG_Address);
  amode[1] = tr(MSG_Name);
  amode[2] = NULL;

  // make sure the following array has the same
  // order than the mailStatusMap in YAM_global.c
  statopt[0] = tr(MSG_FI_StatNew);
  statopt[1] = tr(MSG_FI_StatRead);
  statopt[2] = tr(MSG_FI_StatForwarded);
  statopt[3] = tr(MSG_FI_StatReplied);
  statopt[4] = tr(MSG_FI_StatQueued);
  statopt[5] = tr(MSG_FI_StatFailed);
  statopt[6] = tr(MSG_FI_StatHold);
  statopt[7] = tr(MSG_FI_StatSent);
  statopt[8] = tr(MSG_FI_StatMarked);
  statopt[9] = tr(MSG_FI_STATSPAM);
  statopt[10] = NULL;

  compopt[0] = compopt[5] = compopt[ 8] = " = ";
  compopt[1] = compopt[6] = compopt[ 9] = " <> ";
  compopt[2] =              compopt[10] = " < ";
  compopt[3] =              compopt[11] = " > ";
                            compopt[12] = " IN ";
  compopt[4] = compopt[7] = compopt[13] = NULL;

  fldopt[0][MP_FROM]    = fldopt[1][MP_FROM]    = tr(MSG_FI_FROM_FIELD);
  fldopt[0][MP_TO]      = fldopt[1][MP_TO]      = tr(MSG_FI_TO_FIELD);
  fldopt[0][MP_CC]      = fldopt[1][MP_CC]      = tr(MSG_FI_CC_FIELD);
  fldopt[0][MP_REPLYTO] = fldopt[1][MP_REPLYTO] = tr(MSG_FI_REPLYTO_FIELD);
  fldopt[0][MP_SUBJECT] = fldopt[1][MP_SUBJECT] = tr(MSG_FI_SUBJECT_FIELD);
  fldopt[0][MP_DATE]    = fldopt[1][MP_DATE]    = tr(MSG_FI_DATE_FIELD);
  fldopt[0][MP_OTHER]   = fldopt[1][MP_OTHER]   = tr(MSG_FI_OtherField);
  fldopt[0][MP_SIZE]    = fldopt[1][MP_SIZE]    = tr(MSG_FI_MessageSize);
  fldopt[0][MP_HEADER]  = fldopt[1][MP_HEADER]  = tr(MSG_FI_MessageHeader);
  fldopt[0][MP_BODY]    =                         tr(MSG_FI_MessageBody);
  fldopt[0][MP_WHOLE]   =                         tr(MSG_FI_WholeMessage);
  fldopt[0][MP_STATUS]  =                         tr(MSG_Status);
  fldopt[0][MP_COUNT]   = NULL;

  fldopt[1][MP_BODY] = NULL;
  fldopt[1][MP_WHOLE] = NULL;
  fldopt[1][MP_STATUS] = NULL;
  fldopt[1][MP_COUNT] = NULL;

  // generate a temporary struct Data to which we store our data and
  // copy it later on
  if(!(data = tmpData = calloc(1, sizeof(struct Data))))
  {
    RETURN(0);
    return 0;
  }

  memcpy(data->statusCycleEntries, statopt, sizeof(data->statusCycleEntries));

  // get eventually set attributes first
  while((tag = NextTagItem((APTR)&tags)) != NULL)
  {
    switch(tag->ti_Tag)
    {
      case ATTR(RemoteFilterMode): data->remoteFilterMode = tag->ti_Data; break;
      case ATTR(SingleRule):       singleRule = tag->ti_Data; break;
      case ATTR(AllowSpamStatus):
      {
        // disable the "Spam" status entry
        if(tag->ti_Data == FALSE)
          data->statusCycleEntries[9] = NULL;
      }
      break;
    }
  }

  if((obj = DoSuperNew(cl, obj,

    MUIA_Group_Horiz, FALSE,
    MUIA_HelpNode, "Windows/Searchwindow",
     Child, HGroup,
        Child, Label1(tr(MSG_FI_SearchIn)),
        Child, data->PG_MODE = PageGroup,
          MUIA_Group_ActivePage, data->remoteFilterMode,
          Child, data->CY_MODE[0] = MakeCycle(fldopt[0], tr(MSG_FI_SearchIn)),
          Child, data->CY_MODE[1] = MakeCycle(fldopt[1], tr(MSG_FI_SearchIn)),
        End,
        Child, data->ST_FIELD = BetterStringObject,
          StringFrame,
          MUIA_String_Accept,      "!#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_abcdefghijklmnopqrstuvwxyz{|}~",
          MUIA_String_MaxLen,      SIZE_DEFAULT,
          MUIA_String_AdvanceOnCR, TRUE,
          MUIA_CycleChain,         TRUE,
        End,
      End,
      Child, data->PG_SRCHOPT = PageGroup,
        Child, VGroup, // 0  from, to, cc, reply-to
          Child, HGroup,
            MUIA_Group_HorizSpacing, 0,
            Child, data->CY_COMP[0] = MakeCycle(&compopt[8], ""),
            Child, HSpace(4),
            Child, PopaslObject,
              MUIA_Popasl_Type,      ASL_FileRequest,
              MUIA_Popstring_String, data->ST_MATCH[0] = MakeString(SIZE_PATTERN, ""),
              MUIA_Popstring_Button, data->BT_FILE[0] = PopButton(MUII_PopFile),
            End,
            Child, data->BT_EDIT[0] = PopButton(MUII_PopUp),
          End,
          Child, HGroup,
            Child, VGroup,
              Child, MakeCheckGroup((Object **)&data->CH_CASESENS[0], tr(MSG_FI_CaseSensitive)),
              Child, MakeCheckGroup((Object **)&data->CH_SUBSTR[0], tr(MSG_FI_SubString)),
              Child, MakeCheckGroup((Object **)&data->CH_DOSPATTERN[0], tr(MSG_FI_DOS_PATTERN)),
            End,
            Child, data->RA_ADRMODE = Radio(NULL, amode),
          End,
        End,
        Child, VGroup, // 1  subject, other field
          Child, HGroup,
            MUIA_Group_HorizSpacing, 0,
            Child, data->CY_COMP[1] = MakeCycle(&compopt[8], ""),
            Child, HSpace(4),
            Child, PopaslObject,
              MUIA_Popasl_Type,      ASL_FileRequest,
              MUIA_Popstring_String, data->ST_MATCH[1] = MakeString(SIZE_PATTERN,""),
              MUIA_Popstring_Button, data->BT_FILE[1] = PopButton(MUII_PopFile),
            End,
            Child, data->BT_EDIT[1] = PopButton(MUII_PopUp),
          End,
          Child, VGroup,
            Child, MakeCheckGroup((Object **)&data->CH_CASESENS[1], tr(MSG_FI_CaseSensitive)),
            Child, MakeCheckGroup((Object **)&data->CH_SUBSTR[1], tr(MSG_FI_SubString)),
            Child, MakeCheckGroup((Object **)&data->CH_DOSPATTERN[1], tr(MSG_FI_DOS_PATTERN)),
          End,
        End,
        Child, VGroup, // 2  date, size
          Child, HGroup,
            Child, data->CY_COMP[2] = MakeCycle(compopt, ""),
            Child, data->ST_MATCH[2] = MakeString(SIZE_PATTERN, ""),
          End,
          Child, HVSpace,
        End,
        Child, VGroup, // 3  status
           Child, HGroup,
             Child, data->CY_COMP[3] = MakeCycle(&compopt[5], ""),
             Child, data->CY_STATUS = MakeCycle(NULL, ""),
             Child, HSpace(0),
           End,
          Child, HVSpace,
        End,
        Child, VGroup, // 4  message header/body
          Child, HGroup,
            Child, data->CY_COMP[4] = MakeCycle(&compopt[5], ""),
            Child, data->ST_MATCH[4] = MakeString(SIZE_PATTERN, ""),
          End,
          Child, MakeCheckGroup((Object **)&data->CH_CASESENS[4], tr(MSG_FI_CaseSensitive)),
          Child, MakeCheckGroup((Object **)&data->CH_DOSPATTERN[4], tr(MSG_FI_DOS_PATTERN)),
          Child, MakeCheckGroup((Object **)&data->CH_SKIPENCRYPTED[4], tr(MSG_FI_SKIP_ENCRYPTED)),
          Child, HVSpace,
        End,
      End,
      Child, data->RT_BUTTONS = HGroup,
        Child, RectangleObject,
          MUIA_Rectangle_HBar, TRUE,
          MUIA_FixHeight, 4,
        End,
        Child, HGroup,
          MUIA_Weight, 0,
          MUIA_Group_Spacing, 1,
          MUIA_Group_SameWidth, TRUE,
          Child, data->BT_ADDRULE = MakeButton(MUIX_B "+" MUIX_N),
          Child, data->BT_REMRULE = MakeButton(MUIX_B "-" MUIX_N),
        End,
      End,

    TAG_MORE, inittags(msg))) != NULL)
  {
    int i;

    data = (struct Data *)INST_DATA(cl, obj);

    // copy back the data stored in our temporarly struct Data
    memcpy(data, tmpData, sizeof(struct Data));

    // set the cycle entries containing the correct strings
    set(data->CY_STATUS, MUIA_Cycle_Entries, data->statusCycleEntries);

    // if this isn't a single rule we show the +/- buttons
    set(data->RT_BUTTONS, MUIA_ShowMe, singleRule == FALSE);
    if(singleRule == FALSE)
    {
      xset(obj, MUIA_Frame, MUIV_Frame_Group,
                MUIA_Background, MUII_GroupBack);
    }

    // set the cyclechain
    set(data->RA_ADRMODE, MUIA_CycleChain, TRUE);

    // set ST_MATCH[0] as the current active object
    data->activeObject = data->ST_MATCH[0];

    // set help text
    SetHelp(data->CY_MODE[0], MSG_HELP_FI_CY_MODE);
    SetHelp(data->CY_MODE[1], MSG_HELP_FI_CY_MODE);
    SetHelp(data->ST_FIELD,   MSG_HELP_FI_ST_FIELD);
    SetHelp(data->RA_ADRMODE, MSG_HELP_FI_RA_ADRMODE);
    SetHelp(data->CY_STATUS,  MSG_HELP_FI_CY_STATUS);
    SetHelp(data->BT_ADDRULE, MSG_HELP_CO_BT_MORE);
    SetHelp(data->BT_REMRULE, MSG_HELP_CO_BT_LESS);

    DoMethod(data->BT_ADDRULE, MUIM_Notify, MUIA_Pressed,         FALSE,          obj, 1, MUIM_ObjectListitem_CreateAndAddItem);
    DoMethod(data->BT_REMRULE, MUIM_Notify, MUIA_Pressed,         FALSE,          obj, 1, MUIM_ObjectListitem_Remove);
    DoMethod(data->CY_MODE[0], MUIM_Notify, MUIA_Cycle_Active,    MUIV_EveryTime, obj, 1, METHOD(Update));
    DoMethod(data->CY_MODE[1], MUIM_Notify, MUIA_Cycle_Active,    MUIV_EveryTime, obj, 1, METHOD(Update));
    DoMethod(data->RA_ADRMODE, MUIM_Notify, MUIA_Radio_Active,    MUIV_EveryTime, obj, 3, MUIM_Set, ATTR(Modified), TRUE);
    DoMethod(data->ST_FIELD,   MUIM_Notify, MUIA_String_Contents, MUIV_EveryTime, obj, 3, MUIM_Set, ATTR(Modified), TRUE);
    DoMethod(data->CY_STATUS,  MUIM_Notify, MUIA_Cycle_Active,    MUIV_EveryTime, obj, 3, MUIM_Set, ATTR(Modified), TRUE);

    for(i = 0; i < 5; i++)
    {
      if(data->CY_COMP[i] != NULL)
      {
        DoMethod(data->CY_COMP[i], MUIM_Notify, MUIA_Cycle_Active, MUIV_EveryTime, obj, 1, METHOD(Update));
        DoMethod(data->CY_COMP[i], MUIM_Notify, MUIA_Cycle_Active, MUIV_EveryTime, obj, 3, MUIM_Set, ATTR(Modified), TRUE);
        SetHelp(data->CY_COMP[i], MSG_HELP_FI_CY_COMP);
        set(data->CY_COMP[i], MUIA_HorizWeight, 0);
      }

      if(data->ST_MATCH[i] != NULL)
      {
        DoMethod(data->ST_MATCH[i], MUIM_Notify, MUIA_String_Contents, MUIV_EveryTime, obj, 3, MUIM_Set, ATTR(Modified), TRUE);
        SetHelp(data->ST_MATCH[i], MSG_HELP_FI_ST_MATCH);
      }

      if(data->CH_CASESENS[i] != NULL)
      {
        DoMethod(data->CH_CASESENS[i], MUIM_Notify, MUIA_Selected, MUIV_EveryTime, obj, 3, MUIM_Set, ATTR(Modified), TRUE);
        SetHelp(data->CH_CASESENS[i], MSG_HELP_FI_CH_CASESENS);
      }

      if(data->CH_SUBSTR[i] != NULL)
      {
        DoMethod(data->CH_SUBSTR[i], MUIM_Notify, MUIA_Selected, MUIV_EveryTime, obj, 3, MUIM_Set, ATTR(Modified), TRUE);
        SetHelp(data->CH_SUBSTR[i], MSG_HELP_FI_CH_SUBSTR);
        nnset(data->CH_SUBSTR[i], MUIA_Selected, TRUE);
      }

      if(data->CH_DOSPATTERN[i] != NULL)
      {
        DoMethod(data->CH_DOSPATTERN[i], MUIM_Notify, MUIA_Selected, MUIV_EveryTime, obj, 3, MUIM_Set, ATTR(Modified), TRUE);
        SetHelp(data->CH_DOSPATTERN[i], MSG_HELP_FI_CH_DOS_PATTERN);
      }

      if(data->CH_SKIPENCRYPTED[i] != NULL)
      {
        DoMethod(data->CH_SKIPENCRYPTED[i], MUIM_Notify, MUIA_Selected, MUIV_EveryTime, obj, 3, MUIM_Set, ATTR(Modified), TRUE);
        SetHelp(data->CH_SKIPENCRYPTED[i], MSG_HELP_FI_CH_SKIP_ENCRYPTED);
      }

      if(data->BT_EDIT[i] != NULL && data->ST_MATCH[i] != NULL)
        DoMethod(data->BT_EDIT[i], MUIM_Notify, MUIA_Pressed, FALSE, obj, 2, METHOD(EditFile), data->ST_MATCH[i]);
    }

    // set up some notifications to let certain objects share the same search string
    DoMethod(data->ST_MATCH[0], MUIM_Notify, MUIA_String_Contents, MUIV_EveryTime, obj, 3, METHOD(CloneSearchString), data->ST_MATCH[0], MUIV_TriggerValue);
    DoMethod(data->ST_MATCH[1], MUIM_Notify, MUIA_String_Contents, MUIV_EveryTime, obj, 3, METHOD(CloneSearchString), data->ST_MATCH[1], MUIV_TriggerValue);
    DoMethod(data->ST_MATCH[4], MUIM_Notify, MUIA_String_Contents, MUIV_EveryTime, obj, 3, METHOD(CloneSearchString), data->ST_MATCH[4], MUIV_TriggerValue);

    // make sure all elements are enabled.
    set(obj, MUIA_Disabled, FALSE);
  }

  // free the temporary mem we allocated before
  free(tmpData);

  RETURN((IPTR)obj);
  return (IPTR)obj;
}

///
/// OVERLOAD(OM_SET)
OVERLOAD(OM_SET)
{
  GETDATA;
  struct TagItem *tags = inittags(msg), *tag;
  ULONG ret = 0;

  ENTER();

  while((tag = NextTagItem((APTR)&tags)) != NULL)
  {
    switch(tag->ti_Tag)
    {
      case ATTR(RemoteFilterMode):
      {
        // we check if we switch the FilterMode
        if(tag->ti_Data != (ULONG)data->remoteFilterMode)
        {
          int oldActive = xget(data->CY_MODE[data->remoteFilterMode], MUIA_Cycle_Active);

          // lets first copy the cycle status from the old
          // to the new status
          if(data->remoteFilterMode == FALSE)
            set(data->CY_MODE[tag->ti_Data], MUIA_Cycle_Active, oldActive > 8 ? 8 : oldActive);
          else
            set(data->CY_MODE[tag->ti_Data], MUIA_Cycle_Active, oldActive);

          data->remoteFilterMode = tag->ti_Data;
          set(data->PG_MODE, MUIA_Group_ActivePage, data->remoteFilterMode);
        }
      }
      break;

      case ATTR(RemoveForbidden):
      {
        set(data->BT_REMRULE, MUIA_Disabled, tag->ti_Data);
      }
      break;

      // Overload some global attributes as well
      case MUIA_Disabled:
      {
        BOOL disabled = (BOOL)tag->ti_Data;
        int i;
        int mode = GetMUICycle(data->CY_MODE[data->remoteFilterMode]);
        int oper = GetMUICycle(data->CY_COMP[Mode2Group[mode]]);

        set(data->CY_MODE[0], MUIA_Disabled, disabled);
        set(data->CY_MODE[1], MUIA_Disabled, disabled);
        set(data->ST_FIELD, MUIA_Disabled, disabled);
        set(data->ST_FIELD, MUIA_ShowMe, mode == 6);
        set(data->RA_ADRMODE, MUIA_Disabled, disabled);
        set(data->CY_STATUS, MUIA_Disabled, disabled);

        for(i = 0; i < 5; i++)
        {
          set(data->CY_COMP[i], MUIA_Disabled, disabled);

          if(data->ST_MATCH[i] != NULL)
            set(data->ST_MATCH[i], MUIA_Disabled, disabled);
          if(data->CH_CASESENS[i] != NULL)
            set(data->CH_CASESENS[i], MUIA_Disabled, disabled);
          if(data->CH_SUBSTR[i] != NULL)
            set(data->CH_SUBSTR[i], MUIA_Disabled, disabled || oper == 4 || (i < 2 && oper > 1));
          if(data->CH_DOSPATTERN[i] != NULL)
            set(data->CH_DOSPATTERN[i], MUIA_Disabled, disabled);
          if(data->CH_SKIPENCRYPTED[i] != NULL)
            set(data->CH_SKIPENCRYPTED[i], MUIA_Disabled, disabled || mode == SM_HEADER);
          if(data->BT_FILE[i] != NULL)
            set(data->BT_FILE[i], MUIA_Disabled, disabled || oper != 4);
          if(data->BT_EDIT[i] != NULL)
          {
            const char *file = (data->ST_MATCH[i] != NULL) ? (const char *)xget(data->ST_MATCH[i], MUIA_String_Contents) : NULL;

            set(data->BT_EDIT[i], MUIA_Disabled, disabled || oper != 4 || IsStrEmpty(file));
          }
        }

        // let our superclass ignore this tag
        tag->ti_Tag = TAG_IGNORE;
      }
      break;
    }
  }

  ret = DoSuperMethodA(cl, obj, msg);

  RETURN(ret);
  return ret;
}

///
/// OVERLOAD(OM_GET)
OVERLOAD(OM_GET)
{
  GETDATA;
  IPTR *store = ((struct opGet *)msg)->opg_Storage;

  switch(((struct opGet *)msg)->opg_AttrID)
  {
    case ATTR(Modified):     *store = 1; return TRUE;
    case ATTR(ActiveObject): *store = (ULONG)data->activeObject; return TRUE;
  }

  return DoSuperMethodA(cl, obj, msg);
}
///

/* Public Methods */
/// DECLARE(Clear)
DECLARE(Clear)
{
  GETDATA;
  int m;

  ENTER();

  for(m = 0; m < 5; m++)
  {
    // reset all GUI elements due to the new active filter
    nnset(data->CY_COMP[m], MUIA_Cycle_Active, 0);

    if(data->ST_MATCH[m] != NULL)
      nnset(data->ST_MATCH[m], MUIA_String_Contents, "");
    else
      nnset(data->CY_STATUS, MUIA_Cycle_Active, 0);

    if(data->CH_CASESENS[m] != NULL)
      nnset(data->CH_CASESENS[m], MUIA_Selected, FALSE);

    if(data->CH_SUBSTR[m] != NULL)
      nnset(data->CH_SUBSTR[m], MUIA_Selected, FALSE);

    if(data->CH_DOSPATTERN[m] != NULL)
      nnset(data->CH_DOSPATTERN[m], MUIA_Selected, FALSE);

    if(data->CH_SKIPENCRYPTED[m] != NULL)
      nnset(data->CH_SKIPENCRYPTED[m], MUIA_Selected, FALSE);
  }

  RETURN(0);
  return 0;
}

///
/// DECLARE(PrepareSearch)
DECLARE(PrepareSearch) // struct Search *search
{
  GETDATA;
  int pg;
  const char *match;
  const char *field;
  int flags;

  ENTER();

  pg = xget(data->PG_SRCHOPT, MUIA_Group_ActivePage);
  if(pg != 3) // Page 3 (Status) has no ST_MATCH
    match = (const char *)xget(data->ST_MATCH[pg], MUIA_String_Contents);
  else
    match = "";

  field = (const char *)xget(data->ST_FIELD, MUIA_String_Contents);

  // enable DOS patterns automatically if a pattern file is given
  if(GetMUICycle(data->CY_COMP[pg]) == 4)
    nnset(data->CH_DOSPATTERN[pg], MUIA_Selected, TRUE);

  flags = 0;
  if(GetMUICheck(data->CH_CASESENS[pg]) == TRUE)
    setFlag(flags, SEARCHF_CASE_SENSITIVE);
  if((pg < 2 && GetMUICheck(data->CH_SUBSTR[pg]) == TRUE) || pg == 4)
    setFlag(flags, SEARCHF_SUBSTRING);
  if(GetMUICheck(data->CH_DOSPATTERN[pg]) == TRUE)
    setFlag(flags, SEARCHF_DOS_PATTERN);
  if(GetMUICheck(data->CH_SKIPENCRYPTED[pg]) == TRUE)
    setFlag(flags, SEARCHF_SKIP_ENCRYPTED);

  FI_PrepareSearch(msg->search,
                   GetMUICycle(data->CY_MODE[data->remoteFilterMode]),
                   GetMUIRadio(data->RA_ADRMODE),
                   GetMUICycle(data->CY_COMP[pg]),
                   mailStatusCycleMap[GetMUICycle(data->CY_STATUS)],
                   match,
                   field,
                   flags);

  RETURN(0);
  return 0;
}

///
/// DECLARE(GUIToRule)
// fills a rule structure from the settings of this search controls
DECLARE(GUIToRule) // struct RuleNode *rule
{
  GETDATA;
  int g;
  struct RuleNode *rule;

  ENTER();

  g = xget(data->PG_SRCHOPT, MUIA_Group_ActivePage);
  rule = msg->rule;

  rule->searchMode = GetMUICycle(data->CY_MODE[data->remoteFilterMode]);
  rule->subSearchMode = GetMUIRadio(data->RA_ADRMODE);
  GetMUIString(rule->customField, data->ST_FIELD, sizeof(rule->customField));
  rule->comparison = GetMUICycle(data->CY_COMP[g]);

  // Page 3 (Status) has no ST_MATCH
  if(g == 3)
  {
    rule->matchPattern[0] = mailStatusCycleMap[GetMUICycle(data->CY_STATUS)];
    rule->matchPattern[1] = '\0';
  }
  else
  {
    GetMUIString(rule->matchPattern, data->ST_MATCH[g], sizeof(rule->matchPattern));
  }

  rule->flags = 0;
  if(data->CH_CASESENS[g] != NULL && GetMUICheck(data->CH_CASESENS[g]) == TRUE)
    setFlag(rule->flags, SEARCHF_CASE_SENSITIVE);

  if(data->CH_SUBSTR[g] != NULL && GetMUICheck(data->CH_SUBSTR[g]) == TRUE)
    setFlag(rule->flags, SEARCHF_SUBSTRING);

  if(data->CH_DOSPATTERN[g] != NULL && GetMUICheck(data->CH_DOSPATTERN[g]) == TRUE)
    setFlag(rule->flags, SEARCHF_DOS_PATTERN);

  if(data->CH_SKIPENCRYPTED[g] != NULL && GetMUICheck(data->CH_SKIPENCRYPTED[g]) == TRUE)
    setFlag(rule->flags, SEARCHF_SKIP_ENCRYPTED);

  // enable DOS patterns automatically if a pattern file is given
  if(rule->comparison == 4)
    setFlag(rule->flags, SEARCHF_DOS_PATTERN);

  RETURN(0);
  return 0;
}

///
/// DECLARE(RuleToGUI)
// fills a rule structure from the settings of this search controls
DECLARE(RuleToGUI) // struct RuleNode *rule
{
  GETDATA;
  struct RuleNode *rule;
  int g;

  ENTER();

  rule = msg->rule;
  g = Mode2Group[rule->searchMode];

  nnset(data->CY_MODE[data->remoteFilterMode],  MUIA_Cycle_Active,      rule->searchMode);
  nnset(data->RA_ADRMODE,                       MUIA_Radio_Active,      rule->subSearchMode);
  nnset(data->ST_FIELD,                         MUIA_String_Contents,   rule->customField);
  nnset(data->PG_SRCHOPT,                       MUIA_Group_ActivePage,  g);
  nnset(data->CY_COMP[g],                       MUIA_Cycle_Active,      rule->comparison);

  set(data->ST_FIELD, MUIA_ShowMe, rule->searchMode == 6);

  // Page 3 (Status) has no ST_MATCH
  if(g == 3)
  {
    size_t i;

    for(i=0; i < ARRAY_SIZE(mailStatusCycleMap); i++)
    {
      if(*rule->matchPattern == mailStatusCycleMap[i])
      {
        nnset(data->CY_STATUS, MUIA_Cycle_Active, i);
        break;
      }
    }
  }
  else
  {
    nnset(data->ST_MATCH[g], MUIA_String_Contents, rule->matchPattern);
  }

  if(data->CH_CASESENS[g] != NULL)
    nnset(data->CH_CASESENS[g], MUIA_Selected, isFlagSet(rule->flags, SEARCHF_CASE_SENSITIVE));

  if(data->CH_SUBSTR[g] != NULL)
    nnset(data->CH_SUBSTR[g], MUIA_Selected, isFlagSet(rule->flags, SEARCHF_SUBSTRING));

  if(data->CH_DOSPATTERN[g] != NULL)
    nnset(data->CH_DOSPATTERN[g], MUIA_Selected, isFlagSet(rule->flags, SEARCHF_DOS_PATTERN));

  if(data->CH_SKIPENCRYPTED[g] != NULL)
    nnset(data->CH_SKIPENCRYPTED[g], MUIA_Selected, isFlagSet(rule->flags, SEARCHF_SKIP_ENCRYPTED));

  RETURN(0);
  return 0;
}

///
/// DECLARE(Update)
//  Selects correct form for search mode
DECLARE(Update)
{
  GETDATA;
  enum ModePage mode;

  ENTER();

  mode = GetMUICycle(data->CY_MODE[data->remoteFilterMode]);
  if(mode < MP_COUNT)
  {
    ULONG group = Mode2Group[mode];
    Object *newActiveObj = NULL;

    set(data->PG_SRCHOPT, MUIA_Group_ActivePage, group);

    switch(group)
    {
      case 0:
      case 1:
      case 2:
      case 4:
        newActiveObj = data->ST_MATCH[group];
      break;

      case 3:
        newActiveObj = data->CY_STATUS;
      break;
    }

    if(_win(obj) != NULL &&
       ((Object *)xget(_win(obj), MUIA_Window_ActiveObject) == NULL ||
        (Object *)xget(_win(obj), MUIA_Window_ActiveObject) == data->activeObject))
    {
      set(_win(obj), MUIA_Window_ActiveObject, newActiveObj);
    }

    data->activeObject = newActiveObj;

    set(obj, MUIA_Disabled, FALSE);
  }

  set(obj, ATTR(Modified), TRUE);

  RETURN(0);
  return 0;
}

///
/// DECLARE(CloneSearchString)
DECLARE(CloneSearchString) // Object *origin, char *str
{
  GETDATA;

  ENTER();

  // set the modified string for all objects except the one
  // that triggered the change, but don't trigger further
  // notifications
  if(msg->origin != data->ST_MATCH[0])
    nnset(data->ST_MATCH[0], MUIA_String_Contents, msg->str);
  if(msg->origin != data->ST_MATCH[1])
    nnset(data->ST_MATCH[1], MUIA_String_Contents, msg->str);
  if(msg->origin != data->ST_MATCH[4])
    nnset(data->ST_MATCH[4], MUIA_String_Contents, msg->str);

  // update the enabled state of certain objects, i.e. the edit button for "IN" pattern files
  set(obj, MUIA_Disabled, FALSE);

  RETURN(0);
  return 0;
}

///
/// DECLARE(EditFile)
//  Edits pattern list in text editor
DECLARE(EditFile) // Object *strObject
{
  ENTER();

  if(IsStrEmpty(C->Editor) == FALSE)
  {
    char buffer[SIZE_COMMAND+SIZE_PATHFILE];

    snprintf(buffer, sizeof(buffer), "%s \"%s\"", C->Editor, GetRealPath((char *)xget(msg->strObject, MUIA_String_Contents)));
    LaunchCommand(buffer, LAUNCHF_ASYNC, OUT_NIL);
  }

  RETURN(0);
  return 0;
}

///