jens-maus/yam

View on GitHub
src/mui/WriteWindow.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_Window
 Description: Write window class

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

#include "WriteWindow_cl.h"

#include <ctype.h>
#include <string.h>

#include <proto/codesets.h>
#include <proto/dos.h>
#include <proto/muimaster.h>
#include <proto/wb.h>
#include <dos/notify.h>
#include <libraries/gadtools.h>
#include <libraries/iffparse.h>
#include <mui/BetterString_mcc.h>
#include <mui/NBalance_mcc.h>
#include <mui/NList_mcc.h>
#include <mui/NListtree_mcc.h>
#include <mui/NListview_mcc.h>
#include <mui/TextEditor_mcc.h>
#include <mui/TheBar_mcc.h>
#include <workbench/startup.h>

#include "SDI_hook.h"

#include "YAM.h"
#include "YAM_error.h"
#include "YAM_global.h"
#include "YAM_glossarydisplay.h"
#include "YAM_mainFolder.h"

#include "mime/uucode.h"
#include "mui/AddressBookWindow.h"
#include "mui/AddressField.h"
#include "mui/CodesetPopup.h"
#include "mui/IdentityChooser.h"
#include "mui/MailTextEdit.h"
#include "mui/MimeTypePopup.h"
#include "mui/ReadMailGroup.h"
#include "mui/ReadWindow.h"
#include "mui/RecipientString.h"
#include "mui/SearchTextWindow.h"
#include "mui/SignatureChooser.h"
#include "mui/WriteAttachmentList.h"
#include "mui/WriteWindowToolbar.h"
#include "mui/YAMApplication.h"
#include "tcp/smtp.h"

#include "Busy.h"
#include "Config.h"
#include "DynamicString.h"
#include "FileInfo.h"
#include "FolderList.h"
#include "Locale.h"
#include "Logfile.h"
#include "MailList.h"
#include "MailServers.h"
#include "MUIObjects.h"
#include "ParseEmail.h"
#include "Requesters.h"
#include "Signature.h"
#include "Threads.h"
#include "UserIdentity.h"

#include "Debug.h"

/* CLASSDATA
struct Data
{
  Object *RG_PAGE;
  Object *LB_TO;
  Object *ST_TO;
  Object *GR_TO;
  Object *LB_SUBJECT;
  Object *ST_SUBJECT;
  Object *TX_POSI;
  Object *TE_EDIT;
  Object *TO_TOOLBAR;
  Object *LV_ATTACH;
  Object *GR_ATTACH_TINY;
  Object *LV_ATTACH_TINY;
  Object *BT_ADD;
  Object *BT_ADDPACK;
  Object *BT_REMOVE;
  Object *BT_RENAME;
  Object *BT_DISPLAY;
  Object *RA_ENCODING;
  Object *CY_CTYPE;
  Object *PO_CTYPE;
  Object *ST_DESC;
  Object *GR_HEADER;
  Object *LB_CC;
  Object *ST_CC;
  Object *GR_CC;
  Object *MN_CC;
  Object *LB_BCC;
  Object *ST_BCC;
  Object *GR_BCC;
  Object *MN_BCC;
  Object *LB_FROM;
  Object *CY_FROM;
  Object *LB_REPLYTO;
  Object *ST_REPLYTO;
  Object *GR_REPLYTO;
  Object *MN_REPLYTO;
  Object *LB_FROM_OVERRIDE;
  Object *ST_FROM_OVERRIDE;
  Object *GR_FROM_OVERRIDE;
  Object *ST_EXTHEADER;
  Object *CH_DELSENT;
  Object *CH_MDN;
  Object *CH_ADDINFO;
  Object *CY_IMPORTANCE;
  Object *CY_SECURITY;
  Object *CH_DEFSECURITY;
  Object *CY_SIGNATURE;
  Object *BT_SAVEASDRAFT;
  Object *BT_QUEUE;
  Object *BT_SEND;
  Object *BT_CANCEL;
  Object *MI_BOLD;
  Object *MI_ITALIC;
  Object *MI_UNDERLINE;
  Object *MI_COLORED;
  Object *MI_AUTOSPELL;
  Object *MI_AUTOWRAP;
  Object *MI_DELSENT;
  Object *MI_MDN;
  Object *MI_ADDINFO;
  Object *MI_FFONT;
  Object *MI_TCOLOR;
  Object *MI_TSTYLE;
  Object *WI_SEARCH;
  Object *PO_CODESET;
  Object *MI_SIGNATURE;
  Object *MI_SIGNATURES[MAXSIG_MENU];
  Object *GR_ATTACH_REMIND;
  Object *BT_ATTACH_REMIND_ADD;
  Object *BT_ATTACH_REMIND_LATER;
  Object *BT_ATTACH_REMIND_NEVER;

  struct WriteMailData *wmData; // ptr to write mail data structure
  struct MsgPort *notifyPort;
  struct NotifyRequest *notifyRequest;

  int windowNumber; // the unique window number
  BOOL autoSaved;   // was this mail automatically saved?
  BOOL mailModified; // was anything modified?

  BOOL useFixedFont;   // use a fixed font for displaying the mail
  BOOL useTextColors;  // use Textcolors for displaying the mail
  BOOL useTextStyles;  // use Textstyles for displaying the mail

  BOOL fromRcptHidden;         // TRUE if From: identity chooser is hidden
  BOOL fromOverrideRcptHidden; // TRUE if From: recipient field is hidden
  BOOL ccRcptHidden;           // TRUE if CC: recipient field is hidden
  BOOL bccRcptHidden;          // TRUE if BCC: recipient field is hidden
  BOOL replyToRcptHidden;      // TRUE if Reply-To: recipient field is hidden

  char cursorPos[SIZE_SMALL];
  char windowTitle[SIZE_SUBJECT+1]; // string for the title text of the window
  char screenTitle[SIZE_LARGE];     // string for the title text of the screen
  char windowNumberStr[SIZE_SMALL]; // the unique window number as a string

  enum AttachmentRemind attachmentRemind;
};
*/

/* EXPORT
enum RcptType
{
  MUIV_WriteWindow_RcptType_From = 0,
  MUIV_WriteWindow_RcptType_FromOverride,
  MUIV_WriteWindow_RcptType_To,
  MUIV_WriteWindow_RcptType_CC,
  MUIV_WriteWindow_RcptType_BCC,
  MUIV_WriteWindow_RcptType_ReplyTo
};

enum ActiveObject
{
  MUIV_WriteWindow_ActiveObject_To = 0,
  MUIV_WriteWindow_ActiveObject_TextEditor,
  MUIV_WriteWindow_ActiveObject_Subject
};

enum WriteMode
{
  WRITE_SEND = 0,
  WRITE_QUEUE,
  WRITE_DRAFT
};

enum AttachmentRemind
{
  MUIV_WriteWindow_AttachmentRemind_None = 0,
  MUIV_WriteWindow_AttachmentRemind_Never,
  MUIV_WriteWindow_AttachmentRemind_Later
};
*/

// menu item IDs
enum
{
  WMEN_NEW=501,WMEN_OPEN,WMEN_INSFILE,WMEN_SAVEAS,WMEN_INSQUOT,WMEN_INSALTQUOT,
  WMEN_INSROT13,WMEN_EDIT,WMEN_CUT,WMEN_COPY,WMEN_PASTE,WMEN_DELETE,WMEN_SELECTALL,
  WMEN_SELECTNONE,WMEN_PASQUOT,WMEN_PASALTQUOT,WMEN_PASROT13,WMEN_SEARCH,WMEN_SEARCHAGAIN,
  WMEN_DICT,WMEN_STYLE_BOLD,WMEN_STYLE_ITALIC,WMEN_STYLE_UNDERLINE,
  WMEN_STYLE_COLORED,WMEN_EMOT0,WMEN_EMOT1,WMEN_EMOT2,WMEN_EMOT3,WMEN_UNDO,WMEN_REDO,
  WMEN_AUTOSP,WMEN_AUTOWRAP,WMEN_ADDFILE, WMEN_ADDCLIP, WMEN_ADDPGP,
  WMEN_DELSENT,WMEN_MDN,WMEN_ADDINFO,WMEN_IMPORT0,WMEN_IMPORT1,
  WMEN_IMPORT2,WMEN_SIGN0,WMEN_SIGN1,WMEN_SIGN2,WMEN_SIGN3,WMEN_SIGN4,WMEN_SIGN5,WMEN_SIGN6,WMEN_SIGN7,
  WMEN_SECUR0,WMEN_SECUR1,WMEN_SECUR2,WMEN_SECUR3,WMEN_SECUR4,WMEN_INSUUCODE,
  WMEN_SENDNOW,WMEN_QUEUE,WMEN_SAVEASDRAFT,WMEN_CLOSE,WMEN_SWITCH1,WMEN_SWITCH2,WMEN_SWITCH3,
  WMEN_FFONT,WMEN_TSTYLE,WMEN_TCOLOR,WMEN_CC,WMEN_BCC,WMEN_REPLYTO
};

// style origin IDs
enum
{
  ORIGIN_MENU=0,
  ORIGIN_TOOLBAR
};

/* Private Functions */
/// WhichEncodingForFile
//  Determines best MIME encoding mode for a file
static enum Encoding WhichEncodingForFile(const char *fname,
                                          const char *ctype,
                                          const struct MailServerNode *msn)
{
  // default to base64 encoding, as this is able to transport anything
  enum Encoding encoding = ENC_B64;

  ENTER();

  // we make sure that the following content-types get always encoded via base64
  if(strnicmp(ctype, "image/", 6) != 0 &&
     strnicmp(ctype, "audio/", 6) != 0 &&
     strnicmp(ctype, "video/", 6) != 0)
  {
    FILE *fh;

    if((fh = fopen(fname, "r")) != NULL)
    {
      int c;
      int linesize = 0;
      int total = 0;
      int unsafechars = 0;
      int binarychars = 0;
      int longlines = 0;

      setvbuf(fh, NULL, _IOFBF, SIZE_FILEBUF);

      // if there is no special stuff within the file we can break out
      // telling the caller that there is no encoding needed.
      encoding = ENC_7BIT;

      // scan until end of file
      while((c = fgetc(fh)) != EOF)
      {
        linesize++; // count the characters to get linelength
        total++;    // count the total number of scanned characters

        // first we check if this is a linebreak
        if(c == '\n')
        {
          // (RFC 821) restricts 7bit lines to a maximum of 1000 characters
          // so we have to use QP or base64 later on.
          // but RFC 2822 says that lines should not be longer than 998 chars,
          // so we take this one
          if(linesize > 998)
            longlines++;

          linesize = 0;
        }
        else if (c > 127)
          unsafechars++; // count the number of non 7bit ASCII chars
        else if (c < 32 && c != '\t')
          binarychars++; // count the number of chars used in binaries.

        // if we successfully scanned 4000 bytes out of the file and found enough
        // data we break out here. We have to at least find some long lines or
        // we have to scan the whole part.
        if(total > 4000 && longlines > 0)
          break;
      }

      // take care of no LF within the first 4000 bytes
      if(linesize > 998 && longlines == 0)
        longlines++;

      fclose(fh);

      D(DBF_MIME, "EncodingTest [%s] t:%ld l:%ld b:%ld", fname, total, longlines, binarychars);

      // now that we analyzed the file we have to decide which encoding to take
      if(longlines != 0 || unsafechars != 0 || binarychars != 0)
      {
        BOOL isApplication = (strnicmp(ctype, "application/", 12) == 0);

        if(binarychars == 0 && isApplication == FALSE)
        {
          // no binary characters and no binary attachment so far
          if(longlines != 0)
          {
            // use base64 encoding if there are too long lines, just to be safe
            encoding = ENC_B64;
          }
          else if(unsafechars != 0 || hasServer8bit(msn) == FALSE)
          {
            // use quoted-printable if there are non-7bit ASCII characters or
            // if the SMTP server does not support 7bit characters
            encoding = ENC_QP;
          }
          else
          {
            // otherwise use 8bit encoding
            encoding = ENC_8BIT;
          }
        }
        else
        {
          // if we end up here we have a file with either binary characters or the part
          // to be encoded is a binary attachment.
          encoding = ENC_B64;
        }
      }
    }
  }

  D(DBF_MIME, "identified suitable MIME encoding %ld for file [%s]", encoding, fname);

  RETURN(encoding);
  return encoding;
}

///
/// SetDefaultSecurity
static BOOL SetDefaultSecurity(struct Compose *comp, const Object *win)
{
  BOOL result = TRUE;
  enum Security security = SEC_NONE;
  BOOL FirstAddr = TRUE;
  BOOL warnedAlready = FALSE;
  char *CheckThese[3];
  unsigned int i;

  ENTER();

  // collect address pointers for easier iteration
  CheckThese[0] = comp->MailTo;
  CheckThese[1] = comp->MailCC;
  CheckThese[2] = comp->MailBCC;

  // go through all addresses
  for(i=0; i < ARRAY_SIZE(CheckThese); i++)
  {
    char *buf;

    // skip empty fields
    if(CheckThese[i] == NULL)
      continue;

    // copy string as strtok() will modify it
    if((buf = strdup(CheckThese[i])))
    {
      struct ABookNode *ab=NULL;
      enum Security currsec;
      char *in=buf;
      char *s;
      char *saveptr1;

      // loop through comma-separated addresses in string
      while((s = strtok_r(in, ",", &saveptr1)))
      {
        char *saveptr2;
        char *t = NULL;

        in = NULL;

        while((t = strtok_r(s, " ()<>", &saveptr2)))
        {
          s = NULL;

          if(strchr(t, '@'))
            break;
        }

        // can't find address for this entry - shouldn't happen
        if(t == NULL)
          continue;

        if(SearchABook(&G->abook, t, ASM_ADDRESS|ASM_USER|ASM_COMPLETE, &ab) != 0)
        {
          // get default from entry
          currsec = ab->DefSecurity;

          // check if PGP is present if security is/was set to sign/encrypt
          if(G->PGPVersion == 0 &&
             (currsec == SEC_SIGN || currsec == SEC_ENCRYPT || currsec == SEC_BOTH))
          {
            if(warnedAlready)
              currsec = SEC_NONE;
            else
            {
              char address[SIZE_LARGE];

              // warn the user about this exeptional situation
              if(MUI_Request(_app(win), win, MUIF_NONE, tr(MSG_WR_INVALIDSECURITY_TITLE),
                                                        tr(MSG_WR_INVALIDSECURITY_GADS),
                                                        tr(MSG_WR_INVALIDSECURITY),
                                                        BuildAddress(address, sizeof(address), ab->Address, ab->RealName)) != 0)
              {
                currsec = SEC_NONE;
              }
              else
              {
                result = FALSE;
                break;
              }

              warnedAlready = TRUE;
            }
          }
        }
        else
          currsec = SEC_NONE; // entry not in address book -> no security

        if(currsec != security)
        {
          // first address' setting is always used
          if(FirstAddr)
          {
            FirstAddr = FALSE;
            security = currsec; // assume as default
          }
          else
          {
            // conflict: two addresses have different defaults
            int res = MUI_Request(_app(win), win, MUIF_NONE, NULL, tr(MSG_WR_SECURITYREQ_GADS),
                                                                   tr(MSG_WR_SECURITYREQ));

            switch(res)
            {
              case 0:
                result = FALSE;
              break;

              case 1:
                security = SEC_NONE;
              break;

              case 2:
                security = SEC_SIGN;
              break;

              case 3:
                security = SEC_ENCRYPT;
              break;

              case 4:
                security = SEC_BOTH;
              break;
            }

            break;  // terminate recipient loop
          }
        }
      }

      free(buf);
    }
  }

  comp->Security = security;

  RETURN(result);
  return result;
}

///
/// BuildPartsList
//  Builds message parts from attachment list
static struct WritePart *BuildPartsList(struct WriteMailData *wmData, BOOL delTemp)
{
  struct WritePart *first;

  ENTER();

  // create the first MIME part
  if((first = NewMIMEpart(wmData)) != NULL)
  {
    int i;
    struct WritePart *p;

    p = first;
    p->IsAttachment = FALSE;
    p->IsTemp = TRUE;
    p->EncType = WhichEncodingForFile(p->Filename, p->ContentType, wmData->identity->smtpServer);

    // now walk through our attachment list
    // and create additional parts
    for(i=0; ;i++)
    {
      struct Attach *att = NULL;
      struct WritePart *np = NULL;

      DoMethod(wmData->window, METHOD(GetAttachment), i, &att);
      if(att == NULL)
        break;

      // we check if the file from the attachment list
      // still exists and is readable
      if(FileExists(att->FilePath) == TRUE)
      {
        // create a new MIME part for the attachment
        if((np = NewMIMEpart(wmData)) != NULL)
        {
          // link the two parts together
          p->Next = np;

          // and now set the information for the new part
          np->ContentType  = att->ContentType;
          np->Filename     = att->FilePath;
          np->Description  = att->Description;
          np->Name         = att->Name;
          np->Size         = att->Size;
          np->IsAttachment = TRUE;
          np->IsTemp       = att->IsTemp;

          // find out which encoding to use for the attachment
          np->EncType = WhichEncodingForFile(np->Filename, np->ContentType, wmData->identity->smtpServer);

          // for textual attachments we try to identify true UTF8 texts
          // everything else will be declared as simple ASCII according to ISO-8859-1
          if(strnicmp(np->ContentType, "text", 4) == 0 || strnicmp(np->ContentType, "message", 7) == 0)
          {
            char *buf;

            if((buf = FileToBuffer(np->Filename, NULL)) != NULL)
            {
              if(IsUTF8String(buf) == TRUE)
              {
                D(DBF_MAIL, "contents of '%s' are UTF8 encoded", np->Filename);
                np->Codeset = CodesetsFind((char *)"UTF8", TAG_DONE);
              }
              else
              {
                D(DBF_MAIL, "contents of '%s' seem to be plain ASCII", np->Filename);
                np->Codeset = CodesetsFind((char *)"ISO-8859-1", TAG_DONE);
              }

              free(buf);
            }
          }

          p = np;
        }
      }
      else
      {
        W(DBF_MAIL, "file from attachment list doesn't exist anymore: '%s'");

        ER_NewError(tr(MSG_ER_MISSINGATTFILE), att->FilePath);
      }

      if(np == NULL)
      {
        // an error occurred as we couldn't create a new part
        FreePartsList(first, delTemp);
        first = NULL;

        break;
      }
    }
  }

  RETURN(first);
  return first;
}

///
/// TransformText
//  Inserts or pastes text as plain, ROT13, uuencoded or quoted text
static char *TransformText(const char *source, const enum TransformMode mode, const char *qtext)
{
  char *dest = NULL;
  LONG size;

  ENTER();

  if(ObtainFileInfo(source, FI_SIZE, &size) == TRUE && size > 0)
  {
    int qtextlen = strlen(qtext);
    BOOL quote = (mode == ED_INSQUOT || mode == ED_PASQUOT ||
                  mode == ED_INSALTQUOT || mode == ED_PASALTQUOT);

    size += SIZE_DEFAULT;

    if(quote)
      size += size/20*qtextlen;

    if(mode == ED_INSUUCODE)
      size += strlen(FilePart(qtext))+12+7; // for the "begin 644 XXX" and "end passage

    if((dest = calloc(size, 1)) != NULL)
    {
      FILE *fp;

      if((fp = fopen(source, "r")) != NULL)
      {
        int ch;
        int p=0;
        int pos=0;

        setvbuf(fp, NULL, _IOFBF, SIZE_FILEBUF);

        // in case the source is UUencoded text we have to add the
        // "begin 644 XXX" stuff in advance.
        if(mode == ED_INSUUCODE)
          p = snprintf(dest, size, "\nbegin 644 %s\n", FilePart(qtext));

        while((ch = fgetc(fp)) != EOF)
        {
          if(!pos && quote)
          {
            int i;

            if(p+qtextlen > size-2)
            {
              size += SIZE_LARGE;
              dest = realloc(dest, size);
              if(dest == NULL)
                break;
            }

            for(i=0; i < qtextlen; i++)
              dest[p++] = qtext[i];

            dest[p++] = ' ';
          }

          if(ch == '\n')
            pos = 0;
          else
            ++pos;

          if(mode == ED_INSROT13 || mode == ED_PASROT13)
          {
            if(ch >= 'a' && ch <= 'z' && ((ch += 13) > 'z'))
              ch -= 26;

            if(ch >= 'A' && ch <= 'Z' && ((ch += 13) > 'Z'))
              ch -= 26;
          }

          if(p > size-3)
          {
            size += SIZE_LARGE;
            dest = realloc(dest, size);
            if(dest == NULL)
              break;
          }

          dest[p++] = ch;
        }

        // a realloc call might have failed, so better check this
        if(dest != NULL)
          dest[p] = '\0';

        // in case the source is UUencoded text we have to add the
        // "end" stuff at the end of the text as well
        if(mode == ED_INSUUCODE)
          strlcat(dest, "``\nend\n", size);

        fclose(fp);
      }
    }
  }

  RETURN(dest);
  return dest;
}

///
/// FreeCompose
// free a compose structure
static void FreeCompose(struct Compose *comp)
{
  ENTER();

  if(comp->MailReplyTo != NULL)
    free(comp->MailReplyTo);

  if(comp->MailFollowupTo != NULL)
    free(comp->MailFollowupTo);

  LEAVE();
}

///

/* Overloaded Methods */
/// OVERLOAD(OM_NEW)
OVERLOAD(OM_NEW)
{
  ULONG i;
  struct Data *data;
  struct Data *tmpData;
  static const char *rtitles[4] = { NULL, NULL, NULL, NULL };
  static const char *security[SEC_MAXDUMMY+1];
  static const char *priority[4];

  ENTER();

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

  rtitles[0] = tr(MSG_Message);
  rtitles[1] = tr(MSG_Attachments);
  rtitles[2] = tr(MSG_Options);
  rtitles[3] = NULL;

  security[SEC_NONE]    = tr(MSG_WR_SecNone);
  security[SEC_SIGN]    = tr(MSG_WR_SecSign);
  security[SEC_ENCRYPT] = tr(MSG_WR_SecEncrypt);
  security[SEC_BOTH]    = tr(MSG_WR_SecBoth);
  security[SEC_DEFAULTS]= tr(MSG_WR_SecDefaults);
  security[SEC_MAXDUMMY]= NULL;

  priority[0] = tr(MSG_WR_ImpHigh);
  priority[1] = tr(MSG_WR_ImpNormal);
  priority[2] = tr(MSG_WR_ImpLow);
  priority[3] = NULL;

  // set some default values
  data->useFixedFont = C->UseFixedFontWrite;
  data->useTextColors = C->UseTextColorsWrite;
  data->useTextStyles = C->UseTextStylesWrite;

  // before we create all objects of this new write window we have to
  // check which number we can set for this window. Therefore we search in our
  // current WriteMailData list and check which number we can give this window
  i = 0;
  do
  {
    struct WriteMailData *wmData;
    BOOL found = FALSE;

    IterateList(&G->writeMailDataList, struct WriteMailData *, wmData)
    {
      if(wmData->window != NULL &&
         xget(wmData->window, MUIA_WriteWindow_Num) == i)
      {
        found = TRUE;
        break;
      }
    }

    // if we didn't find a window with the current ID then we can choose it as
    // our WriteWindow ID
    if(found == FALSE)
    {
      D(DBF_GUI, "free write window number %ld found.", i);
      data->windowNumber = i;
      snprintf(data->windowNumberStr, sizeof(data->windowNumberStr), "%d", (int)i);

      break;
    }

    i++;
  }
  while(TRUE);

  // allocate the new writeMailData structure
  if((data->wmData = AllocWriteMailData()) != NULL)
  {
    Object *menuStripObject = NULL;
    Object *slider;
    struct TagItem *tags = inittags(msg);
    struct TagItem *tag;
    struct UserIdentityNode *uin;

    // check for some tags present at OM_NEW
    while((tag = NextTagItem((APTR)&tags)) != NULL)
    {
      switch(tag->ti_Tag)
      {
        case ATTR(Mode): data->wmData->mode = (enum NewMailMode)tag->ti_Data; break;
        case ATTR(Quiet): data->wmData->quietMode = (BOOL)tag->ti_Data; break;
      }
    }

    // set user identity node
    uin = data->wmData->identity;

    D(DBF_GUI, "write window mode: %08lx", data->wmData->mode);

    // if this write window is processed in NMM_REDIRECT mode we create
    // a slightly different graphical interface and hide all other things
    if(data->wmData->mode == NMM_REDIRECT)
    {
      // create a minimal menu strip for the redirect window which just consists
      // of the standard editing operations and shortcuts for the 4 buttons
      // in this window. Without this menu strip copying and pasteing would be
      // impossible, as the BetterString objects do NOT handle key presses
      // themselves anymore.
      // A list of all currently used shortcuts follows below for the full
      // menu.
      menuStripObject = MenustripObject,
        MenuChild, MenuObject,
          MUIA_Menu_Title, tr(MSG_WR_Text),
          MUIA_Menu_CopyStrings, FALSE,
          MenuChild, Menuitem(tr(MSG_WR_MSENDNOW), "S", TRUE, FALSE, WMEN_SENDNOW),
          MenuChild, Menuitem(tr(MSG_WR_MSENDLATER), "L", TRUE, FALSE, WMEN_QUEUE),
          MenuChild, Menuitem(tr(MSG_WR_MSAVEASDRAFT), "H", TRUE, FALSE, WMEN_SAVEASDRAFT),
          MenuChild, Menuitem(tr(MSG_WR_MCLOSE), "W", TRUE, FALSE, WMEN_CLOSE),
        End,
        MenuChild, MenuObject,
          MUIA_Menu_Title, tr(MSG_WR_Edit),
          MUIA_Menu_CopyStrings, FALSE,
          MenuChild, Menuitem(tr(MSG_WR_MUndo), "Z", TRUE, FALSE, WMEN_UNDO),
          MenuChild, Menuitem(tr(MSG_WR_Redo), "Y", TRUE, FALSE, WMEN_REDO),
          MenuChild, MenuBarLabel,
          MenuChild, Menuitem(tr(MSG_WR_MCut), "X", TRUE, FALSE, WMEN_CUT),
          MenuChild, Menuitem(tr(MSG_WR_MCopy), "C", TRUE, FALSE, WMEN_COPY),
          MenuChild, Menuitem(tr(MSG_WR_MPaste), "V", TRUE, FALSE, WMEN_PASTE),
          MenuChild, Menuitem(tr(MSG_WR_DELETE), NULL, TRUE, FALSE, WMEN_DELETE),
          MenuChild, MenuBarLabel,
          MenuChild, Menuitem(tr(MSG_WR_SELECTALL), "A", TRUE, FALSE, WMEN_SELECTALL),
          MenuChild, Menuitem(tr(MSG_WR_SELECTNONE), NULL, TRUE, FALSE, WMEN_SELECTNONE),
        End,
      End;

      if(menuStripObject != NULL)
      {
        obj = DoSuperNew(cl, obj,
          MUIA_Window_Title, "",
          MUIA_Window_ScreenTitle, "",
          MUIA_HelpNode, "Windows/Writewindow",
          MUIA_Window_ID, MAKE_ID('W','R','I','B'),
          MUIA_Window_AppWindow, FALSE,
          MUIA_Window_Menustrip, menuStripObject,
          WindowContents, VGroup,

            Child, data->GR_HEADER = ColGroup(2),
              Child, data->LB_FROM = Label(tr(MSG_WR_REDIRECT_FROM)),
              Child, data->CY_FROM = IdentityChooserObject,
                MUIA_ControlChar, ShortCut(tr(MSG_WR_REDIRECT_FROM)),
              End,

              Child, data->LB_FROM_OVERRIDE = HGroup,
                MUIA_Group_PageMode, TRUE,
                MUIA_HorizWeight, 0,
                Child, HSpace(-1),
                Child, Label(tr(MSG_WR_From)),
              End,
              Child, data->GR_FROM_OVERRIDE = MakeAddressField(&data->ST_FROM_OVERRIDE, NULL, NULL, ABM_FROM, data->windowNumber, AFF_EXTERNAL_SHORTCUTS),

              Child, data->LB_TO = Label(tr(MSG_WR_REDIRECT_TO)),
              Child, data->GR_TO = MakeAddressField(&data->ST_TO, tr(MSG_WR_REDIRECT_TO), MSG_HELP_WR_ST_TO, ABM_TO, data->windowNumber, AFF_ALLOW_MULTI|AFF_EXTERNAL_SHORTCUTS),

              Child, data->LB_CC = Label(tr(MSG_WR_REDIRECT_CC)),
              Child, data->GR_CC = MakeAddressField(&data->ST_CC, tr(MSG_WR_REDIRECT_CC), MSG_HELP_WR_ST_CC, ABM_CC, data->windowNumber, AFF_ALLOW_MULTI|AFF_EXTERNAL_SHORTCUTS),

              Child, data->LB_BCC = Label(tr(MSG_WR_REDIRECT_BCC)),
              Child, data->GR_BCC = MakeAddressField(&data->ST_BCC, tr(MSG_WR_REDIRECT_BCC), MSG_HELP_WR_ST_BCC, ABM_BCC, data->windowNumber, AFF_ALLOW_MULTI|AFF_EXTERNAL_SHORTCUTS),
            End,

            Child, ColGroup(5),
              Child, data->BT_SEND  = MakeButton(tr(MSG_WR_SENDNOW)),
              Child, data->BT_QUEUE = MakeButton(tr(MSG_WR_SENDLATER)),
              Child, HVSpace,
              Child, data->BT_SAVEASDRAFT = MakeButton(tr(MSG_WR_SAVEASDRAFT)),
              Child, data->BT_CANCEL = MakeButton(tr(MSG_WR_CANCEL)),
            End,

          End,
          TAG_MORE, inittags(msg));
      }

      if(obj != NULL)
      {
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_SENDNOW,     obj, 3, METHOD(ComposeMail), WRITE_SEND, TRUE);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_QUEUE,       obj, 3, METHOD(ComposeMail), WRITE_QUEUE, TRUE);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_SAVEASDRAFT, obj, 3, METHOD(ComposeMail), WRITE_DRAFT, TRUE);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_CLOSE,       obj, 1, METHOD(CancelAction));
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_CUT,         obj, 2, METHOD(EditActionPerformed), EA_CUT);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_COPY,        obj, 2, METHOD(EditActionPerformed), EA_COPY);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_PASTE,       obj, 2, METHOD(EditActionPerformed), EA_PASTE);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_DELETE,      obj, 2, METHOD(EditActionPerformed), EA_DELETE);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_SELECTALL,   obj, 2, METHOD(EditActionPerformed), EA_SELECTALL);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_SELECTNONE,  obj, 2, METHOD(EditActionPerformed), EA_SELECTNONE);
      }
    }
    else
    {
      // now we create the Menustrip object with all the menu items
      // and corresponding shortcuts
      //
      // The follwong shortcut list should help to identify the hard-coded
      // shortcuts:
      //
      //  A   reserved for 'Select All' operation (WMEN_SELECTALL)
      //  B   Bold soft-style (WMEN_STYLE_BOLD)
      //  C   reserved for 'Copy' operation (WMEN_COPY)
      //  D   Dictonary (WMEN_DICT)
      //  E   Launch editor (WMEN_EDIT)
      //  F   Find/Search (WMEN_SEARCH)
      //  G   Search again (WMEN_SEARCHAGAIN)
      //  H   Save mail as draft (reserved by YAM.cd)
      //  I   Italic soft-style (WMEN_STYLE_BOLD)
      //  J
      //  K   Colored soft-style (WMEN_STYLE_COLORED)
      //  L   Send later (reserved by YAM.cd)
      //  M
      //  N   New mail (WMEN_NEW)
      //  O   Open file (WMEN_OPEN)
      //  P   Insert as plain text (WMEN_PLAIN)
      //  Q   Insert as quoted text (WMEN_PASQUOT)
      //  R   Add file as attachment (WMEN_ADDFILE)
      //  S   Send mail (WMEN_SEND)
      //  T   Enable/Disable Text Colors (RMEN_TCOLOR)
      //  U   Underline soft-style (WMEN_STYLE_UNDERLINE)
      //  V   reserved for 'Paste' operation (WMEN_PASTE)
      //  W   Close window (WMEN_CLOSE)
      //  X   reserved for 'Cut' operation (WMEN_CUT)
      //  Y   reserved for 'Redo' operation (WMEN_REDO)
      //  Z   reserved for 'Undo' operation (WMEN_UNDO)
      //  1   Switch to Message view (WMEN_SWITCH1)
      //  2   Switch to Attachment view (WMEN_SWITCH2)
      //  3   Switch to Options view (WMEN_SWITCH3)
      //  4   Show/Hide Cc Address-Field (WMEN_CC)
      //  5   Show/Hide Bcc Address-Field (WMEN_BCC)
      //  6   Show/Hide Reply-To Address-Field (WMEN_REPLYTO)
      //  0   Use no signature (WMEN_SIGN0)
      //  7   Use signature 1 (WMEN_SIGN1)
      //  8   Use signature 2 (WMEN_SIGN2)
      //  9   Use signature 3 (WMEN_SIGN3)

      menuStripObject = MenustripObject,
        MenuChild, MenuObject,
          MUIA_Menu_Title, tr(MSG_WR_Text),
          MUIA_Menu_CopyStrings, FALSE,
          MenuChild, Menuitem(tr(MSG_New), "N", TRUE, FALSE, WMEN_NEW),
          MenuChild, Menuitem(tr(MSG_Open), "O", TRUE, FALSE, WMEN_OPEN),
          MenuChild, MenuitemObject,
            MUIA_Menuitem_Title, tr(MSG_WR_InsertAs),
            MUIA_Menuitem_CopyStrings, FALSE,
            MenuChild, Menuitem(tr(MSG_WR_Plain), "P", TRUE, FALSE, WMEN_INSFILE),
            MenuChild, Menuitem(tr(MSG_WR_Quoted), NULL, TRUE, FALSE, WMEN_INSQUOT),
            MenuChild, Menuitem(tr(MSG_WR_AltQuoted), NULL, TRUE, FALSE, WMEN_INSALTQUOT),
            MenuChild, Menuitem(tr(MSG_WR_ROT13), NULL, TRUE, FALSE, WMEN_INSROT13),
            MenuChild, Menuitem(tr(MSG_WR_UUCODE), NULL, TRUE, FALSE, WMEN_INSUUCODE),
          End,
          MenuChild, MenuBarLabel,
          MenuChild, Menuitem(tr(MSG_WR_LaunchEd), "E", C->Editor[0] != '\0', FALSE, WMEN_EDIT),
          MenuChild, MenuBarLabel,
          MenuChild, Menuitem(tr(MSG_SaveAs), NULL, TRUE, FALSE, WMEN_SAVEAS),
          MenuChild, MenuBarLabel,
          MenuChild, Menuitem(tr(MSG_WR_MSENDNOW), "S", TRUE, FALSE, WMEN_SENDNOW),
          MenuChild, Menuitem(tr(MSG_WR_MSENDLATER), "L", TRUE, FALSE, WMEN_QUEUE),
          MenuChild, Menuitem(tr(MSG_WR_MSAVEASDRAFT), "H", TRUE, FALSE, WMEN_SAVEASDRAFT),
          MenuChild, Menuitem(tr(MSG_WR_MCLOSE), "W", TRUE, FALSE, WMEN_CLOSE),
        End,
        MenuChild, MenuObject,
          MUIA_Menu_Title, tr(MSG_WR_Edit),
          MUIA_Menu_CopyStrings, FALSE,
          MenuChild, Menuitem(tr(MSG_WR_MUndo), "Z", TRUE, FALSE, WMEN_UNDO),
          MenuChild, Menuitem(tr(MSG_WR_Redo), "Y", TRUE, FALSE, WMEN_REDO),
          MenuChild, MenuBarLabel,
          MenuChild, Menuitem(tr(MSG_WR_MCut), "X", TRUE, FALSE, WMEN_CUT),
          MenuChild, Menuitem(tr(MSG_WR_MCopy), "C", TRUE, FALSE, WMEN_COPY),
          MenuChild, Menuitem(tr(MSG_WR_MPaste), "V", TRUE, FALSE, WMEN_PASTE),
          MenuChild, MenuitemObject,
            MUIA_Menuitem_Title, tr(MSG_WR_PasteAs),
            MUIA_Menuitem_CopyStrings, FALSE,
            MenuChild, Menuitem(tr(MSG_WR_Quoted), "Q", TRUE, FALSE, WMEN_PASQUOT),
            MenuChild, Menuitem(tr(MSG_WR_AltQuoted), NULL, TRUE, FALSE, WMEN_PASALTQUOT),
            MenuChild, Menuitem(tr(MSG_WR_ROT13), NULL, TRUE, FALSE, WMEN_PASROT13),
          End,
          MenuChild, Menuitem(tr(MSG_WR_DELETE), NULL, TRUE, FALSE, WMEN_DELETE),
          MenuChild, MenuBarLabel,
          MenuChild, Menuitem(tr(MSG_WR_SELECTALL), "A", TRUE, FALSE, WMEN_SELECTALL),
          MenuChild, Menuitem(tr(MSG_WR_SELECTNONE), NULL, TRUE, FALSE, WMEN_SELECTNONE),
          MenuChild, MenuBarLabel,
          MenuChild, Menuitem(tr(MSG_WR_SEARCH), "F", TRUE, FALSE, WMEN_SEARCH),
          MenuChild, Menuitem(tr(MSG_WR_SEARCH_AGAIN), "G", TRUE, FALSE, WMEN_SEARCHAGAIN),
          MenuChild, MenuBarLabel,
          MenuChild, Menuitem(tr(MSG_WR_Dictionary), "D", TRUE, FALSE, WMEN_DICT),
          MenuChild, MenuitemObject,
            MUIA_Menuitem_Title,tr(MSG_WR_Textstyle),
            MUIA_Menuitem_CopyStrings, FALSE,
            MenuChild, data->MI_BOLD = MenuitemCheck(tr(MSG_WR_Bold), "B", TRUE, FALSE, TRUE, 0, WMEN_STYLE_BOLD),
            MenuChild, data->MI_ITALIC = MenuitemCheck(tr(MSG_WR_Italic), "I", TRUE, FALSE, TRUE, 0, WMEN_STYLE_ITALIC),
            MenuChild, data->MI_UNDERLINE = MenuitemCheck(tr(MSG_WR_Underlined), "U", TRUE, FALSE, TRUE, 0, WMEN_STYLE_UNDERLINE),
            MenuChild, data->MI_COLORED = MenuitemCheck(tr(MSG_WR_Colored), "K", TRUE, FALSE, TRUE, 0, WMEN_STYLE_COLORED),
          End,
          MenuChild, MenuitemObject,
            MUIA_Menuitem_Title,tr(MSG_WR_Emoticons),
            MUIA_Menuitem_CopyStrings, FALSE,
            MenuChild, Menuitem(tr(MSG_WR_Happy), NULL, TRUE, FALSE, WMEN_EMOT0),
            MenuChild, Menuitem(tr(MSG_WR_Indifferent), NULL, TRUE, FALSE, WMEN_EMOT1),
            MenuChild, Menuitem(tr(MSG_WR_Sad), NULL, TRUE, FALSE, WMEN_EMOT2),
            MenuChild, Menuitem(tr(MSG_WR_Ironic), NULL, TRUE, FALSE, WMEN_EMOT3),
          End,
          MenuChild, MenuBarLabel,
          MenuChild, data->MI_AUTOSPELL = MenuitemCheck(tr(MSG_WR_SpellCheck), NULL, TRUE, FALSE, TRUE, 0, WMEN_AUTOSP),
          MenuChild, data->MI_AUTOWRAP = MenuitemCheck(tr(MSG_WR_AUTOWRAP), NULL, TRUE, FALSE, TRUE, 0, WMEN_AUTOWRAP),
        End,
        MenuChild, MenuObject,
          MUIA_Menu_Title, tr(MSG_Attachments),
          MUIA_Menu_CopyStrings, FALSE,
          MenuChild, Menuitem(tr(MSG_WR_MAddFile), "R", TRUE, FALSE, WMEN_ADDFILE),
          MenuChild, Menuitem(tr(MSG_WR_AddCB), NULL, TRUE, FALSE, WMEN_ADDCLIP),
          MenuChild, Menuitem(tr(MSG_WR_AddKey), NULL, G->PGPVersion != 0, FALSE, WMEN_ADDPGP),
        End,
        MenuChild, MenuObject,
          MUIA_Menu_Title, tr(MSG_WR_VIEW),
          MUIA_Menu_CopyStrings, FALSE,
          MenuChild, Menuitem(tr(MSG_WR_MSWITCH_MSG), "1", TRUE, FALSE, WMEN_SWITCH1),
          MenuChild, Menuitem(tr(MSG_WR_MSWITCH_ATT), "2", TRUE, FALSE, WMEN_SWITCH2),
          MenuChild, Menuitem(tr(MSG_WR_MSWITCH_OPT), "3", TRUE, FALSE, WMEN_SWITCH3),
          MenuChild, data->MN_CC = MenuitemCheck(tr(MSG_WR_MCCADDRFIELD), "4", TRUE, C->ShowRcptFieldCC, TRUE, 0, WMEN_CC),
          MenuChild, data->MN_BCC = MenuitemCheck(tr(MSG_WR_MBCCADDRFIELD), "5", TRUE, C->ShowRcptFieldBCC, TRUE, 0, WMEN_BCC),
          MenuChild, data->MN_REPLYTO = MenuitemCheck(tr(MSG_WR_MREPLYTOADDRFIELD),"6", TRUE, C->ShowRcptFieldReplyTo, TRUE, 0, WMEN_REPLYTO),
        End,
        MenuChild, MenuObject,
          MUIA_Menu_Title, tr(MSG_Options),
          MUIA_Menu_CopyStrings, FALSE,
          MenuChild, data->MI_DELSENT = MenuitemCheck(tr(MSG_WR_MDelSend), NULL, TRUE, FALSE, TRUE, 0, WMEN_DELSENT),
          MenuChild, data->MI_MDN = MenuitemCheck(tr(MSG_WR_MReceipt), NULL, TRUE, FALSE, TRUE, 0, WMEN_MDN),
          MenuChild, data->MI_ADDINFO = MenuitemCheck(tr(MSG_WR_MAddInfo), NULL, TRUE, FALSE, TRUE, 0, WMEN_ADDINFO),
          MenuChild, MenuitemObject,
            MUIA_Menuitem_Title,tr(MSG_WR_MImportance),
            MenuChild, MenuitemCheck(priority[0], NULL, TRUE, FALSE, TRUE, 0x06, WMEN_IMPORT0),
            MenuChild, MenuitemCheck(priority[1], NULL, TRUE, TRUE,  TRUE, 0x05, WMEN_IMPORT1),
            MenuChild, MenuitemCheck(priority[2], NULL, TRUE, FALSE, TRUE, 0x03, WMEN_IMPORT2),
          End,
          MenuChild, data->MI_SIGNATURE = MenuitemObject,
            MUIA_Menuitem_Title,tr(MSG_CO_CrdSignature),
            MUIA_Menuitem_CopyStrings, FALSE,
          End,
          MenuChild, MenuitemObject,
            MUIA_Menuitem_Title,tr(MSG_CO_CrdSecurity),
            MUIA_Menuitem_CopyStrings, FALSE,
            MenuChild, MenuitemCheck(security[SEC_NONE], NULL, TRUE, FALSE, TRUE, 0x3E, WMEN_SECUR0),
            MenuChild, MenuitemCheck(security[SEC_SIGN], NULL, G->PGPVersion != 0, FALSE, TRUE, 0x3D, WMEN_SECUR1),
            MenuChild, MenuitemCheck(security[SEC_ENCRYPT], NULL, G->PGPVersion != 0, FALSE, TRUE, 0x3B, WMEN_SECUR2),
            MenuChild, MenuitemCheck(security[SEC_BOTH], NULL, G->PGPVersion != 0, FALSE, TRUE, 0x37, WMEN_SECUR3),
            MenuChild, MenuitemCheck(security[SEC_DEFAULTS], NULL, TRUE, TRUE, TRUE, 0x2F, WMEN_SECUR4),
          End,
          MenuChild, MenuBarLabel,
          MenuChild, data->MI_FFONT  = MenuitemCheck(tr(MSG_WR_FIXEDFONT),  NULL, TRUE, data->useFixedFont,  TRUE, 0, WMEN_FFONT),
          MenuChild, data->MI_TCOLOR = MenuitemCheck(tr(MSG_WR_TEXTCOLORS), "T",  TRUE, data->useTextColors, TRUE, 0, WMEN_TCOLOR),
          MenuChild, data->MI_TSTYLE = MenuitemCheck(tr(MSG_WR_TEXTSTYLES), NULL, TRUE, data->useTextStyles, TRUE, 0, WMEN_TSTYLE),
        End,
      End;

      // create the slider for the text editor
      slider = ScrollbarObject, End;

      // create the write window object
      if(menuStripObject != NULL && slider != NULL)
      {
        obj = DoSuperNew(cl, obj,

          MUIA_Window_Title, "",
          MUIA_Window_ScreenTitle, "",
          MUIA_HelpNode, "Windows/Writewindow",
          MUIA_Window_ID, MAKE_ID('W','R','W', data->windowNumber),
          MUIA_Window_AppWindow, TRUE,
          MUIA_Window_Menustrip, menuStripObject,
          WindowContents, VGroup,
            Child, data->RG_PAGE = RegisterGroup(rtitles),
              MUIA_CycleChain, TRUE,

              // Message
              Child, VGroup,
                MUIA_HelpNode, "Windows/Writewindow#Messagesheet",

                Child, HGroup,
                  GroupSpacing(0),
                  Child, HGroup,
                    MUIA_HorizWeight, 75,

                    Child, data->GR_HEADER = ColGroup(2),
                      Child, data->LB_FROM = Label(tr(MSG_WR_From)),
                      Child, data->CY_FROM = IdentityChooserObject,
                        MUIA_ControlChar, ShortCut(tr(MSG_WR_From)),
                      End,

                      Child, data->LB_FROM_OVERRIDE = HGroup,
                        MUIA_Group_PageMode, TRUE,
                        MUIA_HorizWeight, 0,
                        Child, HSpace(-1),
                        Child, Label(tr(MSG_WR_From)),
                      End,
                      Child, data->GR_FROM_OVERRIDE = MakeAddressField(&data->ST_FROM_OVERRIDE, NULL, NULL, ABM_FROM, data->windowNumber, AFF_EXTERNAL_SHORTCUTS),

                      Child, data->LB_TO = Label(tr(MSG_WR_To)),
                      Child, data->GR_TO = MakeAddressField(&data->ST_TO, tr(MSG_WR_To), MSG_HELP_WR_ST_TO, ABM_TO, data->windowNumber, AFF_ALLOW_MULTI|AFF_EXTERNAL_SHORTCUTS),

                      Child, data->LB_CC = Label(tr(MSG_WR_CC)),
                      Child, data->GR_CC = MakeAddressField(&data->ST_CC, tr(MSG_WR_CC), MSG_HELP_WR_ST_CC, ABM_CC, data->windowNumber, AFF_ALLOW_MULTI|AFF_EXTERNAL_SHORTCUTS),

                      Child, data->LB_BCC = Label(tr(MSG_WR_BCC)),
                      Child, data->GR_BCC = MakeAddressField(&data->ST_BCC, tr(MSG_WR_BCC), MSG_HELP_WR_ST_BCC, ABM_BCC, data->windowNumber, AFF_ALLOW_MULTI|AFF_EXTERNAL_SHORTCUTS),

                      Child, data->LB_REPLYTO = Label(tr(MSG_WR_REPLYTO)),
                      Child, data->GR_REPLYTO = MakeAddressField(&data->ST_REPLYTO, tr(MSG_WR_REPLYTO), MSG_HELP_WR_ST_REPLYTO, ABM_REPLYTO, data->windowNumber, AFF_ALLOW_MULTI|AFF_EXTERNAL_SHORTCUTS),

                      Child, data->LB_SUBJECT = Label(tr(MSG_WR_Subject)),
                      Child, data->ST_SUBJECT = BetterStringObject,
                        StringFrame,
                        MUIA_BetterString_NoShortcuts, TRUE,
                        MUIA_String_MaxLen,            SIZE_SUBJECT,
                        MUIA_String_AdvanceOnCR,       TRUE,
                        MUIA_ControlChar,              ShortCut(tr(MSG_WR_Subject)),
                        MUIA_CycleChain,               TRUE,
                      End,
                    End,
                  End,
                  Child, data->GR_ATTACH_TINY = HGroup,
                    GroupSpacing(0),
                    MUIA_ShowMe, FALSE,
                    MUIA_HorizWeight, 25,
                    Child, NBalanceObject,
                      MUIA_Balance_Quiet, TRUE,
                    End,
                    Child, NListviewObject,
                      MUIA_CycleChain, TRUE,
                      MUIA_NListview_NList, data->LV_ATTACH_TINY = WriteAttachmentListObject,
                        MUIA_WriteAttachmentList_Tiny, TRUE,
                      End,
                    End,
                  End,
                End,

                Child, hasHideToolBarFlag(C->HideGUIElements) ?
                  (RectangleObject, MUIA_ShowMe, FALSE, End) :
                  (HGroup, GroupSpacing(0),
                    Child, HGroupV,
                      Child, data->TO_TOOLBAR = WriteWindowToolbarObject,
                      End,
                    End,

                    Child, hasHideXYFlag(C->HideGUIElements) ?
                      (HSpace(1)) :
                      (HGroup, GroupSpacing(0),
                        Child, RectangleObject,
                          MUIA_Rectangle_VBar, TRUE,
                          MUIA_FixWidth,       3,
                        End,
                        Child, VGroup,
                          GroupSpacing(0),
                          Child, VSpace(0),
                          Child, data->TX_POSI = TextObject,
                            MUIA_Weight,        0,
                            MUIA_Text_Contents, "000\n000",
                            MUIA_Text_Copy,     FALSE,
                            MUIA_Background,    MUII_RegisterBack,
                            MUIA_Frame,         MUIV_Frame_None,
                            MUIA_Font,          MUIV_Font_Tiny,
                          End,
                          Child, VSpace(0),
                        End,
                      End),
                End),

                Child, HGroup,
                  MUIA_HelpNode, "Windows/Writewindow#Internaleditor",
                  MUIA_Group_Spacing, 0,
                  Child, data->TE_EDIT = MailTextEditObject,
                    InputListFrame,
                    MUIA_CycleChain,                 TRUE,
                    MUIA_TextEditor_Slider,          slider,
                    MUIA_TextEditor_FixedFont,       C->UseFixedFontWrite,
                    MUIA_TextEditor_WrapMode,        MUIV_TextEditor_WrapMode_SoftWrap,
                    MUIA_TextEditor_WrapBorder,      C->EdWrapMode == EWM_EDITING ? C->EdWrapCol : 0,
                  //MUIA_TextEditor_ExportWrap,      C->EdWrapMode != EWM_OFF ? C->EdWrapCol : 0,
                    MUIA_TextEditor_ImportHook,      MUIV_TextEditor_ImportHook_Plain,
                    MUIA_TextEditor_ExportHook,      MUIV_TextEditor_ExportHook_NoStyle,
                    MUIA_MailTextEdit_CheckKeywords, C->AttachmentReminder,
                  End,
                  Child, slider,
                End,

                Child, data->GR_ATTACH_REMIND = HGroup,
                  MUIA_ShowMe, FALSE,
                  Child, Label(tr(MSG_ATTACHMENT_REMIND_INFO)),
                  Child, HSpace(0),
                  Child, data->BT_ATTACH_REMIND_ADD = MakeButton(tr(MSG_ATTACHMENT_REMIND_ADD)),
                  Child, data->BT_ATTACH_REMIND_LATER = MakeButton(tr(MSG_ATTACHMENT_REMIND_LATER)),
                  Child, data->BT_ATTACH_REMIND_NEVER = MakeCloseButton(),
                End,
              End,

              // Attachments
              Child, VGroup,
                MUIA_HelpNode, "Windows/Writewindow#Attachmentsheet",
                Child, NListviewObject,
                  MUIA_CycleChain, TRUE,
                  MUIA_NListview_NList, data->LV_ATTACH = WriteAttachmentListObject,
                  End,
                End,

                Child, ColGroup(5),
                  Child, data->BT_ADD     = MakeButton(tr(MSG_WR_Add)),
                  Child, data->BT_ADDPACK = MakeButton(tr(MSG_WR_AddPack)),
                  Child, data->BT_REMOVE  = MakeButton(tr(MSG_WR_REMOVE)),
                  Child, data->BT_RENAME  = MakeButton(tr(MSG_WR_RENAME)),
                  Child, data->BT_DISPLAY = MakeButton(tr(MSG_WR_Display)),
                End,

                Child, ColGroup(2),
                  Child, Label2(tr(MSG_WR_ContentType)),
                  Child, data->PO_CTYPE = MimeTypePopupObject,
                    MUIA_MimeTypePopup_ControlChar, ShortCut(tr(MSG_WR_ContentType)),
                  End,
                  Child, Label2(tr(MSG_WR_Description)),
                  Child, data->ST_DESC = BetterStringObject,
                    StringFrame,
                    MUIA_BetterString_NoShortcuts, TRUE,
                    MUIA_String_MaxLen,            SIZE_DEFAULT,
                    MUIA_String_AdvanceOnCR,       TRUE,
                    MUIA_ControlChar,              ShortCut(tr(MSG_WR_Description)),
                    MUIA_CycleChain,               TRUE,
                  End,
                End,
              End,

              // Options
              Child, VGroup,
                MUIA_HelpNode, "Windows/Writewindow#OptionssheetWindows",
                Child, ColGroup(2),
                  GroupFrameT(tr(MSG_Options)),

                  Child, Label(tr(MSG_WR_ExtraHeaders)),
                  Child, data->ST_EXTHEADER = BetterStringObject,
                    StringFrame,
                    MUIA_BetterString_NoShortcuts, TRUE,
                    MUIA_String_MaxLen,            SIZE_LARGE,
                    MUIA_String_AdvanceOnCR,       TRUE,
                    MUIA_ControlChar,              ShortCut(tr(MSG_WR_ExtraHeaders)),
                    MUIA_CycleChain,               TRUE,
                  End,

                  Child, Label(tr(MSG_WR_CHARSET)),
                  Child, data->PO_CODESET = CodesetPopupObject,
                    MUIA_CodesetPopup_ControlChar, tr(MSG_WR_CHARSET),
                  End,

                  Child, Label(tr(MSG_WR_Importance)),
                  Child, data->CY_IMPORTANCE = MakeCycle(priority, tr(MSG_WR_Importance)),

                  Child, Label(tr(MSG_WR_Signature)),
                  Child, data->CY_SIGNATURE = SignatureChooserObject,
                    MUIA_ControlChar, ShortCut(tr(MSG_WR_Signature)),
                  End,

                  Child, Label(tr(MSG_WR_PGPSECURITY)),
                  Child, data->CY_SECURITY = MakeCycle(security, tr(MSG_WR_PGPSECURITY)),

                  Child, HSpace(1),
                  Child, HBarT(tr(MSG_WR_SendOpt)), End,

                  Child, HSpace(1),
                  Child, MakeCheckGroup((Object **)&data->CH_DELSENT, tr(MSG_WR_DelSend)),

                  Child, HSpace(1),
                  Child, MakeCheckGroup((Object **)&data->CH_MDN,     tr(MSG_WR_Receipt)),

                  Child, HSpace(1),
                  Child, MakeCheckGroup((Object **)&data->CH_ADDINFO, tr(MSG_WR_AddInfo)),

                  Child, HVSpace,
                  Child, HVSpace,

                End,
              End,

            End,

            // Buttons
            Child, ColGroup(5),
              Child, data->BT_SEND        = MakeButton(tr(MSG_WR_SENDNOW)),
              Child, data->BT_QUEUE       = MakeButton(tr(MSG_WR_SENDLATER)),
              Child, HVSpace,
              Child, data->BT_SAVEASDRAFT = MakeButton(tr(MSG_WR_SAVEASDRAFT)),
              Child, data->BT_CANCEL      = MakeButton(tr(MSG_WR_CANCEL)),
            End,
          End,

        TAG_MORE, inittags(msg));
      }
    }

    // check if object creation worked as expected
    if(obj != NULL)
    {
      char filename[SIZE_PATHFILE];

      if((data = (struct Data *)INST_DATA(cl,obj)) == NULL)
      {
        FreeWriteMailData(tmpData->wmData);
        free(tmpData);
        RETURN(0);
        return 0;
      }

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

      // place this newly created window to the writeMailData structure aswell
      data->wmData->window = obj;

      // Add the window to the application object
      DoMethod(G->App, OM_ADDMEMBER, obj);

      // set notifies and default options in case this
      // write window is not in REDIRECT mode
      if(data->wmData->mode != NMM_REDIRECT)
      {
        // prepare a first initial window title string
        snprintf(data->windowTitle, sizeof(data->windowTitle), "[%d] %s: ", data->windowNumber+1, tr(MSG_WR_WriteWT));

        // set the default window object
        xset(obj, MUIA_Window_DefaultObject, data->ST_TO,
                  MUIA_Window_ActiveObject,  data->ST_TO);

        // set the key focus attributes of the TO and SUBJECT gadgets
        xset(data->ST_TO, MUIA_BetterString_KeyDownFocus, data->ST_SUBJECT,
                          MUIA_RecipientString_ReplyToString, data->ST_REPLYTO);

        xset(data->ST_SUBJECT, MUIA_BetterString_KeyUpFocus, data->ST_TO,
                               MUIA_BetterString_KeyDownFocus, data->TE_EDIT);

        // set certain default check settings of the autospell and autowrap menu items
        set(data->MI_AUTOSPELL, MUIA_Menuitem_Checked, xget(data->TE_EDIT, MUIA_TextEditor_TypeAndSpell));
        set(data->MI_AUTOWRAP,  MUIA_Menuitem_Checked, xget(data->TE_EDIT, MUIA_TextEditor_WrapBorder) > 0);

        // set the codeset popupbutton string list contents
        nnset(data->PO_CODESET, MUIA_CodesetPopup_Codeset, C->DefaultWriteCodeset);

        // put the importance cycle gadget into the cycle group
        set(data->CY_IMPORTANCE, MUIA_Cycle_Active, TRUE);

        // disable certain GUI elements per default
        DoMethod(_app(obj), MUIM_MultiSet,  MUIA_Disabled, TRUE, data->PO_CTYPE,
                                                                 data->ST_DESC,
                                                                 data->BT_REMOVE,
                                                                 data->BT_RENAME,
                                                                 data->BT_DISPLAY,
                                                                 NULL);

        // set the help elements of our GUI gadgets
        SetHelp(data->ST_SUBJECT,     MSG_HELP_WR_ST_SUBJECT);
        SetHelp(data->BT_ADD,         MSG_HELP_WR_BT_ADD);
        SetHelp(data->BT_ADDPACK,     MSG_HELP_WR_BT_ADDPACK);
        SetHelp(data->BT_REMOVE,      MSG_HELP_WR_BT_REMOVE);
        SetHelp(data->BT_DISPLAY,     MSG_HELP_WR_BT_DISPLAY);
        SetHelp(data->PO_CTYPE,       MSG_HELP_WR_ST_CTYPE);
        SetHelp(data->ST_DESC,        MSG_HELP_WR_ST_DESC);
        SetHelp(data->ST_EXTHEADER,   MSG_HELP_WR_ST_EXTHEADER);
        SetHelp(data->CH_DELSENT,     MSG_HELP_WR_CH_DELSEND);
        SetHelp(data->CH_MDN,         MSG_HELP_WR_CH_RECEIPT);
        SetHelp(data->CH_ADDINFO,     MSG_HELP_WR_CH_ADDINFO);
        SetHelp(data->CY_IMPORTANCE,  MSG_HELP_WR_CY_IMPORTANCE);
        SetHelp(data->CY_SIGNATURE,   MSG_HELP_WR_CY_SIGNATURE);
        SetHelp(data->CY_SECURITY,    MSG_HELP_WR_CY_SECURITY);
        SetHelp(data->LV_ATTACH,      MSG_HELP_WR_LV_ATTACH);
        SetHelp(data->LV_ATTACH_TINY, MSG_HELP_WR_LV_ATTACH);

        DoMethod(obj, MUIM_MultiSet, MUIA_Font, MUIV_Font_Tiny,
          data->BT_ATTACH_REMIND_ADD,
          data->BT_ATTACH_REMIND_LATER,
          data->BT_ATTACH_REMIND_NEVER,
          NULL);
        set(data->BT_ATTACH_REMIND_NEVER, MUIA_Weight, 0);

        // set the menuitem notifies
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_NEW,         data->TE_EDIT, 1, MUIM_TextEditor_ClearText);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_OPEN,        obj, 2, METHOD(EditorCmd), ED_OPEN);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_INSFILE,     obj, 2, METHOD(EditorCmd), ED_INSERT);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_INSQUOT,     obj, 2, METHOD(EditorCmd), ED_INSQUOT);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_INSALTQUOT,  obj, 2, METHOD(EditorCmd), ED_INSALTQUOT);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_INSROT13,    obj, 2, METHOD(EditorCmd), ED_INSROT13);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_INSUUCODE,   obj, 2, METHOD(EditorCmd), ED_INSUUCODE);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_SAVEAS,      obj, 1, METHOD(SaveTextAs));
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_EDIT,        obj, 1, METHOD(LaunchEditor));
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_SENDNOW,     obj, 3, METHOD(ComposeMail), WRITE_SEND, TRUE);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_QUEUE,       obj, 3, METHOD(ComposeMail), WRITE_QUEUE, TRUE);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_SAVEASDRAFT, obj, 3, METHOD(ComposeMail), WRITE_DRAFT, TRUE);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_CLOSE,       obj, 1, METHOD(CancelAction));
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_CUT,         obj, 2, METHOD(EditActionPerformed), EA_CUT);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_COPY,        obj, 2, METHOD(EditActionPerformed), EA_COPY);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_PASTE,       obj, 2, METHOD(EditActionPerformed), EA_PASTE);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_DELETE,      obj, 2, METHOD(EditActionPerformed), EA_DELETE);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_PASQUOT,     obj, 2, METHOD(EditorCmd), ED_PASQUOT);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_PASALTQUOT,  obj, 2, METHOD(EditorCmd), ED_PASALTQUOT);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_PASROT13,    obj, 2, METHOD(EditorCmd), ED_PASROT13);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_SEARCH,      obj, 2, METHOD(Search), MUIF_NONE);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_SEARCHAGAIN, obj, 2, METHOD(Search), MUIF_ReadMailGroup_Search_Again);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_DICT,        MUIV_Notify_Application, 3, MUIM_CallHook, &DI_OpenHook, obj);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_UNDO,        obj, 2, METHOD(EditActionPerformed), EA_UNDO);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_REDO,        obj, 2, METHOD(EditActionPerformed), EA_REDO);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_ADDFILE,     obj, 2, METHOD(RequestAttachment), C->AttachDir);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_ADDCLIP,     obj, 1, METHOD(AddClipboard));
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_ADDPGP,      obj, 1, METHOD(AddPGPKey));
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_SELECTALL,   obj, 2, METHOD(EditActionPerformed), EA_SELECTALL);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_SELECTNONE,  obj, 2, METHOD(EditActionPerformed), EA_SELECTNONE);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_SWITCH1,     data->RG_PAGE, 3, MUIM_Set, MUIA_Group_ActivePage, 0);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_SWITCH2,     data->RG_PAGE, 3, MUIM_Set, MUIA_Group_ActivePage, 1);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_SWITCH3,     data->RG_PAGE, 3, MUIM_Set, MUIA_Group_ActivePage, 2);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_CC,          obj, 2, METHOD(MenuToggleRecipientObject), MUIV_WriteWindow_RcptType_CC);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_BCC,         obj, 2, METHOD(MenuToggleRecipientObject), MUIV_WriteWindow_RcptType_BCC);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_REPLYTO,     obj, 2, METHOD(MenuToggleRecipientObject), MUIV_WriteWindow_RcptType_ReplyTo);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_EMOT0,       data->TE_EDIT, 3, MUIM_TextEditor_InsertText, ":-)", MUIV_TextEditor_InsertText_Cursor);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_EMOT1,       data->TE_EDIT, 3, MUIM_TextEditor_InsertText, ":-|", MUIV_TextEditor_InsertText_Cursor);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_EMOT2,       data->TE_EDIT, 3, MUIM_TextEditor_InsertText, ":-(", MUIV_TextEditor_InsertText_Cursor);
        DoMethod(obj, MUIM_Notify, MUIA_Window_MenuAction, WMEN_EMOT3,       data->TE_EDIT, 3, MUIM_TextEditor_InsertText, ";-)", MUIV_TextEditor_InsertText_Cursor);

        // catch MUIA_AppMessage with a hook so that we get notified
        // as soon as the user drops a WB icon on the pagegroup object
        DoMethod(data->RG_PAGE, MUIM_Notify, MUIA_AppMessage, MUIV_EveryTime, obj, 2, METHOD(HandleAppMessage), MUIV_TriggerValue);

        // set some notifications on the toolbar
        // in case it was generated
        if(data->TO_TOOLBAR != NULL)
        {
          // connect the buttons presses
          DoMethod(data->TO_TOOLBAR, MUIM_TheBar_Notify, TB_WRITE_EDITOR,    MUIA_Pressed, FALSE, obj, 1, METHOD(LaunchEditor));
          DoMethod(data->TO_TOOLBAR, MUIM_TheBar_Notify, TB_WRITE_INSERT,    MUIA_Pressed, FALSE, obj, 2, METHOD(EditorCmd), ED_INSERT);
          DoMethod(data->TO_TOOLBAR, MUIM_TheBar_Notify, TB_WRITE_CUT,       MUIA_Pressed, FALSE, data->TE_EDIT, 2, MUIM_TextEditor_ARexxCmd, "CUT");
          DoMethod(data->TO_TOOLBAR, MUIM_TheBar_Notify, TB_WRITE_COPY,      MUIA_Pressed, FALSE, data->TE_EDIT, 2, MUIM_TextEditor_ARexxCmd, "COPY");
          DoMethod(data->TO_TOOLBAR, MUIM_TheBar_Notify, TB_WRITE_PASTE,     MUIA_Pressed, FALSE, data->TE_EDIT, 2, MUIM_TextEditor_ARexxCmd, "PASTE");
          DoMethod(data->TO_TOOLBAR, MUIM_TheBar_Notify, TB_WRITE_UNDO,      MUIA_Pressed, FALSE, data->TE_EDIT, 2, MUIM_TextEditor_ARexxCmd, "UNDO");
          DoMethod(data->TO_TOOLBAR, MUIM_TheBar_Notify, TB_WRITE_BOLD,      MUIA_Selected, MUIV_EveryTime, obj, 3, METHOD(SetSoftStyle), SSM_BOLD, ORIGIN_TOOLBAR);
          DoMethod(data->TO_TOOLBAR, MUIM_TheBar_Notify, TB_WRITE_ITALIC,    MUIA_Selected, MUIV_EveryTime, obj, 3, METHOD(SetSoftStyle), SSM_ITALIC, ORIGIN_TOOLBAR);
          DoMethod(data->TO_TOOLBAR, MUIM_TheBar_Notify, TB_WRITE_UNDERLINE, MUIA_Selected, MUIV_EveryTime, obj, 3, METHOD(SetSoftStyle), SSM_UNDERLINE, ORIGIN_TOOLBAR);
          DoMethod(data->TO_TOOLBAR, MUIM_TheBar_Notify, TB_WRITE_COLORED,   MUIA_Selected, MUIV_EveryTime, obj, 3, METHOD(SetSoftStyle), SSM_COLOR, ORIGIN_TOOLBAR);
          DoMethod(data->TO_TOOLBAR, MUIM_TheBar_Notify, TB_WRITE_SEARCH,    MUIA_Pressed, FALSE, obj, 3, METHOD(Search), MUIF_NONE);

          // connect attributes to button disables
          DoMethod(data->TE_EDIT, MUIM_Notify, MUIA_TextEditor_AreaMarked, MUIV_EveryTime, data->TO_TOOLBAR, 4, MUIM_TheBar_SetAttr, TB_WRITE_CUT, MUIA_TheBar_Attr_Disabled, MUIV_NotTriggerValue);
          DoMethod(data->TE_EDIT, MUIM_Notify, MUIA_TextEditor_AreaMarked, MUIV_EveryTime, data->TO_TOOLBAR, 4, MUIM_TheBar_SetAttr, TB_WRITE_COPY, MUIA_TheBar_Attr_Disabled, MUIV_NotTriggerValue);
          DoMethod(data->TE_EDIT, MUIM_Notify, MUIA_TextEditor_UndoAvailable, MUIV_EveryTime, data->TO_TOOLBAR, 4, MUIM_TheBar_SetAttr, TB_WRITE_UNDO, MUIA_TheBar_Attr_Disabled, MUIV_NotTriggerValue);

          // connect attributes to button selections
          // modifying the buttons' states must not cause any notifications!
          DoMethod(data->TE_EDIT, MUIM_Notify, MUIA_TextEditor_StyleBold,      MUIV_EveryTime, data->TO_TOOLBAR, 4, MUIM_TheBar_NoNotifySetAttr, TB_WRITE_BOLD,      MUIA_TheBar_Attr_Selected, MUIV_TriggerValue);
          DoMethod(data->TE_EDIT, MUIM_Notify, MUIA_TextEditor_StyleItalic,    MUIV_EveryTime, data->TO_TOOLBAR, 4, MUIM_TheBar_NoNotifySetAttr, TB_WRITE_ITALIC,    MUIA_TheBar_Attr_Selected, MUIV_TriggerValue);
          DoMethod(data->TE_EDIT, MUIM_Notify, MUIA_TextEditor_StyleUnderline, MUIV_EveryTime, data->TO_TOOLBAR, 4, MUIM_TheBar_NoNotifySetAttr, TB_WRITE_UNDERLINE, MUIA_TheBar_Attr_Selected, MUIV_TriggerValue);
          DoMethod(data->TE_EDIT, MUIM_Notify, MUIA_TextEditor_Pen,            0,              data->TO_TOOLBAR, 4, MUIM_TheBar_NoNotifySetAttr, TB_WRITE_COLORED,   MUIA_TheBar_Attr_Selected, FALSE);
          DoMethod(data->TE_EDIT, MUIM_Notify, MUIA_TextEditor_Pen,            6,              data->TO_TOOLBAR, 4, MUIM_TheBar_NoNotifySetAttr, TB_WRITE_COLORED,   MUIA_TheBar_Attr_Selected, FALSE);
          DoMethod(data->TE_EDIT, MUIM_Notify, MUIA_TextEditor_Pen,            7,              data->TO_TOOLBAR, 4, MUIM_TheBar_NoNotifySetAttr, TB_WRITE_COLORED,   MUIA_TheBar_Attr_Selected, TRUE);
          DoMethod(data->TE_EDIT, MUIM_Notify, MUIA_TextEditor_Pen,            8,              data->TO_TOOLBAR, 4, MUIM_TheBar_NoNotifySetAttr, TB_WRITE_COLORED,   MUIA_TheBar_Attr_Selected, FALSE);
          DoMethod(data->TE_EDIT, MUIM_Notify, MUIA_TextEditor_Pen,            9,              data->TO_TOOLBAR, 4, MUIM_TheBar_NoNotifySetAttr, TB_WRITE_COLORED,   MUIA_TheBar_Attr_Selected, FALSE);
          DoMethod(data->TE_EDIT, MUIM_Notify, MUIA_TextEditor_Pen,           10,              data->TO_TOOLBAR, 4, MUIM_TheBar_NoNotifySetAttr, TB_WRITE_COLORED,   MUIA_TheBar_Attr_Selected, FALSE);
          DoMethod(data->TE_EDIT, MUIM_Notify, MUIA_TextEditor_Pen,           11,              data->TO_TOOLBAR, 4, MUIM_TheBar_NoNotifySetAttr, TB_WRITE_COLORED,   MUIA_TheBar_Attr_Selected, FALSE);
          DoMethod(data->TE_EDIT, MUIM_Notify, MUIA_TextEditor_Pen,           12,              data->TO_TOOLBAR, 4, MUIM_TheBar_NoNotifySetAttr, TB_WRITE_COLORED,   MUIA_TheBar_Attr_Selected, FALSE);

          DoMethod(data->MI_BOLD,      MUIM_Notify, MUIA_Menuitem_Checked, MUIV_EveryTime, data->TO_TOOLBAR, 4, MUIM_TheBar_NoNotifySetAttr, TB_WRITE_BOLD,      MUIA_TheBar_Attr_Selected, MUIV_TriggerValue);
          DoMethod(data->MI_ITALIC,    MUIM_Notify, MUIA_Menuitem_Checked, MUIV_EveryTime, data->TO_TOOLBAR, 4, MUIM_TheBar_NoNotifySetAttr, TB_WRITE_ITALIC,    MUIA_TheBar_Attr_Selected, MUIV_TriggerValue);
          DoMethod(data->MI_UNDERLINE, MUIM_Notify, MUIA_Menuitem_Checked, MUIV_EveryTime, data->TO_TOOLBAR, 4, MUIM_TheBar_NoNotifySetAttr, TB_WRITE_UNDERLINE, MUIA_TheBar_Attr_Selected, MUIV_TriggerValue);
          DoMethod(data->MI_COLORED,   MUIM_Notify, MUIA_Menuitem_Checked, MUIV_EveryTime, data->TO_TOOLBAR, 4, MUIM_TheBar_NoNotifySetAttr, TB_WRITE_COLORED,   MUIA_TheBar_Attr_Selected, MUIV_TriggerValue);
        }

        if(data->TX_POSI != NULL)
        {
          DoMethod(data->TE_EDIT, MUIM_Notify, MUIA_TextEditor_CursorX, MUIV_EveryTime, obj, 1, METHOD(UpdateCursorPos));
          DoMethod(data->TE_EDIT, MUIM_Notify, MUIA_TextEditor_CursorY, MUIV_EveryTime, obj, 1, METHOD(UpdateCursorPos));
        }

        DoMethod(data->TE_EDIT, MUIM_Notify, MUIA_TextEditor_StyleBold,      MUIV_EveryTime, data->MI_BOLD,      3, MUIM_NoNotifySet, MUIA_Menuitem_Checked, MUIV_TriggerValue);
        DoMethod(data->TE_EDIT, MUIM_Notify, MUIA_TextEditor_StyleItalic,    MUIV_EveryTime, data->MI_ITALIC,    3, MUIM_NoNotifySet, MUIA_Menuitem_Checked, MUIV_TriggerValue);
        DoMethod(data->TE_EDIT, MUIM_Notify, MUIA_TextEditor_StyleUnderline, MUIV_EveryTime, data->MI_UNDERLINE, 3, MUIM_NoNotifySet, MUIA_Menuitem_Checked, MUIV_TriggerValue);
        DoMethod(data->TE_EDIT, MUIM_Notify, MUIA_TextEditor_Pen,             0,             data->MI_COLORED,   3, MUIM_NoNotifySet, MUIA_Menuitem_Checked, FALSE);
        DoMethod(data->TE_EDIT, MUIM_Notify, MUIA_TextEditor_Pen,             6,             data->MI_COLORED,   3, MUIM_NoNotifySet, MUIA_Menuitem_Checked, FALSE);
        DoMethod(data->TE_EDIT, MUIM_Notify, MUIA_TextEditor_Pen,             7,             data->MI_COLORED,   3, MUIM_NoNotifySet, MUIA_Menuitem_Checked, TRUE);
        DoMethod(data->TE_EDIT, MUIM_Notify, MUIA_TextEditor_Pen,             8,             data->MI_COLORED,   3, MUIM_NoNotifySet, MUIA_Menuitem_Checked, FALSE);
        DoMethod(data->TE_EDIT, MUIM_Notify, MUIA_TextEditor_Pen,             9,             data->MI_COLORED,   3, MUIM_NoNotifySet, MUIA_Menuitem_Checked, FALSE);
        DoMethod(data->TE_EDIT, MUIM_Notify, MUIA_TextEditor_Pen,            10,             data->MI_COLORED,   3, MUIM_NoNotifySet, MUIA_Menuitem_Checked, FALSE);
        DoMethod(data->TE_EDIT, MUIM_Notify, MUIA_TextEditor_Pen,            11,             data->MI_COLORED,   3, MUIM_NoNotifySet, MUIA_Menuitem_Checked, FALSE);
        DoMethod(data->TE_EDIT, MUIM_Notify, MUIA_TextEditor_Pen,            12,             data->MI_COLORED,   3, MUIM_NoNotifySet, MUIA_Menuitem_Checked, FALSE);
        DoMethod(data->TE_EDIT, MUIM_Notify, MUIA_TextEditor_MatchedKeyword, MUIV_EveryTime, obj,                2, METHOD(MatchedKeyword), MUIV_TriggerValue);

        DoMethod(data->MI_BOLD,      MUIM_Notify, MUIA_Menuitem_Checked, MUIV_EveryTime, obj, 3, METHOD(SetSoftStyle), SSM_BOLD, ORIGIN_MENU);
        DoMethod(data->MI_ITALIC,    MUIM_Notify, MUIA_Menuitem_Checked, MUIV_EveryTime, obj, 3, METHOD(SetSoftStyle), SSM_ITALIC, ORIGIN_MENU);
        DoMethod(data->MI_UNDERLINE, MUIM_Notify, MUIA_Menuitem_Checked, MUIV_EveryTime, obj, 3, METHOD(SetSoftStyle), SSM_UNDERLINE, ORIGIN_MENU);
        DoMethod(data->MI_COLORED,   MUIM_Notify, MUIA_Menuitem_Checked, MUIV_EveryTime, obj, 3, METHOD(SetSoftStyle), SSM_COLOR, ORIGIN_MENU);

        DoMethod(data->RG_PAGE,        MUIM_Notify, MUIA_Group_ActivePage,                  0, MUIV_Notify_Window, 3, MUIM_Set, MUIA_Window_ActiveObject, data->TE_EDIT);
        DoMethod(data->RG_PAGE,        MUIM_Notify, MUIA_Group_ActivePage,                  1, MUIV_Notify_Window, 3, MUIM_Set, MUIA_Window_ActiveObject, data->LV_ATTACH);
        DoMethod(data->ST_SUBJECT,     MUIM_Notify, MUIA_String_Acknowledge,                MUIV_EveryTime, MUIV_Notify_Window, 3, MUIM_Set, MUIA_Window_ActiveObject, data->TE_EDIT);
        DoMethod(data->ST_SUBJECT,     MUIM_Notify, MUIA_String_Contents,                   MUIV_EveryTime, obj, 1, METHOD(UpdateWindowTitle));
        DoMethod(data->BT_ADD,         MUIM_Notify, MUIA_Pressed,                           FALSE,          obj, 2, METHOD(RequestAttachment), C->AttachDir);
        DoMethod(data->BT_ADDPACK,     MUIM_Notify, MUIA_Pressed,                           FALSE,          obj, 1, METHOD(AddArchive));
        DoMethod(data->BT_REMOVE,      MUIM_Notify, MUIA_Pressed,                           FALSE,          obj, 1, METHOD(RemoveAttachment));
        DoMethod(data->BT_RENAME,      MUIM_Notify, MUIA_Pressed,                           FALSE,          obj, 1, METHOD(RenameAttachment));
        DoMethod(data->BT_DISPLAY,     MUIM_Notify, MUIA_Pressed,                           FALSE,          obj, 2, METHOD(DisplayAttachment), data->LV_ATTACH);
        DoMethod(data->LV_ATTACH,      MUIM_Notify, MUIA_NList_DoubleClick,                 MUIV_EveryTime, obj, 2, METHOD(DisplayAttachment), data->LV_ATTACH);
        DoMethod(data->LV_ATTACH,      MUIM_Notify, MUIA_NList_Active,                      MUIV_EveryTime, obj, 1, METHOD(GetAttachmentEntry));
        DoMethod(data->LV_ATTACH_TINY, MUIM_Notify, MUIA_NList_DoubleClick,                 MUIV_EveryTime, obj, 2, METHOD(DisplayAttachment), data->LV_ATTACH_TINY);
        DoMethod(data->LV_ATTACH_TINY, MUIM_Notify, MUIA_NList_Active,                      MUIV_EveryTime, obj, 1, METHOD(GetAttachmentEntry));
        DoMethod(data->PO_CTYPE,       MUIM_Notify, MUIA_MimeTypePopup_MimeTypeChanged,     MUIV_EveryTime, obj, 1, METHOD(PutAttachmentEntry));
        DoMethod(data->ST_DESC,        MUIM_Notify, MUIA_String_Contents,                   MUIV_EveryTime, obj, 1, METHOD(PutAttachmentEntry));
        DoMethod(data->CH_DELSENT,     MUIM_Notify, MUIA_Selected,                          MUIV_EveryTime, data->MI_DELSENT,        3, MUIM_Set,      MUIA_Menuitem_Checked, MUIV_TriggerValue);
        DoMethod(data->CH_MDN,         MUIM_Notify, MUIA_Selected,                          MUIV_EveryTime, data->MI_MDN,            3, MUIM_Set,      MUIA_Menuitem_Checked, MUIV_TriggerValue);
        DoMethod(data->CH_ADDINFO,     MUIM_Notify, MUIA_Selected,                          MUIV_EveryTime, data->MI_ADDINFO,        3, MUIM_Set,      MUIA_Menuitem_Checked, MUIV_TriggerValue);
        DoMethod(data->MI_AUTOSPELL,   MUIM_Notify, MUIA_Menuitem_Checked,                  MUIV_EveryTime, data->TE_EDIT,           3, MUIM_Set,      MUIA_TextEditor_TypeAndSpell, MUIV_TriggerValue);
        DoMethod(data->MI_AUTOWRAP,    MUIM_Notify, MUIA_Menuitem_Checked,                  TRUE,           data->TE_EDIT,           3, MUIM_Set,      MUIA_TextEditor_WrapBorder, C->EdWrapCol);
        DoMethod(data->MI_AUTOWRAP,    MUIM_Notify, MUIA_Menuitem_Checked,                  FALSE,          data->TE_EDIT,           3, MUIM_Set,      MUIA_TextEditor_WrapBorder, 0);
        DoMethod(data->MI_DELSENT,     MUIM_Notify, MUIA_Menuitem_Checked,                  MUIV_EveryTime, data->CH_DELSENT,        3, MUIM_Set,      MUIA_Selected, MUIV_TriggerValue);
        DoMethod(data->MI_MDN,         MUIM_Notify, MUIA_Menuitem_Checked,                  MUIV_EveryTime, data->CH_MDN,            3, MUIM_Set,      MUIA_Selected, MUIV_TriggerValue);
        DoMethod(data->MI_ADDINFO,     MUIM_Notify, MUIA_Menuitem_Checked,                  MUIV_EveryTime, data->CH_ADDINFO,        3, MUIM_Set,      MUIA_Selected, MUIV_TriggerValue);
        DoMethod(data->MI_FFONT,       MUIM_Notify, MUIA_Menuitem_Checked,                  MUIV_EveryTime, obj, 1, METHOD(StyleOptionsChanged));
        DoMethod(data->MI_TCOLOR,      MUIM_Notify, MUIA_Menuitem_Checked,                  MUIV_EveryTime, obj, 1, METHOD(StyleOptionsChanged));
        DoMethod(data->MI_TSTYLE,      MUIM_Notify, MUIA_Menuitem_Checked,                  MUIV_EveryTime, obj, 1, METHOD(StyleOptionsChanged));

        DoMethod(data->BT_ATTACH_REMIND_ADD,   MUIM_Notify, MUIA_Pressed, FALSE, obj, 2, METHOD(RequestAttachment), C->AttachDir);
        DoMethod(data->BT_ATTACH_REMIND_LATER, MUIM_Notify, MUIA_Pressed, FALSE, obj, 3, MUIM_Set, ATTR(AttachmentRemind), MUIV_WriteWindow_AttachmentRemind_Later);
        DoMethod(data->BT_ATTACH_REMIND_NEVER, MUIM_Notify, MUIA_Pressed, FALSE, obj, 3, MUIM_Set, ATTR(AttachmentRemind), MUIV_WriteWindow_AttachmentRemind_Never);

        // set the notifies for the importance cycle gadget
        DoMethod(data->CY_IMPORTANCE, MUIM_Notify, MUIA_Cycle_Active,      0,              menuStripObject,         4, MUIM_SetUData, WMEN_IMPORT0, MUIA_Menuitem_Checked, TRUE);
        DoMethod(obj,                 MUIM_Notify, MUIA_Window_MenuAction, WMEN_IMPORT0,   data->CY_IMPORTANCE,     3, MUIM_Set,      MUIA_Cycle_Active, 0);
        DoMethod(data->CY_IMPORTANCE, MUIM_Notify, MUIA_Cycle_Active,      1,              menuStripObject,         4, MUIM_SetUData, WMEN_IMPORT1, MUIA_Menuitem_Checked, TRUE);
        DoMethod(obj,                 MUIM_Notify, MUIA_Window_MenuAction, WMEN_IMPORT1,   data->CY_IMPORTANCE,     3, MUIM_Set,      MUIA_Cycle_Active, 1);
        DoMethod(data->CY_IMPORTANCE, MUIM_Notify, MUIA_Cycle_Active,      2,              menuStripObject,         4, MUIM_SetUData, WMEN_IMPORT2, MUIA_Menuitem_Checked, TRUE);
        DoMethod(obj,                 MUIM_Notify, MUIA_Window_MenuAction, WMEN_IMPORT2,   data->CY_IMPORTANCE,     3, MUIM_Set,      MUIA_Cycle_Active, 2);

        // set the notifies for the security cycle gadget
        DoMethod(data->CY_SECURITY,   MUIM_Notify, MUIA_Cycle_Active,      0,              menuStripObject,         4, MUIM_SetUData, WMEN_SECUR0, MUIA_Menuitem_Checked, TRUE);
        DoMethod(obj,                 MUIM_Notify, MUIA_Window_MenuAction, WMEN_SECUR0,    data->CY_SECURITY,       3, MUIM_Set,      MUIA_Cycle_Active, 0);
        DoMethod(data->CY_SECURITY,   MUIM_Notify, MUIA_Cycle_Active,      1,              menuStripObject,         4, MUIM_SetUData, WMEN_SECUR1, MUIA_Menuitem_Checked, TRUE);
        DoMethod(obj,                 MUIM_Notify, MUIA_Window_MenuAction, WMEN_SECUR1,    data->CY_SECURITY,       3, MUIM_Set,      MUIA_Cycle_Active, 1);
        DoMethod(data->CY_SECURITY,   MUIM_Notify, MUIA_Cycle_Active,      2,              menuStripObject,         4, MUIM_SetUData, WMEN_SECUR2, MUIA_Menuitem_Checked, TRUE);
        DoMethod(obj,                 MUIM_Notify, MUIA_Window_MenuAction, WMEN_SECUR2,    data->CY_SECURITY,       3, MUIM_Set,      MUIA_Cycle_Active, 2);
        DoMethod(data->CY_SECURITY,   MUIM_Notify, MUIA_Cycle_Active,      3,              menuStripObject,         4, MUIM_SetUData, WMEN_SECUR3, MUIA_Menuitem_Checked, TRUE);
        DoMethod(obj,                 MUIM_Notify, MUIA_Window_MenuAction, WMEN_SECUR3,    data->CY_SECURITY,       3, MUIM_Set,      MUIA_Cycle_Active, 3);
        DoMethod(data->CY_SECURITY,   MUIM_Notify, MUIA_Cycle_Active,      4,              menuStripObject,         4, MUIM_SetUData, WMEN_SECUR4, MUIA_Menuitem_Checked, TRUE);
        DoMethod(obj,                 MUIM_Notify, MUIA_Window_MenuAction, WMEN_SECUR4,    data->CY_SECURITY,       3, MUIM_Set,      MUIA_Cycle_Active, 4);

        // set notify for signature cycle gadget
        DoMethod(data->CY_SIGNATURE, MUIM_Notify, MUIA_SignatureChooser_Signature, MUIV_EveryTime, obj, 1, METHOD(SignatureChanged));

        // tell the two attachment list which other object is to be kept in sync
        set(data->LV_ATTACH,      MUIA_WriteAttachmentList_SyncList, data->LV_ATTACH_TINY);
        set(data->LV_ATTACH_TINY, MUIA_WriteAttachmentList_SyncList, data->LV_ATTACH);

        // hide optional recipient string object depending on their
        // defaults
        if(C->ShowRcptFieldCC == FALSE)
          DoMethod(obj, METHOD(HideRecipientObject), MUIV_WriteWindow_RcptType_CC);
        if(C->ShowRcptFieldBCC == FALSE)
          DoMethod(obj, METHOD(HideRecipientObject), MUIV_WriteWindow_RcptType_BCC);
        if(C->ShowRcptFieldReplyTo == FALSE)
          DoMethod(obj, METHOD(HideRecipientObject), MUIV_WriteWindow_RcptType_ReplyTo);

        // update the available signatures first
        DoMethod(obj, METHOD(UpdateSignatures));
      }
      else
      {
        // prepare a first initial window title string
        strlcpy(data->windowTitle, tr(MSG_WR_REDIRECT_TITLE), sizeof(data->windowTitle));
      }

      // set the default window and screen title
      xset(obj, MUIA_Window_Title,       data->windowTitle,
                MUIA_Window_ScreenTitle, CreateScreenTitle(data->screenTitle, sizeof(data->screenTitle), data->windowTitle));

      // hide the override from address gadget also
      // in redirect mode
      if(C->OverrideFromAddress == FALSE)
        DoMethod(obj, METHOD(HideRecipientObject), MUIV_WriteWindow_RcptType_FromOverride);

      // set notify for identity cycle gadget
      DoMethod(data->CY_FROM, MUIM_Notify, MUIA_IdentityChooser_Identity, MUIV_EveryTime, obj, 2, METHOD(IdentityChanged), MUIV_TriggerValue);

      // make sure update the IdentityChooser state
      DoMethod(obj, METHOD(IdentityChanged), uin);

      // if we only have one identity we hide the identitychooser object
      if(xget(data->CY_FROM, MUIA_IdentityChooser_NumIdentities) < 2)
        DoMethod(obj, METHOD(HideRecipientObject), MUIV_WriteWindow_RcptType_From);

      // set some help text
      SetHelp(data->ST_TO,          MSG_HELP_WR_ST_TO);
      SetHelp(data->BT_QUEUE,       MSG_HELP_WR_BT_QUEUE);
      SetHelp(data->BT_SAVEASDRAFT, MSG_HELP_WR_BT_SAVEASDRAFT);
      SetHelp(data->BT_SEND,        MSG_HELP_WR_BT_SEND);
      SetHelp(data->BT_CANCEL,      MSG_HELP_WR_BT_CANCEL);
      SetHelp(data->PO_CODESET,     MSG_HELP_WR_PO_CHARSET);

      // declare the mail as modified if any of these objects reports a change
      DoMethod(data->ST_FROM_OVERRIDE, MUIM_Notify, MUIA_String_Contents,                   MUIV_EveryTime, obj, 3, MUIM_Set, ATTR(Modified), TRUE);
      DoMethod(data->ST_TO,            MUIM_Notify, MUIA_String_Contents,                   MUIV_EveryTime, obj, 3, MUIM_Set, ATTR(Modified), TRUE);
      DoMethod(data->ST_CC,            MUIM_Notify, MUIA_String_Contents,                   MUIV_EveryTime, obj, 3, MUIM_Set, ATTR(Modified), TRUE);
      DoMethod(data->ST_BCC,           MUIM_Notify, MUIA_String_Contents,                   MUIV_EveryTime, obj, 3, MUIM_Set, ATTR(Modified), TRUE);
      DoMethod(data->ST_REPLYTO,       MUIM_Notify, MUIA_String_Contents,                   MUIV_EveryTime, obj, 3, MUIM_Set, ATTR(Modified), TRUE);
      DoMethod(data->ST_EXTHEADER,     MUIM_Notify, MUIA_String_Contents,                   MUIV_EveryTime, obj, 3, MUIM_Set, ATTR(Modified), TRUE);
      DoMethod(data->PO_CTYPE,         MUIM_Notify, MUIA_MimeTypePopup_MimeTypeChanged,     MUIV_EveryTime, obj, 3, MUIM_Set, ATTR(Modified), TRUE);
      DoMethod(data->ST_DESC,          MUIM_Notify, MUIA_String_Contents,                   MUIV_EveryTime, obj, 3, MUIM_Set, ATTR(Modified), TRUE);
      DoMethod(data->PO_CODESET,       MUIM_Notify, MUIA_CodesetPopup_CodesetChanged,       MUIV_EveryTime, obj, 3, MUIM_Set, ATTR(Modified), TRUE);
      DoMethod(data->CY_IMPORTANCE,    MUIM_Notify, MUIA_Cycle_Active,                      MUIV_EveryTime, obj, 3, MUIM_Set, ATTR(Modified), TRUE);
      DoMethod(data->CY_SECURITY,      MUIM_Notify, MUIA_Cycle_Active,                      MUIV_EveryTime, obj, 3, MUIM_Set, ATTR(Modified), TRUE);
      DoMethod(data->CH_DELSENT,       MUIM_Notify, MUIA_Selected,                          MUIV_EveryTime, obj, 3, MUIM_Set, ATTR(Modified), TRUE);
      DoMethod(data->CH_MDN,           MUIM_Notify, MUIA_Selected,                          MUIV_EveryTime, obj, 3, MUIM_Set, ATTR(Modified), TRUE);
      DoMethod(data->CH_ADDINFO,       MUIM_Notify, MUIA_Selected,                          MUIV_EveryTime, obj, 3, MUIM_Set, ATTR(Modified), TRUE);

      // create a notify for changing the codeset
      DoMethod(data->PO_CODESET, MUIM_Notify, MUIA_CodesetPopup_Codeset, MUIV_EveryTime, obj, 2, METHOD(CodesetChanged), MUIV_TriggerValue);

      // set main window button notifies
      DoMethod(data->BT_SAVEASDRAFT, MUIM_Notify, MUIA_Pressed, FALSE, obj, 3, METHOD(ComposeMail), WRITE_DRAFT, TRUE);
      DoMethod(data->BT_QUEUE,       MUIM_Notify, MUIA_Pressed, FALSE, obj, 3, METHOD(ComposeMail), WRITE_QUEUE, TRUE);
      DoMethod(data->BT_SEND,        MUIM_Notify, MUIA_Pressed, FALSE, obj, 3, METHOD(ComposeMail), WRITE_SEND, TRUE);
      DoMethod(data->BT_CANCEL,      MUIM_Notify, MUIA_Pressed, FALSE, obj, 1, METHOD(CancelAction));

      // connect the closerequest attribute to the cancel action method so that
      // users might get informed of an eventually data loss
      DoMethod(obj, MUIM_Notify, MUIA_Window_CloseRequest, TRUE, obj, 1, METHOD(CancelAction));

      // prepare the temporary filename of that new write window
      snprintf(filename, sizeof(filename), "YAMw%08x-%d.txt", (unsigned int)FindTask(NULL), data->windowNumber+1);
      AddPath(data->wmData->filename, C->TempDir, filename, sizeof(data->wmData->filename));

      // set the global codeset as the default one
      data->wmData->codeset = G->writeCodeset;

      // Finally set up the notifications for external changes to the file being edited
      // if this is not a redirect window. We let the UserData point to this object to be
      // able to invoke a method from the main loop. However, we only do this for write
      // windows which are not opened in redirect mode
      if(data->wmData->mode != NMM_REDIRECT)
      {
        #if defined(__amigaos4__)
        data->wmData->notifyRequest = AllocDosObjectTags(DOS_NOTIFYREQUEST, ADO_NotifyName,     data->wmData->filename,
                                                                            ADO_NotifyUserData, (ULONG)obj,
                                                                            ADO_NotifyMethod,   NRF_SEND_MESSAGE,
                                                                            ADO_NotifyPort,     G->writeWinNotifyPort,
                                                                            TAG_DONE);
        #else
        if((data->wmData->notifyRequest = AllocVecPooled(G->SharedMemPool, sizeof(*data->wmData->notifyRequest))) != NULL)
        {
          data->wmData->notifyRequest->nr_Name = data->wmData->filename;
          data->wmData->notifyRequest->nr_UserData = (ULONG)obj;
          data->wmData->notifyRequest->nr_Flags = NRF_SEND_MESSAGE;
          data->wmData->notifyRequest->nr_stuff.nr_Msg.nr_Port = G->writeWinNotifyPort;
        }
        #endif
      }
      else
        data->wmData->notifyRequest = NULL;

      // there is no active notification yet
      data->wmData->fileNotifyActive = FALSE;

      // place our data in the node and add it to the writeMailDataList
      AddTail((struct List *)&(G->writeMailDataList), (struct Node *)data->wmData);

      if(data->wmData->mode != NMM_REDIRECT)
      {
        // finally set up the notifications for external changes to the file being edited if this is not a redirect window
        if((data->notifyPort = AllocSysObjectTags(ASOT_PORT, TAG_DONE)) != NULL)
        {
          #if defined(__amigaos4__)
          data->notifyRequest = AllocDosObjectTags(DOS_NOTIFYREQUEST, ADO_NotifyName, data->wmData->filename,
                                                                      ADO_NotifyMethod, NRF_SEND_MESSAGE,
                                                                      ADO_NotifyPort, data->notifyPort,
                                                                      TAG_DONE);
          #else
          if((data->notifyRequest = AllocVecPooled(G->SharedMemPool, sizeof(*data->notifyRequest))) != NULL)
          {
            data->notifyRequest->nr_Name = data->wmData->filename;
            data->notifyRequest->nr_Flags = NRF_SEND_MESSAGE;
            data->notifyRequest->nr_stuff.nr_Msg.nr_Port = data->notifyPort;
          }
          #endif

          if(data->notifyRequest != NULL)
          {
            StartNotify(data->notifyRequest);
          }
        }
      }

      // we created a new write window, lets
      // go and start the PREWRITE macro
      DoMethod(_app(obj), MUIM_YAMApplication_StartMacro, MACRO_PREWRITE, data->windowNumberStr);

      // the mail is not modified yet
      data->mailModified = FALSE;
    }
  }

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

  RETURN((ULONG)obj);
  return (ULONG)obj;
}

///
/// OVERLOAD(OM_DISPOSE)
OVERLOAD(OM_DISPOSE)
{
  GETDATA;
  ULONG result;

  ENTER();

  if(data->wmData->mode != NMM_REDIRECT)
  {
    int i;

    // cleanup the attachment list
    for(i=0; ;i++)
    {
      struct Attach *att;

      DoMethod(data->LV_ATTACH, MUIM_NList_GetEntry, i, &att);
      if(att == NULL)
        break;

      if(att->IsTemp == TRUE)
        DeleteFile(att->FilePath);
    }

    if(data->notifyRequest != NULL)
    {
      EndNotify(data->notifyRequest);

      #if defined(__amigaos4__)
      FreeDosObject(DOS_NOTIFYREQUEST, data->notifyRequest);
      #else
      FreeVecPooled(G->SharedMemPool, data->notifyRequest);
      #endif
      data->notifyRequest = NULL;
    }

    if(data->notifyPort != NULL)
    {
      FreeSysObject(ASOT_PORT, data->notifyPort);
      data->notifyPort = NULL;
    }
  }

  // check the reference window ptr of the addressbook
  if(G->ABookWinObject != NULL && (LONG)xget(G->ABookWinObject, MUIA_AddressBookWindow_WindowNumber) == data->windowNumber)
    set(G->ABookWinObject, MUIA_AddressBookWindow_WindowNumber, -1);

  // we have to dispose certain object on our own
  // because they may be hidden by the user
  if(data->fromRcptHidden == TRUE)
  {
    MUI_DisposeObject(data->LB_FROM);
    MUI_DisposeObject(data->CY_FROM);
  }

  if(data->fromOverrideRcptHidden == TRUE)
  {
    MUI_DisposeObject(data->LB_FROM_OVERRIDE);
    MUI_DisposeObject(data->GR_FROM_OVERRIDE);
  }

  if(data->ccRcptHidden == TRUE)
  {
    MUI_DisposeObject(data->LB_CC);
    MUI_DisposeObject(data->GR_CC);
  }

  if(data->bccRcptHidden == TRUE)
  {
    MUI_DisposeObject(data->LB_BCC);
    MUI_DisposeObject(data->GR_BCC);
  }

  if(data->replyToRcptHidden == TRUE)
  {
    MUI_DisposeObject(data->LB_REPLYTO);
    MUI_DisposeObject(data->GR_REPLYTO);
  }

  // signal the super class to dispose as well
  result = DoSuperMethodA(cl, obj, msg);

  RETURN(result);
  return result;
}

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

  switch(((struct opGet *)msg)->opg_AttrID)
  {
    case ATTR(WriteMailData): *store = (ULONG)data->wmData; return TRUE;
    case ATTR(Num):           *store = data->windowNumber; return TRUE;
    case ATTR(To):            *store = xget(data->ST_TO, MUIA_String_Contents) ; return TRUE;
    case ATTR(Quiet):         *store = data->wmData->quietMode; return TRUE;
    case ATTR(NotifyPort):    *store = (ULONG)data->notifyPort; return TRUE;
    case ATTR(EditorActive):  *store = ((Object *)xget(obj, MUIA_Window_ActiveObject) == data->TE_EDIT) ? TRUE : FALSE; return TRUE;
  }

  return DoSuperMethodA(cl, obj, msg);
}
///
/// OVERLOAD(OM_SET)
OVERLOAD(OM_SET)
{
  GETDATA;
  struct TagItem *tags = inittags(msg), *tag;

  while((tag = NextTagItem((APTR)&tags)) != NULL)
  {
    switch(tag->ti_Tag)
    {
      case ATTR(ActiveObject):
      {
        Object *actObj = NULL;

        switch((enum ActiveObject)tag->ti_Data)
        {
          case MUIV_WriteWindow_ActiveObject_To:
            actObj = data->ST_TO;
          break;

          case MUIV_WriteWindow_ActiveObject_TextEditor:
            actObj = data->TE_EDIT;
          break;

          case MUIV_WriteWindow_ActiveObject_Subject:
            actObj = data->ST_SUBJECT;
          break;
        }

        if(actObj != NULL)
          set(obj, MUIA_Window_ActiveObject, actObj);
      }
      break;

      case ATTR(Identity):
      {
        set(data->CY_FROM, MUIA_IdentityChooser_Identity, tag->ti_Data);

        // make the superMethod call ignore those tags
        tag->ti_Tag = TAG_IGNORE;
      }
      break;

      case ATTR(From):
      {
        setstring(data->ST_FROM_OVERRIDE, tag->ti_Data);

        // make the superMethod call ignore those tags
        tag->ti_Tag = TAG_IGNORE;
      }
      break;

      case ATTR(To):
      {
        setstring(data->ST_TO, tag->ti_Data);

        // make the superMethod call ignore those tags
        tag->ti_Tag = TAG_IGNORE;
      }
      break;

      case ATTR(CC):
      {
        char *str = (char *)tag->ti_Data;

        setstring(data->ST_CC, str);
        if(IsStrEmpty(str) == FALSE)
          DoMethod(obj, METHOD(ShowRecipientObject), MUIV_WriteWindow_RcptType_CC);

        // make the superMethod call ignore those tags
        tag->ti_Tag = TAG_IGNORE;
      }
      break;

      case ATTR(BCC):
      {
        char *str = (char *)tag->ti_Data;

        setstring(data->ST_BCC, str);
        if(IsStrEmpty(str) == FALSE)
          DoMethod(obj, METHOD(ShowRecipientObject), MUIV_WriteWindow_RcptType_BCC);

        // make the superMethod call ignore those tags
        tag->ti_Tag = TAG_IGNORE;
      }
      break;

      case ATTR(ReplyTo):
      {
        char *str = (char *)tag->ti_Data;

        setstring(data->ST_REPLYTO, str);
        if(IsStrEmpty(str) == FALSE)
          DoMethod(obj, METHOD(ShowRecipientObject), MUIV_WriteWindow_RcptType_ReplyTo);

        // make the superMethod call ignore those tags
        tag->ti_Tag = TAG_IGNORE;
      }
      break;

      case ATTR(ExtHeaders):
      {
        setstring(data->ST_EXTHEADER, tag->ti_Data);

        // make the superMethod call ignore those tags
        tag->ti_Tag = TAG_IGNORE;
      }
      break;

      case ATTR(Subject):
      {
        setstring(data->ST_SUBJECT, tag->ti_Data);

        // make the superMethod call ignore those tags
        tag->ti_Tag = TAG_IGNORE;
      }
      break;

      case ATTR(AttachDescription):
      {
        setstring(data->ST_DESC, tag->ti_Data);

        // make the superMethod call ignore those tags
        tag->ti_Tag = TAG_IGNORE;
      }
      break;

      case ATTR(AttachContentType):
      {
        set(data->PO_CTYPE, MUIA_MimeTypePopup_MimeType, tag->ti_Data);

        // make the superMethod call ignore those tags
        tag->ti_Tag = TAG_IGNORE;
      }
      break;

      case ATTR(SendDisabled):
      {
        set(data->BT_SEND, MUIA_Disabled, tag->ti_Data);

        // make the superMethod call ignore those tags
        tag->ti_Tag = TAG_IGNORE;
      }
      break;

      case ATTR(DelSent):
      {
        setcheckmark(data->CH_DELSENT, tag->ti_Data);

        // make the superMethod call ignore those tags
        tag->ti_Tag = TAG_IGNORE;
      }
      break;

      case ATTR(MDN):
      {
        setcheckmark(data->CH_MDN, tag->ti_Data);

        // make the superMethod call ignore those tags
        tag->ti_Tag = TAG_IGNORE;
      }
      break;

      case ATTR(AddInfo):
      {
        setcheckmark(data->CH_ADDINFO, tag->ti_Data);

        // make the superMethod call ignore those tags
        tag->ti_Tag = TAG_IGNORE;
      }
      break;

      case ATTR(Importance):
      {
        setcycle(data->CY_IMPORTANCE, tag->ti_Data);

        // make the superMethod call ignore those tags
        tag->ti_Tag = TAG_IGNORE;
      }
      break;

      case ATTR(Signature):
      {
        set(data->CY_SIGNATURE, MUIA_SignatureChooser_Signature, tag->ti_Data);

        // make the superMethod call ignore those tags
        tag->ti_Tag = TAG_IGNORE;
      }
      break;

      case ATTR(Security):
      {
        setcycle(data->CY_SECURITY, tag->ti_Data);

        // make the superMethod call ignore those tags
        tag->ti_Tag = TAG_IGNORE;
      }
      break;

      case ATTR(MailBody):
      {
        set(data->TE_EDIT, MUIA_TextEditor_Contents, tag->ti_Data);

        // make the superMethod call ignore those tags
        tag->ti_Tag = TAG_IGNORE;
      }
      break;

      case ATTR(Modified):
      {
        data->mailModified = tag->ti_Data ? TRUE : FALSE;
      }
      break;

      case ATTR(AttachmentRemind):
      {
        data->attachmentRemind = tag->ti_Data;
        set(data->GR_ATTACH_REMIND, MUIA_ShowMe, FALSE);

        // make the superMethod call ignore those tags
        tag->ti_Tag = TAG_IGNORE;
      }
      break;
    }
  }

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

/* Private Methods */
/// DECLARE(EditActionPerformed)
//  User pressed an item of the edit submenu (cut/copy/paste, etc)
DECLARE(EditActionPerformed) // enum EditAction action
{
  GETDATA;
  Object *actObj;

  ENTER();

  // now we check what active object we currently got
  // so that we can send the menu action to the correct
  // gadget
  if((actObj = (Object *)xget(obj, MUIA_Window_ActiveObject)) != NULL)
  {
    // check which action we got
    switch(msg->action)
    {
      case EA_CUT:
      {
        if(actObj == data->TE_EDIT)
          DoMethod(actObj, MUIM_TextEditor_ARexxCmd, "CUT");
        else if(actObj == data->ST_TO || actObj == data->ST_SUBJECT ||
                actObj == data->ST_CC || actObj == data->ST_BCC ||
                actObj == data->ST_REPLYTO ||
                actObj == data->ST_EXTHEADER || actObj == data->ST_DESC ||
                actObj == data->PO_CTYPE)
        {
          DoMethod(actObj, MUIM_BetterString_DoAction, MUIV_BetterString_DoAction_Cut);
        }
      }
      break;

      case EA_COPY:
      {
        if(actObj == data->TE_EDIT)
          DoMethod(actObj, MUIM_TextEditor_ARexxCmd, "COPY");
        else if(actObj == data->ST_TO || actObj == data->ST_SUBJECT ||
                actObj == data->ST_CC || actObj == data->ST_BCC ||
                actObj == data->ST_REPLYTO ||
                actObj == data->ST_EXTHEADER || actObj == data->ST_DESC ||
                actObj == data->PO_CTYPE)
        {
          DoMethod(actObj, MUIM_BetterString_DoAction, MUIV_BetterString_DoAction_Copy);
        }
        else if(actObj == data->LV_ATTACH || actObj == data->LV_ATTACH_TINY)
        {
          DoMethod(actObj, MUIM_NList_CopyToClip, MUIV_NList_CopyToClip_Active, 0, NULL, NULL);
        }
      }
      break;

      case EA_PASTE:
      {
        if(actObj == data->TE_EDIT)
          DoMethod(actObj, MUIM_TextEditor_ARexxCmd, "PASTE");
        else if(actObj == data->ST_TO || actObj == data->ST_SUBJECT ||
                actObj == data->ST_CC || actObj == data->ST_BCC ||
                actObj == data->ST_REPLYTO ||
                actObj == data->ST_EXTHEADER || actObj == data->ST_DESC ||
                actObj == data->PO_CTYPE)
        {
          DoMethod(actObj, MUIM_BetterString_DoAction, MUIV_BetterString_DoAction_Paste);
        }
      }
      break;

      case EA_DELETE:
      {
        if(actObj == data->TE_EDIT)
          DoMethod(actObj, MUIM_TextEditor_ARexxCmd, "DELETE");
        else if(actObj == data->ST_TO || actObj == data->ST_SUBJECT ||
                actObj == data->ST_CC || actObj == data->ST_BCC ||
                actObj == data->ST_REPLYTO ||
                actObj == data->ST_EXTHEADER || actObj == data->ST_DESC ||
                actObj == data->PO_CTYPE)
        {
          DoMethod(actObj, MUIM_BetterString_DoAction, MUIV_BetterString_DoAction_Delete);
        }
      }
      break;

      case EA_UNDO:
      {
        if(actObj == data->TE_EDIT)
          DoMethod(actObj, MUIM_TextEditor_ARexxCmd, "UNDO");
        else if(actObj == data->ST_TO || actObj == data->ST_SUBJECT ||
                actObj == data->ST_CC || actObj == data->ST_BCC ||
                actObj == data->ST_REPLYTO ||
                actObj == data->ST_EXTHEADER || actObj == data->ST_DESC ||
                actObj == data->PO_CTYPE)
        {
          DoMethod(actObj, MUIM_BetterString_DoAction, MUIV_BetterString_DoAction_Undo);
        }
      }
      break;

      case EA_REDO:
      {
        if(actObj == data->TE_EDIT)
          DoMethod(actObj, MUIM_TextEditor_ARexxCmd, "REDO");
        else if(actObj == data->ST_TO || actObj == data->ST_SUBJECT ||
                actObj == data->ST_CC || actObj == data->ST_BCC ||
                actObj == data->ST_REPLYTO ||
                actObj == data->ST_EXTHEADER || actObj == data->ST_DESC ||
                actObj == data->PO_CTYPE)
        {
          DoMethod(actObj, MUIM_BetterString_DoAction, MUIV_BetterString_DoAction_Redo);
        }
      }
      break;

      case EA_SELECTALL:
      {
        if(actObj == data->TE_EDIT)
          DoMethod(actObj, MUIM_TextEditor_ARexxCmd, "SELECTALL");
        else if(actObj == data->ST_TO || actObj == data->ST_SUBJECT ||
                actObj == data->ST_CC || actObj == data->ST_BCC ||
                actObj == data->ST_REPLYTO ||
                actObj == data->ST_EXTHEADER || actObj == data->ST_DESC ||
                actObj == data->PO_CTYPE)
        {
          DoMethod(actObj, MUIM_BetterString_DoAction, MUIV_BetterString_DoAction_SelectAll);
        }
      }
      break;

      case EA_SELECTNONE:
      {
        if(actObj == data->TE_EDIT)
          DoMethod(actObj, MUIM_TextEditor_ARexxCmd, "SELECTNONE");
        else if(actObj == data->ST_TO || actObj == data->ST_SUBJECT ||
                actObj == data->ST_CC || actObj == data->ST_BCC ||
                actObj == data->ST_REPLYTO ||
                actObj == data->ST_EXTHEADER || actObj == data->ST_DESC ||
                actObj == data->PO_CTYPE)
        {
          DoMethod(actObj, MUIM_BetterString_DoAction, MUIV_BetterString_DoAction_SelectNone);
        }
      }
      break;
    }
  }

  RETURN(0);
  return 0;
}

///
/// DECLARE(RequestAttachment)
// Requests to enter a filename and add one or more files to the attachment list
DECLARE(RequestAttachment) // STRPTR drawer
{
  struct FileReqCache *frc;

  ENTER();

  if((frc = ReqFile(ASL_ATTACH, obj, tr(MSG_WR_AddFile), REQF_MULTISELECT, msg->drawer, "")) != NULL)
  {
    char filename[SIZE_PATHFILE];

    if(frc->numArgs == 0)
    {
      D(DBF_GUI, "chosen file: [%s] from drawer: [%s]", frc->file, frc->drawer);

      AddPath(filename, frc->drawer, frc->file, sizeof(filename));

      DoMethod(obj, METHOD(AddAttachment), filename, NULL, FALSE);
    }
    else
    {
      int i;

      for(i=0; i < frc->numArgs; i++)
      {
        D(DBF_GUI, "chosen file: [%s] from drawer: [%s]", frc->argList[i], frc->drawer);

        AddPath(filename, frc->drawer, frc->argList[i], sizeof(filename));

        DoMethod(obj, METHOD(AddAttachment), filename, NULL, FALSE);
      }
    }
  }

  RETURN(0);
  return 0;
}

///
/// DECLARE(AddClipboard)
// Adds contents of clipboard as attachment ***/
DECLARE(AddClipboard)
{
  struct TempFile *tf;

  ENTER();

  if((tf = OpenTempFile("w")) != NULL)
  {
    BOOL dumped = FALSE;

    if(DumpClipboard(tf->FP))
    {
      fclose(tf->FP);
      tf->FP = NULL;

      DoMethod(obj, METHOD(AddAttachment), tf->Filename, "clipboard.text", TRUE);
      free(tf);

      // don't delete this file by CloseTempFile()
      dumped = TRUE;
    }

    if(dumped == FALSE)
      CloseTempFile(tf);
  }

  RETURN(0);
  return 0;
}

///
/// DECLARE(AddPGPKey)
// Adds ASCII version of user's public PGP key as attachment
DECLARE(AddPGPKey)
{
  GETDATA;
  struct UserIdentityNode *uin = data->wmData->identity;

  ENTER();

  if(uin->usePGP == TRUE)
  {
    char *pgpid = uin->pgpKeyID[0] != '\0' ? uin->pgpKeyID : uin->address;
    char options[SIZE_LARGE];
    const char *fname = "T:PubKey.asc";

    snprintf(options, sizeof(options), (G->PGPVersion == 5) ? "-x %s -o %s +force +batchmode=1" : "-kxa %s %s +f +bat", pgpid, fname);

    // on error returns > 0
    if(PGPCommand((G->PGPVersion == 5) ? "pgpk" : "pgp", options, 0) == 0)
    {
      LONG size = 0;

      if(ObtainFileInfo(fname, FI_SIZE, &size) == TRUE && size > 0)
      {
        DoMethod(obj, METHOD(AddAttachment), fname, NULL, TRUE);
        set(data->PO_CTYPE, MUIA_MimeTypePopup_MimeType, "application/pgp-keys");
      }
      else
        ER_NewError(tr(MSG_ER_ErrorAppendKey), pgpid);
    }
    else
      ER_NewError(tr(MSG_ER_ErrorAppendKey), pgpid);
  }
  else
    DisplayBeep(_screen(obj));

  RETURN(0);
  return 0;
}

///
/// DECLARE(UpdateCusorPos)
// Shows/Updates cursor coordinates in write window
DECLARE(UpdateCursorPos)
{
  GETDATA;

  ENTER();

  snprintf(data->cursorPos, sizeof(data->cursorPos), "%03ld\n%03ld", xget(data->TE_EDIT, MUIA_TextEditor_CursorY)+1,
                                                                     xget(data->TE_EDIT, MUIA_TextEditor_CursorX)+1);
  set(data->TX_POSI, MUIA_Text_Contents, data->cursorPos);

  RETURN(0);
  return 0;
}

///
/// DECLARE(UpdateWindowTitle)
//  As soon as the subject string gadget is going to be
//  changed, this hook will be called.
DECLARE(UpdateWindowTitle)
{
  GETDATA;
  char *subject;
  size_t titleLen;

  ENTER();

  subject = (char *)xget(data->ST_SUBJECT, MUIA_String_Contents);
  titleLen = snprintf(data->windowTitle, sizeof(data->windowTitle), "[%d] %s: ", data->windowNumber+1, tr(MSG_WR_WriteWT));

  if(strlen(subject)+titleLen > sizeof(data->windowTitle)-1)
  {
    if(titleLen < sizeof(data->windowTitle)-4)
    {
      strlcat(data->windowTitle, subject, sizeof(data->windowTitle)-titleLen-4);
      strlcat(data->windowTitle, "...", sizeof(data->windowTitle)); // signals that the string was cut.
    }
    else
      strlcat(&data->windowTitle[sizeof(data->windowTitle)-5], "...", 4);
  }
  else
    strlcat(data->windowTitle, subject, sizeof(data->windowTitle));

  xset(obj, MUIA_Window_Title, data->windowTitle,
            MUIA_Window_ScreenTitle, CreateScreenTitle(data->screenTitle, sizeof(data->screenTitle), data->windowTitle));

  // the mail is modified
  data->mailModified = TRUE;

  RETURN(0);
  return 0;
}

///
/// DECLARE(AddArchive)
// Creates an archive of one or more files and adds it to the attachment list
DECLARE(AddArchive)
{
  BOOL result = FALSE;
  struct FileReqCache *frc;

  ENTER();

  // request files from the user via asl.library
  if((frc = ReqFile(ASL_ATTACH, obj, tr(MSG_WR_AddFile), REQF_MULTISELECT, C->AttachDir, "")) &&
      frc->numArgs > 0)
  {
    char *p;
    char arcname[SIZE_FILE];

    // get the basename of the first selected file and prepare it to the user
    // as a suggestion for an archive name
    strlcpy(arcname, FilePart(frc->argList[0]), sizeof(arcname));
    if((p = strrchr(arcname, '.')) != NULL)
      *p = '\0';

    // now ask the user for the choosen archive name
    if(StringRequest(arcname, SIZE_FILE, tr(MSG_WR_CreateArc), tr(MSG_WR_CreateArcReq), tr(MSG_Okay), NULL, tr(MSG_Cancel), FALSE, obj))
    {
      char *command = NULL;
      char arcpath[SIZE_PATHFILE];
      struct TempFile *tf = NULL;
      char *src;
      BPTR filedir;
      BOOL mustCloseQuote = FALSE;

      // create the destination archive name
      AddPath(arcpath, C->TempDir, arcname, sizeof(arcpath));

      // now we generate the temporary file containing the file names
      // which we are going to put in the archive.
      if(strstr(C->PackerCommand, "%l") != NULL && (tf = OpenTempFile("w")) != NULL)
      {
        int i;

        for(i=0; i < frc->numArgs; i++)
          fprintf(tf->FP, strchr(frc->argList[i], ' ') ? "\"%s\"\n" : "%s\n", frc->argList[i]);

        // just close here as we delete it later on
        fclose(tf->FP);
        tf->FP = NULL;
      }

      // now we create the command we are going to
      // execute for generating the archive.
      for(src = C->PackerCommand; *src != '\0'; src++)
      {
        if(*src == '%')
        {
          src++;
          switch(*src)
          {
            case '%':
              dstrcat(&command, "%");
            break;

            case 'a':
            {
              D(DBF_UTIL, "insert archive name '%s'", arcpath);
              if(strchr(arcpath, ' ') != NULL)
              {
                // surround the file name by quotes if it contains spaces
                dstrcat(&command, "\"");
                dstrcat(&command, arcpath);
                // remember to add the closing quotes
                mustCloseQuote = TRUE;
              }
              else
                dstrcat(&command, arcpath);
            }
            break;

            case 'l':
            {
              D(DBF_UTIL, "insert filename '%s'", tf->Filename);
              if(strchr(tf->Filename, ' ') != NULL)
              {
                // surround the file name by quotes if it contains spaces
                dstrcat(&command, "\"");
                dstrcat(&command, tf->Filename);
                // remember to add the closing quotes
                mustCloseQuote = TRUE;
              }
              else
                dstrcat(&command, tf->Filename);
            }
            break;

            case 'f':
            {
              int i;

              for(i=0; i < frc->numArgs; i++)
              {
                char filename[SIZE_PATHFILE];

                snprintf(filename, sizeof(filename), "\"%s\" ", frc->argList[i]);
                dstrcat(&command, filename);
              }
              break;
            }
            break;
          }
        }
        else if(*src == ' ')
        {
          // if we are to insert a space character and there are quotes
          // to be closed we do it now and forget about the quotes afterwards.
          if(mustCloseQuote == TRUE)
          {
            dstrcat(&command, "\" ");
            mustCloseQuote = FALSE;
          }
          else
          {
            // no quotes to be closed, just add the space character
            dstrcat(&command, " ");
          }
        }
        else
        {
          char chr[2];

          chr[0] = *src;
          chr[1] = '\0';

          dstrcat(&command, chr);
        }
      }

      // if there are still quotes to be closed do it now
      if(mustCloseQuote == TRUE)
        dstrcat(&command, "\"");

      if(IsStrEmpty(command) == FALSE)
      {
        // now we make the request drawer the current one temporarly.
        if((filedir = Lock(frc->drawer, ACCESS_READ)) != 0)
        {
          BPTR olddir = CurrentDir(filedir);

          // now we do the archive generation right here.
          if(LaunchCommand(command, 0, (C->ShowPackerProgress == TRUE) ? OUT_CONSOLE_CLOSE : OUT_NIL) == RETURN_OK)
            result = TRUE;

          // make the old directory the new current one again
          CurrentDir(olddir);
          UnLock(filedir);
        }
      }

      // free our private resources
      dstrfree(command);
      CloseTempFile(tf);

      // if everything worked out fine we go
      // and find out the real final attachment name
      if(result == TRUE)
      {
        APTR oldwin;
        LONG size;
        char filename[SIZE_PATHFILE];

        // don't let DOS bother us with requesters while we check some files
        oldwin = SetProcWindow((APTR)-1);

        strlcpy(filename, arcpath, sizeof(filename));
        if(ObtainFileInfo(filename, FI_SIZE, &size) == FALSE)
        {
          snprintf(filename, sizeof(filename), "%s.lha", arcpath);
          if(ObtainFileInfo(filename, FI_SIZE, &size) == FALSE)
          {
            snprintf(filename, sizeof(filename), "%s.lzx", arcpath);
            if(ObtainFileInfo(filename, FI_SIZE, &size) == FALSE)
              snprintf(filename, sizeof(filename), "%s.zip", arcpath);
          }
        }

        // allow requesters from DOS again
        SetProcWindow(oldwin);

        DoMethod(obj, METHOD(AddAttachment), filename, NULL, TRUE);
      }
      else
        ER_NewError(tr(MSG_ER_PACKERROR));

    }
  }

  RETURN(result);
  return result;
}

///
/// DECLARE(RemoveAttachment)
// Deletes a file from the attachment list and the belonging temporary file
DECLARE(RemoveAttachment)
{
  GETDATA;
  struct Attach *attach = NULL;

  ENTER();

  // first we get the active entry
  DoMethod(data->LV_ATTACH, MUIM_NList_GetEntry, MUIV_NList_GetEntry_Active, &attach);
  if(attach != NULL)
  {
    // then we remove the active entry from the NList
    DoMethod(data->LV_ATTACH, MUIM_NList_Remove, MUIV_NList_Remove_Active);

    // delete the temporary file if exists
    if(attach->IsTemp == TRUE)
      DeleteFile(attach->FilePath);

    // the mail is modified
    data->mailModified = TRUE;
  }

  RETURN(0);
  return 0;
}

///
/// DECLARE(RenameAttachment)
// Renames a file from the attachment list
DECLARE(RenameAttachment)
{
  GETDATA;
  struct Attach *attach = NULL;

  ENTER();

  // first we get the active entry
  DoMethod(data->LV_ATTACH, MUIM_NList_GetEntry, MUIV_NList_GetEntry_Active, &attach);
  if(attach != NULL)
  {
    char newName[SIZE_FILE];

    strlcpy(newName, attach->Name, sizeof(newName));
    if(StringRequest(newName, sizeof(newName), tr(MSG_WR_RENAME_ATTACHMENT), tr(MSG_WR_ENTER_NEW_NAME), tr(MSG_Okay), NULL, tr(MSG_Cancel), FALSE, obj))
    {
      if(strcmp(attach->Name, newName) != 0)
      {
        strlcpy(attach->Name, newName, sizeof(attach->Name));

        DoMethod(data->LV_ATTACH, MUIM_NList_Redraw, MUIV_NList_Redraw_Active);

        // the mail is modified
        data->mailModified = TRUE;
      }
    }
  }

  RETURN(0);
  return 0;
}

///
/// DECLARE(DisplayAttachment)
// Displays an attached file using a MIME viewer
DECLARE(DisplayAttachment) // Object *originator
{
  struct Attach *attach = NULL;

  ENTER();

  DoMethod(msg->originator, MUIM_NList_GetEntry, MUIV_NList_GetEntry_Active, &attach);
  if(attach != NULL)
  {
    if(FileExists(attach->FilePath) == TRUE)
      RE_DisplayMIME(attach->FilePath, NULL, attach->ContentType, FALSE);
    else
      ER_NewError(tr(MSG_ER_INVALIDATTFILE), attach->FilePath);
  }

  RETURN(0);
  return 0;
}

///
/// DECLARE(GetAttachmentEntry)
// Fills form with data from selected list entry
DECLARE(GetAttachmentEntry)
{
  GETDATA;
  struct Attach *attach = NULL;

  ENTER();

  DoMethod(data->LV_ATTACH, MUIM_NList_GetEntry, MUIV_NList_GetEntry_Active, &attach);
  DoMethod(_app(obj), MUIM_MultiSet, MUIA_Disabled, attach ? FALSE : TRUE, data->PO_CTYPE,
                                                                           data->ST_DESC,
                                                                           data->BT_REMOVE,
                                                                           data->BT_RENAME,
                                                                           data->BT_DISPLAY, NULL);

  if(attach != NULL)
  {
    nnset(data->PO_CTYPE, MUIA_MimeTypePopup_MimeType, attach->ContentType);
    nnset(data->ST_DESC, MUIA_String_Contents, attach->Description);
  }

  RETURN(0);
  return 0;
}

///
/// DECLARE(PutAttachmentEntry)
// Fills form data into selected list entry
DECLARE(PutAttachmentEntry)
{
  GETDATA;
  struct Attach *attach = NULL;

  ENTER();

  DoMethod(data->LV_ATTACH, MUIM_NList_GetEntry, MUIV_NList_GetEntry_Active, &attach);
  if(attach != NULL)
  {
    strlcpy(attach->ContentType, (char *)xget(data->PO_CTYPE, MUIA_MimeTypePopup_MimeType), sizeof(attach->ContentType));
    GetMUIString(attach->Description, data->ST_DESC, sizeof(attach->Description));
    DoMethod(data->LV_ATTACH, MUIM_NList_Redraw, MUIV_NList_Redraw_Active);
  }

  RETURN(0);
  return 0;
}

///
/// DECLARE(SignatureChanged)
// Changes the current signature
DECLARE(SignatureChanged)
{
  GETDATA;
  struct TempFile *tfin;

  ENTER();

  if((tfin = OpenTempFile(NULL)) != NULL)
  {
    FILE *in;
    Object *editor = data->TE_EDIT;

    DoMethod(editor, MUIM_MailTextEdit_SaveToFile, tfin->Filename, NULL);
    if((in = fopen(tfin->Filename, "r")) != NULL)
    {
      struct TempFile *tfout;

      setvbuf(in, NULL, _IOFBF, SIZE_FILEBUF);

      // open a new temporary file for writing the new text
      if((tfout = OpenTempFile("w")) != NULL)
      {
        char *buf = NULL;
        size_t buflen = 0;
        ssize_t curlen;
        ULONG flags;

        while((curlen = getline(&buf, &buflen, in)) > 0)
        {
          if(strcmp(buf, "-- \n") == 0)
            break;

          fwrite(buf, curlen, 1, tfout->FP);
        }

        // add the signature or in case the signature is NULL
        // WriteSignature() will return immediatly
        WriteSignature(tfout->FP, (struct SignatureNode *)xget(data->CY_SIGNATURE, MUIA_SignatureChooser_Signature), TRUE);

        // now our out file is finished, so we can
        // put everything in our text editor.
        fclose(tfout->FP);
        tfout->FP = NULL;

        // free the buffer
        free(buf);

        // put everything in the editor
        flags = MUIF_NONE;
        if(data->useTextStyles == TRUE)
          setFlag(flags, MUIF_MailTextEdit_LoadFromFile_UseStyles);
        if(data->useTextColors == TRUE)
          setFlag(flags, MUIF_MailTextEdit_LoadFromFile_UseColors);
        if(xget(editor, MUIA_TextEditor_HasChanged))
          setFlag(flags, MUIF_MailTextEdit_LoadFromFile_SetChanged);

        DoMethod(editor, MUIM_MailTextEdit_LoadFromFile, tfout->Filename, NULL, flags);

        // make sure the temp file is deleted
        CloseTempFile(tfout);
      }

      fclose(in);
    }

    CloseTempFile(tfin);
  }

  RETURN(0);
  return 0;
}

///
/// DECLARE(SetSoftStyle)
DECLARE(SetSoftStyle) // enum SoftStyleMode ssm, ULONG origin
{
  GETDATA;
  char *txt;

  ENTER();

  // retrieve the whole text lines where the cursor/block is currently active.
  if((txt = (char *)DoMethod(data->TE_EDIT, MUIM_TextEditor_ExportBlock, MUIF_TextEditor_ExportBlock_FullLines)))
  {
    int txtlen = strlen(txt);

    if(txtlen > 0)
    {
      ULONG x1=0;
      ULONG y1=0;
      ULONG x2=0;
      ULONG y2=0;
      ULONG firstChar=0;
      ULONG lastChar=0;
      LONG nx1;
      LONG nx2;
      ULONG ny1;
      char marker[2] = " ";
      BOOL enableStyle = FALSE;
      ULONG toolbarStore = 0;
      LONG addedChars = 0;
      LONG lastAddedChars = 0;

      D(DBF_GUI, "txt '%s', origin %ld", txt, msg->origin);

      // define the marker for the
      // selected soft-style and check if
      // we should enable or disable the style
      switch(msg->ssm)
      {
        case SSM_NORMAL:
          // nothing;
        break;

        case SSM_BOLD:
        {
          marker[0] = '*';
          if(msg->origin == ORIGIN_MENU)
            enableStyle = (xget(data->MI_BOLD, MUIA_Menuitem_Checked) == TRUE);
          else if(msg->origin == ORIGIN_TOOLBAR)
          {
            DoMethod(data->TO_TOOLBAR, MUIM_TheBar_GetAttr, TB_WRITE_BOLD, MUIA_TheBar_Attr_Selected, &toolbarStore);
            enableStyle = (toolbarStore != 0);
          }
        }
        break;

        case SSM_ITALIC:
        {
          marker[0] = '/';
          if(msg->origin == ORIGIN_MENU)
            enableStyle = (xget(data->MI_ITALIC, MUIA_Menuitem_Checked) == TRUE);
          else if(msg->origin == ORIGIN_TOOLBAR)
          {
            DoMethod(data->TO_TOOLBAR, MUIM_TheBar_GetAttr, TB_WRITE_ITALIC, MUIA_TheBar_Attr_Selected, &toolbarStore);
            enableStyle = (toolbarStore != 0);
          }
        }
        break;

        case SSM_UNDERLINE:
        {
          marker[0] = '_';
          if(msg->origin == ORIGIN_MENU)
            enableStyle = (xget(data->MI_UNDERLINE, MUIA_Menuitem_Checked) == TRUE);
          else if(msg->origin == ORIGIN_TOOLBAR)
          {
            DoMethod(data->TO_TOOLBAR, MUIM_TheBar_GetAttr, TB_WRITE_UNDERLINE, MUIA_TheBar_Attr_Selected, &toolbarStore);
            enableStyle = (toolbarStore != 0);
          }
        }
        break;

        case SSM_COLOR:
        {
          marker[0] = '#';
          if(msg->origin == ORIGIN_MENU)
            enableStyle = (xget(data->MI_COLORED, MUIA_Menuitem_Checked) == TRUE);
          else if(msg->origin == ORIGIN_TOOLBAR)
          {
            DoMethod(data->TO_TOOLBAR, MUIM_TheBar_GetAttr, TB_WRITE_COLORED, MUIA_TheBar_Attr_Selected, &toolbarStore);
            enableStyle = (toolbarStore != 0);
          }
        }
        break;
      }

      D(DBF_GUI, "marker '%s', enable %ld, toolbarStore %ld", marker, enableStyle, toolbarStore);

      // check/get the status of the marked area
      if(DoMethod(data->TE_EDIT, MUIM_TextEditor_BlockInfo, &x1, &y1, &x2, &y2) == FALSE)
      {
        x1 = xget(data->TE_EDIT, MUIA_TextEditor_CursorX);
        y1 = xget(data->TE_EDIT, MUIA_TextEditor_CursorY);
        x2 = 0;
        y2 = y1;
      }

      // set the n* values we use to parse
      // through the retrieved txt
      nx1=x1;
      nx2=x1;
      ny1=y1;

      // we first walk back from the starting (nx1) position searching for the a
      // space or the start of line
      while(nx1 > 0 && !isspace(txt[nx1]))
        nx1--;

      if(nx1 > 0 && (ULONG)nx1 != x1)
        nx1++;

      // now we search forward from nx2 and find the end of the word
      // by searching for the next space
      while(nx2 < txtlen && !isspace(txt[nx2]))
        nx2++;

      // lets set x1 to the actual nx1 we just identified
      firstChar = nx1;
      lastChar = nx2;

      // make the texteditor be quiet during our
      // operations
      set(data->TE_EDIT, MUIA_TextEditor_Quiet, TRUE);

      // prepare to walk through our selected area
      do
      {
        // if we found a string between nx1 and nx2 we can now
        // enable or disable the style for that particular area
        if(nx2-nx1 > 0)
        {
          char *ntxt = NULL;

          if(enableStyle == TRUE)
          {
            // check if there is already some soft-style active
            // and if yes, we go and strip them
            if(msg->ssm == SSM_NORMAL ||
               (txt[nx1] != marker[0] || txt[nx2-1] != marker[0]))
            {
              if((ntxt = calloc(1, nx2-nx1+2+1)))
              {
                ntxt[0] = marker[0];

                // strip foreign soft-style markers
                if((txt[nx1] == '*' && txt[nx2-1] == '*') ||
                   (txt[nx1] == '/' && txt[nx2-1] == '/') ||
                   (txt[nx1] == '_' && txt[nx2-1] == '_') ||
                   (txt[nx1] == '#' && txt[nx2-1] == '#'))
                {
                  strlcat(ntxt, &txt[nx1+1], nx2-nx1);
                }
                else
                {
                  strlcat(ntxt, &txt[nx1], nx2-nx1+2);
                  addedChars += 2;
                }

                strlcat(ntxt, marker, nx2-nx1+2+1);
              }
            }
          }
          else
          {
            // check if there is already some soft-style active
            // and if yes, we go and strip them
            if(msg->ssm != SSM_NORMAL &&
               (txt[nx1] == marker[0] && txt[nx2-1] == marker[0]))
            {
              if((ntxt = calloc(1, nx2-2-nx1+1+1)))
                strlcpy(ntxt, &txt[nx1+1], nx2-2-nx1+1);

              addedChars -= 2;
            }
          }

          if(ntxt)
          {
            D(DBF_GUI, "ntxt: '%s' : %ld %ld", ntxt, enableStyle, addedChars);

            // now we mark the area we found to be the one we want to
            // set bold/italic whatever
            DoMethod(data->TE_EDIT, MUIM_TextEditor_MarkText, nx1+lastAddedChars, ny1, nx2+lastAddedChars, ny1);

            // now we replace the marked area with the text we extract but adding two
            // bold signs at the back and setting the area bold
            DoMethod(data->TE_EDIT, MUIM_TextEditor_Replace, ntxt, MUIF_NONE);

            // set the soft-style accordingly.
            if(enableStyle == TRUE)
            {
              DoMethod(data->TE_EDIT, MUIM_TextEditor_MarkText, nx1+lastAddedChars, ny1, nx1+lastAddedChars+strlen(ntxt), ny1);

              // make sure the text in question is really in the style we want
              switch(msg->ssm)
              {
                case SSM_NORMAL:
                {
                  xset(data->TE_EDIT, MUIA_TextEditor_StyleBold,      FALSE,
                                      MUIA_TextEditor_StyleItalic,    FALSE,
                                      MUIA_TextEditor_StyleUnderline, FALSE,
                                      MUIA_TextEditor_Pen,            0);
                }
                break;

                case SSM_BOLD:
                {
                  xset(data->TE_EDIT, MUIA_TextEditor_StyleBold,      data->useTextStyles,
                                      MUIA_TextEditor_StyleItalic,    FALSE,
                                      MUIA_TextEditor_StyleUnderline, FALSE,
                                      MUIA_TextEditor_Pen,            0);
                }
                break;

                case SSM_ITALIC:
                {
                  xset(data->TE_EDIT, MUIA_TextEditor_StyleBold,      FALSE,
                                      MUIA_TextEditor_StyleItalic,    data->useTextStyles,
                                      MUIA_TextEditor_StyleUnderline, FALSE,
                                      MUIA_TextEditor_Pen,            0);
                }
                break;

                case SSM_UNDERLINE:
                {
                  xset(data->TE_EDIT, MUIA_TextEditor_StyleBold,      FALSE,
                                      MUIA_TextEditor_StyleItalic,    FALSE,
                                      MUIA_TextEditor_StyleUnderline, data->useTextStyles,
                                      MUIA_TextEditor_Pen,            0);
                }
                break;

                case SSM_COLOR:
                {
                  xset(data->TE_EDIT, MUIA_TextEditor_StyleBold,      FALSE,
                                      MUIA_TextEditor_StyleItalic,    FALSE,
                                      MUIA_TextEditor_StyleUnderline, FALSE,
                                      MUIA_TextEditor_Pen,            data->useTextColors == TRUE ? 7 : 0);
                }
                break;
              }
            }

            free(ntxt);
          }

          lastChar = nx2+addedChars;
          lastAddedChars = addedChars;
        }
        else
          break;

        // now we check wheter there is more to process or not.
        if(x2 > (ULONG)nx2+1)
        {
          nx1 = ++nx2; // set nx1 to end of last string

          // now we search forward from nx1 and find the start of the
          // next word
          while(nx1 < txtlen && isspace(txt[nx1]))
            nx1++;

          // search for the end of the found word
          nx2 = nx1;
          while(nx2 < txtlen && !isspace(txt[nx2]))
            nx2++;
        }
        else
          nx2 = nx1;
      }
      while(TRUE);

      // mark all text we just processed
      DoMethod(data->TE_EDIT, MUIM_TextEditor_MarkText, firstChar, ny1, lastChar, ny1);

      // turn the texteditor back on
      set(data->TE_EDIT, MUIA_TextEditor_Quiet, FALSE);
    }

    FreeVec(txt);
  }

  RETURN(0);
  return 0;
}

///
/// DECLARE(StyleOptionsChanged)
DECLARE(StyleOptionsChanged)
{
  GETDATA;
  BOOL updateText = FALSE;
  BOOL tmp;

  ENTER();

  // check useTextcolors diff
  if((tmp = xget(data->MI_TCOLOR, MUIA_Menuitem_Checked)) != data->useTextColors)
  {
    data->useTextColors = tmp;
    updateText = TRUE;
  }

  // check useTextstyles diff
  if((tmp = xget(data->MI_TSTYLE, MUIA_Menuitem_Checked)) != data->useTextStyles)
  {
    data->useTextStyles = tmp;
    updateText = TRUE;
  }

  // check useFixedFont diff
  if((tmp = xget(data->MI_FFONT, MUIA_Menuitem_Checked)) != data->useFixedFont)
  {
    data->useFixedFont = tmp;

    // update the font settings of TE_EDIT
    set(data->TE_EDIT, MUIA_TextEditor_FixedFont, data->useFixedFont);
  }

  // issue an update of the readMailGroup's components
  if(updateText == TRUE)
  {
    char *orgtext;

    // get the original text from TE_EDIT
    orgtext = (char *)DoMethod(data->TE_EDIT, MUIM_TextEditor_ExportText);

    if(orgtext != NULL)
    {
      char *parsedText;

      // parse the text and do some highlighting and stuff
      if((parsedText = ParseEmailText(orgtext, FALSE, data->useTextStyles, data->useTextColors)) != NULL)
      {
        // set the new text and preserve the changed status
        set(data->TE_EDIT, MUIA_TextEditor_Contents, parsedText);

        dstrfree(parsedText);
      }

      FreeVec(orgtext); // use FreeVec() because TextEditor.mcc uses AllocVec()
    }
    else
      E(DBF_GUI, "couldn't export text from TE_EDIT");
  }

  RETURN(0);
  return 0;
}
///

/* Public Methods */
/// DECLARE(SaveTextAs)
// Saves contents of internal editor to a file
DECLARE(SaveTextAs)
{
  GETDATA;
  struct FileReqCache *frc;

  ENTER();

  if(xget(obj, MUIA_Window_Open) == TRUE)
  {
    // don't trigger notifications as this will change the active object
    nnset(data->RG_PAGE, MUIA_Group_ActivePage, 0);
  }

  if((frc = ReqFile(ASL_ATTACH, obj, tr(MSG_WR_SaveTextAs), REQF_SAVEMODE, C->AttachDir, "")))
  {
    char filename[SIZE_PATHFILE];

    AddPath(filename, frc->drawer, frc->file, sizeof(filename));

    if(FileExists(filename) == FALSE ||
       MUI_Request(_app(obj), obj, MUIF_NONE, tr(MSG_MA_ConfirmReq), tr(MSG_YesNoReq), tr(MSG_FILE_OVERWRITE), frc->file) != 0)
    {
      if(DoMethod(data->TE_EDIT, MUIM_MailTextEdit_SaveToFile, filename, data->wmData->codeset) == FALSE)
        ER_NewError(tr(MSG_ER_CantCreateFile), filename);
    }
  }

  RETURN(0);
  return 0;
}

///
/// DECLARE(AddAttachment)
// Adds a file to the attachment list, gets its size and type
DECLARE(AddAttachment) // const char *filename, const char *name, ULONG istemp
{
  GETDATA;
  BOOL result = FALSE;

  ENTER();

  if(msg->filename != NULL)
  {
    static struct Attach attach;
    const char *ctype;
    #if defined(__amigaos4__)
    struct ExamineData *ed;
    #else
    BPTR lock;
    #endif

    memset(&attach, 0, sizeof(struct Attach));

    // we try to get the filesize and comment and copy
    // it into our attchement structure.
    #if defined(__amigaos4__)
    if((ed = ExamineObjectTags(EX_StringName, msg->filename, TAG_DONE)) != NULL)
    {
      attach.Size = ed->FileSize;
      strlcpy(attach.Description, ed->Comment, sizeof(attach.Description));

      // check the dir type and protection
      if(EXD_IS_FILE(ed) && isFlagClear(ed->Protection, EXDF_READ))
        result = TRUE;

      FreeDosObject(DOS_EXAMINEDATA, ed);
    }
    #else
    if((lock = Lock((STRPTR)msg->filename, ACCESS_READ)))
    {
      struct FileInfoBlock *fib;

      if((fib = AllocDosObject(DOS_FIB, NULL)))
      {
        if(Examine(lock, fib))
        {
          attach.Size = fib->fib_Size;
          strlcpy(attach.Description, fib->fib_Comment, sizeof(attach.Description));

          // check the dir type and protection
          if(fib->fib_DirEntryType < 0 &&
             isFlagClear(fib->fib_Protection, FIBF_READ))
          {
            result = TRUE;
          }
        }

        FreeDosObject(DOS_FIB, fib);
      }

      UnLock(lock);
    }
    #endif

    // now we try to find out the content-type of the
    // attachment by using the IdentifyFile() function which will
    // do certain checks to guess the content-type out of the file name
    // and content.
    if(result == TRUE && (ctype = IdentifyFile(msg->filename)) != NULL && ctype[0] != '\0')
    {
      attach.IsTemp = msg->istemp;
      strlcpy(attach.FilePath, msg->filename, sizeof(attach.FilePath));
      strlcpy(attach.Name, msg->name ? msg->name : (char *)FilePart(msg->filename), sizeof(attach.Name));
      strlcpy(attach.ContentType, ctype, sizeof(attach.ContentType));
      nnset(data->PO_CTYPE, MUIA_MimeTypePopup_MimeType, attach.ContentType);
      nnset(data->ST_DESC, MUIA_String_Contents, attach.Description);

      // put the attachment into our attachment list and set it active
      DoMethod(obj, METHOD(InsertAttachment), &attach);

      // the mail is modified
      data->mailModified = TRUE;
    }
    else
    {
      ER_NewError(tr(MSG_ER_ADDFILEERROR), msg->filename);

      result = FALSE;
    }
  }

  RETURN(result);
  return result;
}
///
/// DECLARE(InsertAttachment)
// Insert an attachment directly with a preconfigured struct Attach
DECLARE(InsertAttachment) // struct Attach *attach
{
  GETDATA;
  ULONG result;

  ENTER();

  result = DoMethod(data->LV_ATTACH, MUIM_NList_InsertSingle, msg->attach, MUIV_NList_Insert_Bottom);
  set(data->LV_ATTACH, MUIA_NList_Active, MUIV_NList_Active_Bottom);
  set(data->LV_ATTACH_TINY, MUIA_NList_Active, MUIV_NList_Active_Bottom);
  set(data->GR_ATTACH_TINY, MUIA_ShowMe, TRUE);
  set(data->GR_ATTACH_REMIND, MUIA_ShowMe, FALSE);

  RETURN(result);
  return result;
}

///
/// DECLARE(AddMailAttachment)
// add a mail as attachment
DECLARE(AddMailAttachment) // struct Mail *mail
{
  BOOL result = FALSE;

  ENTER();

  if(msg->mail != NULL)
  {
    char filename[SIZE_PATHFILE];
    struct Attach attach;

    memset(&attach, 0, sizeof(struct Attach));

    GetMailFile(filename, sizeof(filename), NULL, msg->mail);
    if(StartUnpack(filename, attach.FilePath, msg->mail->Folder) != NULL)
    {
      snprintf(attach.Name, sizeof(attach.Name), "%s.eml", msg->mail->Subject);
      strlcpy(attach.Description, msg->mail->Subject, sizeof(attach.Description));
      strlcpy(attach.ContentType, "message/rfc822", sizeof(attach.ContentType));
      attach.Size = msg->mail->Size;

      // add the attachment to our attachment list
      DoMethod(obj, METHOD(InsertAttachment), &attach);

      result = TRUE;
    }
    else
      E(DBF_MAIL, "unpacking of file '%s' failed!", filename);
  }

  RETURN(result);
  return result;
}

///
/// DECLARE(AddSignature)
//  Adds a signature to the end of the file
DECLARE(AddSignature)
{
  GETDATA;
  struct SignatureNode *sn;
  struct SignatureNode *signature = NULL;

  ENTER();

  // now we iterate through the signatureList
  // for picking the signature from the currently
  // selected identity
  IterateList(&C->signatureList, struct SignatureNode *, sn)
  {
    if(sn == data->wmData->identity->signature)
    {
      signature = data->wmData->identity->signature;
      break;
    }
  }

  if(signature != NULL)
  {
    FILE *fh_mail;
    BOOL addline = FALSE;

    if((fh_mail = fopen(data->wmData->filename, "r")) != NULL)
    {
      fseek(fh_mail, -1, SEEK_END);
      addline = fgetc(fh_mail) != '\n';
      fclose(fh_mail);
    }

    if((fh_mail = fopen(data->wmData->filename, "a")) != NULL)
    {
      setvbuf(fh_mail, NULL, _IOFBF, SIZE_FILEBUF);

      if(addline)
        fputc('\n', fh_mail);

      WriteSignature(fh_mail, signature, TRUE);
      fclose(fh_mail);
    }
  }

  // lets set the signature cycle gadget
  // accordingly to the set signature
  nnset(data->CY_SIGNATURE, MUIA_SignatureChooser_Signature, signature);

  RETURN(0);
  return 0;
}

///
/// DECLARE(AddRecipient)
//  Adds a recipient to one of the recipient types (To/Cc/BCc)
DECLARE(AddRecipient) // enum RcptType type, char *recipient
{
  GETDATA;
  ENTER();

  switch(msg->type)
  {
    case MUIV_WriteWindow_RcptType_From:
    {
      struct UserIdentityNode *uin;

      // try to match the identity by search through our user identities
      if((uin = FindUserIdentityByAddress(&C->userIdentityList, msg->recipient)) != NULL)
        set(data->CY_FROM, MUIA_IdentityChooser_Identity, uin);
      else
        DisplayBeep(_screen(obj));
    }
    break;

    case MUIV_WriteWindow_RcptType_FromOverride:
      DoMethod(data->ST_FROM_OVERRIDE, MUIM_RecipientString_AddRecipient, msg->recipient);
    break;

    case MUIV_WriteWindow_RcptType_To:
      DoMethod(data->ST_TO, MUIM_RecipientString_AddRecipient, msg->recipient);
    break;

    case MUIV_WriteWindow_RcptType_CC:
      DoMethod(data->ST_CC, MUIM_RecipientString_AddRecipient, msg->recipient);
      DoMethod(obj, METHOD(ShowRecipientObject), MUIV_WriteWindow_RcptType_CC);
    break;

    case MUIV_WriteWindow_RcptType_BCC:
      DoMethod(data->ST_BCC, MUIM_RecipientString_AddRecipient, msg->recipient);
      DoMethod(obj, METHOD(ShowRecipientObject), MUIV_WriteWindow_RcptType_BCC);
    break;

    case MUIV_WriteWindow_RcptType_ReplyTo:
      DoMethod(data->ST_REPLYTO, MUIM_RecipientString_AddRecipient, msg->recipient);
      DoMethod(obj, METHOD(ShowRecipientObject), MUIV_WriteWindow_RcptType_ReplyTo);
    break;
  }

  RETURN(0);
  return 0;
}

///
/// DECLARE(InsertAddresses)
//  Appends an array of addresses to a string gadget
DECLARE(InsertAddresses) // enum RcptType type, char **addr, ULONG add
{
  GETDATA;
  Object *str = NULL;

  ENTER();

  ENTER();

  switch(msg->type)
  {
    case MUIV_WriteWindow_RcptType_From:
    {
      struct UserIdentityNode *uin;

      // we try to match the addresses stored in **addr with the address
      // of our user identities. But as there can be only ONE address
      // we break out as soon as we have found one and beep otherwise
      do
      {
        // try to match the identity by search through our user identities
        if((uin = FindUserIdentityByAddress(&C->userIdentityList, *(msg->addr))) != NULL)
        {
          set(data->CY_FROM, MUIA_IdentityChooser_Identity, uin);
          break;
        }

        ++msg->addr;
      }
      while(*(msg->addr) != NULL);

      if(uin == NULL)
        DisplayBeep(_screen(obj));
    }
    break;

    case MUIV_WriteWindow_RcptType_FromOverride:
      str = data->ST_FROM_OVERRIDE;
    break;

    case MUIV_WriteWindow_RcptType_To:
      str = data->ST_TO;
    break;

    case MUIV_WriteWindow_RcptType_CC:
      str = data->ST_CC;
      DoMethod(obj, METHOD(ShowRecipientObject), MUIV_WriteWindow_RcptType_CC);
    break;

    case MUIV_WriteWindow_RcptType_BCC:
      str = data->ST_BCC;
      DoMethod(obj, METHOD(ShowRecipientObject), MUIV_WriteWindow_RcptType_BCC);
    break;

    case MUIV_WriteWindow_RcptType_ReplyTo:
      str = data->ST_REPLYTO;
      DoMethod(obj, METHOD(ShowRecipientObject), MUIV_WriteWindow_RcptType_ReplyTo);
    break;
  }

  if(str != NULL)
  {
    if(msg->add == FALSE)
      setstring(str, "");

    do
    {
      DoMethod(str, MUIM_RecipientString_AddRecipient, *(msg->addr));
      ++msg->addr;
    }
    while(*(msg->addr) != NULL);
  }

  RETURN(0);
  return 0;
}

///
/// DECLARE(LaunchEditor)
// Launches external editor with message text
DECLARE(LaunchEditor)
{
  GETDATA;

  ENTER();

  if(C->Editor[0] != '\0')
  {
    struct WriteMailData *wmData = data->wmData;
    char buffer[SIZE_COMMAND+SIZE_PATHFILE];

    // stop any pending file notification.
    if(wmData->fileNotifyActive == TRUE)
    {
      EndNotify(wmData->notifyRequest);
      wmData->fileNotifyActive = FALSE;
    }

    // Workaround for a MUI bug
    if(xget(obj, MUIA_Window_Open) == TRUE)
    {
      // don't trigger notifications as this will change the active object
      nnset(data->RG_PAGE, MUIA_Group_ActivePage, 0);
    }

    // save the mail text in the currently selected codeset
    DoMethod(data->TE_EDIT, MUIM_MailTextEdit_SaveToFile, data->wmData->filename, G->editorCodeset);

    // remember the modification date of the file
    if(ObtainFileInfo(data->wmData->filename, FI_DATE, &data->wmData->lastFileChangeTime) == FALSE)
    {
      // use the current time in case ObtainFileInfo() failed
      DateStamp(&data->wmData->lastFileChangeTime);
    }

    snprintf(buffer, sizeof(buffer), "%s \"%s\"", C->Editor, GetRealPath(wmData->filename));
    LaunchCommand(buffer, LAUNCHF_ASYNC, OUT_NIL);

    // (re)start the file notification on the temporary write window
    // content file
    if(StartNotify(wmData->notifyRequest) != 0)
    {
      D(DBF_UTIL, "started notification request for file: '%s' of write window %ld", wmData->filename, data->windowNumber);
      wmData->fileNotifyActive = TRUE;
    }
    else
      W(DBF_UTIL, "file notification [%s] of write window %ld failed!", wmData->filename, data->windowNumber);
  }
  else
    DisplayBeep(_screen(obj));

  RETURN(0);
  return 0;
}

///
/// DECLARE(EditorCmd)
// Inserts file or clipboard into editor
DECLARE(EditorCmd) // enum TransformMode cmd
{
  GETDATA;
  char *text = NULL;
  char *quoteChar;
  char filename[SIZE_PATHFILE];
  struct TempFile *tf;

  ENTER();

  quoteChar = (msg->cmd == ED_INSALTQUOT || msg->cmd == ED_PASALTQUOT) ? C->AltQuoteChar : C->QuoteChar;

  switch(msg->cmd)
  {
    case ED_INSERT:
    case ED_INSQUOT:
    case ED_INSALTQUOT:
    case ED_INSROT13:
    case ED_OPEN:
    {
      struct FileReqCache *frc;

      if((frc = ReqFile(ASL_ATTACH, obj, tr(MSG_WR_InsertFile), REQF_NONE, C->AttachDir, "")))
      {
        AddPath(filename, frc->drawer, frc->file, sizeof(filename));
        text = TransformText(filename, msg->cmd, quoteChar);
      }
      else
      {
        RETURN(0);
        return 0;
      }
    }
    break;

    // the user wants to insert a file as
    // a uuencoded text passage.
    case ED_INSUUCODE:
    {
      struct FileReqCache *frc;

      // first we request the filename of the file we want to its
      // uuencoded interpretation to be inserted into the editor
      if((frc = ReqFile(ASL_ATTACH, obj, tr(MSG_WR_InsertFile), REQF_NONE, C->AttachDir, "")))
      {
        AddPath(filename, frc->drawer, frc->file, sizeof(filename));

        // open a temporary file
        if((tf = OpenTempFile("w")))
        {
          FILE *in;

          // open both files and start the uuencoding
          if((in = fopen(filename, "r")))
          {
            setvbuf(in, NULL, _IOFBF, SIZE_FILEBUF);

            // lets uuencode the file now.
            if(uuencode_file(in, tf->FP) > 0)
            {
              // we successfully encoded the file, so lets
              // close our tempfile file pointer and call the tranformtext function
              fclose(tf->FP);
              tf->FP = NULL;

              // now transform the text
              text = TransformText(tf->Filename, msg->cmd, filename);
            }

            fclose(in);
          }

          CloseTempFile(tf);
        }
      }
    }
    break;

    default:
    {
      if((tf = OpenTempFile("w")))
      {
        DumpClipboard(tf->FP);
        fclose(tf->FP);
        tf->FP = NULL;
        text = TransformText(tf->Filename, msg->cmd, quoteChar);
        CloseTempFile(tf);
      }
      else
      {
        RETURN(0);
        return 0;
      }
    }
    break;
  }

  if(text != NULL)
  {
    if(msg->cmd == ED_OPEN)
      DoMethod(data->TE_EDIT, MUIM_TextEditor_ClearText);

    DoMethod(data->TE_EDIT, MUIM_TextEditor_InsertText, text, MUIV_TextEditor_InsertText_Cursor);

    // free our allocated text
    free(text);
  }

  RETURN(0);
  return 0;
}

///
/// DECLARE(ArexxCommand)
// forward a string command to the TE.mcc object
DECLARE(ArexxCommand) // char *command
{
  GETDATA;
  ULONG result;
  ENTER();

  result = DoMethod(data->TE_EDIT, MUIM_TextEditor_ARexxCmd, msg->command);

  RETURN(result);
  return result;
}

///
/// DECLARE(Search)
// Opens a search subwindow
DECLARE(Search) // ULONG flags
{
  GETDATA;
  ENTER();

  if(data->WI_SEARCH == NULL)
  {
    if((data->WI_SEARCH = SearchTextWindowObject, End))
    {
      // perform the search operation
      DoMethod(data->WI_SEARCH, MUIM_SearchTextWindow_Open, data->TE_EDIT);
    }
  }
  else
  {
    if(hasSearchAgainFlag(msg->flags))
      DoMethod(data->WI_SEARCH, MUIM_SearchTextWindow_Next);
    else
      DoMethod(data->WI_SEARCH, MUIM_SearchTextWindow_Open, data->TE_EDIT);
  }

  RETURN(0);
  return 0;
}

///
/// DECLARE(InsertText)
// Insert a certain text into the editor
DECLARE(InsertText) // char *text
{
  GETDATA;
  ENTER();

  DoMethod(data->TE_EDIT, MUIM_TextEditor_InsertText, msg->text, MUIV_TextEditor_InsertText_Cursor);

  RETURN(0);
  return 0;
}

///
/// DECLARE(LoadText)
// Load the message text from a specified file
// or reload it from the former file
DECLARE(LoadText) // char *filename, ULONG changed
{
  GETDATA;
  BOOL result;
  ULONG flags;

  ENTER();

  flags = MUIF_NONE;
  if(msg->changed != FALSE)
    setFlag(flags, MUIF_MailTextEdit_LoadFromFile_SetChanged);
  if(data->useTextStyles == TRUE)
    setFlag(flags, MUIF_MailTextEdit_LoadFromFile_UseStyles);
  if(data->useTextColors == TRUE)
    setFlag(flags, MUIF_MailTextEdit_LoadFromFile_UseColors);

  if((result = DoMethod(data->TE_EDIT, MUIM_MailTextEdit_LoadFromFile, (msg->filename != NULL) ? msg->filename : data->wmData->filename, NULL, flags)) == TRUE)
  {
    // update the cursor position
    DoMethod(obj, METHOD(UpdateCursorPos));
  }

  RETURN(result);
  return (ULONG)result;
}

///
/// DECLARE(SetupFromOldMail)
// When editing a message, sets write window options to old values ***/
DECLARE(SetupFromOldMail) // struct ReadMailData *rmData
{
  struct Part *part;

  ENTER();

  // we start to iterate right from the first part *after* PART_RAW
  // and check which one really is really an attachment or which
  // one is the LetterPart
  for(part = msg->rmData->firstPart->Next; part; part = part->Next)
  {
    if(part->Nr == msg->rmData->letterPartNum)
    {
      // use the CodesetChanged method to signal that the write
      // window should select the codeset of the letterPart of the
      // old mail
      DoMethod(obj, METHOD(CodesetChanged), part->CParCSet);
    }

    if(part->Nr != msg->rmData->letterPartNum &&
       stricmp(part->ContentType, "application/pgp-signature"))
    {
      struct Attach attach;
      struct BusyNode *busy;

      memset(&attach, 0, sizeof(struct Attach));

      busy = BusyBegin(BUSY_TEXT);
      BusyText(busy, tr(MSG_BusyDecSaving), "");

      RE_DecodePart(part);

      attach.Size = part->Size;
      attach.IsTemp = TRUE;

      if(part->Name)
        strlcpy(attach.Name, part->Name, sizeof(attach.Name));

      strlcpy(attach.FilePath, part->Filename, sizeof(attach.FilePath));
      *part->Filename = '\0';
      strlcpy(attach.ContentType, part->ContentType, sizeof(attach.ContentType));
      strlcpy(attach.Description, part->Description, sizeof(attach.Description));

      DoMethod(obj, METHOD(InsertAttachment), &attach);

      BusyEnd(busy);
    }
  }

  RETURN(0);
  return 0;
}

///
/// DECLARE(GetAttachment)
DECLARE(GetAttachment) // int num, struct Attach *att
{
  GETDATA;
  ULONG result;
  ENTER();

  result = DoMethod(data->LV_ATTACH, MUIM_NList_GetEntry, msg->num, msg->att);

  RETURN(result);
  return result;
}

///
/// DECLARE(ComposeMail)
//  Validates write window options and generates a new message
DECLARE(ComposeMail) // enum WriteMode mode, ULONG closeWindow
{
  GETDATA;
  struct Compose comp;
  struct MailList *newMailList = NULL;
  struct MailList *refMailList = NULL;
  char *addr;
  int numAttachments = 0;
  struct Folder *outfolder;
  BOOL winOpen = xget(obj, MUIA_Window_Open);
  struct WriteMailData *wmData = data->wmData;
  enum WriteMode mode = msg->mode;
  struct FolderNode *fnode;
  struct Folder *mlFolder = NULL;
  ULONG success = FALSE;

  ENTER();

  D(DBF_MAIL, "write mode %ld", mode);

  // clear some variables we fill up later on
  memset(&comp, 0, sizeof(struct Compose));

  // first we check all input values and fill up
  // the struct Compose structure

  // retrieve the From: address override but only
  // if the user has enabled the Rcpt gadget
  if(data->fromOverrideRcptHidden == FALSE)
    comp.FromOverride = (char *)xget(data->ST_FROM_OVERRIDE, MUIA_String_Contents);

  if(mode == WRITE_DRAFT)
  {
    // Drafts mail will always save the recipients as they are, no matter if they are valid or not
    comp.MailTo = (char *)xget(data->ST_TO, MUIA_String_Contents);
    comp.Subject = (char *)xget(data->ST_SUBJECT, MUIA_String_Contents);
    comp.MailCC = (char *)xget(data->ST_CC, MUIA_String_Contents);
    comp.MailBCC = (char *)xget(data->ST_BCC, MUIA_String_Contents);
    comp.ReplyTo = (char *)xget(data->ST_REPLYTO, MUIA_String_Contents);
  }
  else
  {
    // get the contents of the TO: String gadget and check if it is valid
    addr = (char *)DoMethod(data->ST_TO, MUIM_RecipientString_Resolve, MUIF_RecipientString_Resolve_NoValid);
    if(addr == NULL)
    {
      ER_NewError(tr(MSG_ER_AliasNotFound), (STRPTR)xget(data->ST_TO, MUIA_String_Contents));

      if(winOpen == TRUE)
      {
        // don't trigger notifications as this will change the active object
        nnset(data->RG_PAGE, MUIA_Group_ActivePage, 0);
      }

      set(obj, MUIA_Window_ActiveObject, data->ST_TO);

      goto out;
    }
    else if(IsStrEmpty(addr) == TRUE && wmData->quietMode == FALSE)
    {
      // set the TO Field active and go back
      if(winOpen == TRUE)
      {
        // don't trigger notifications as this will change the active object
        nnset(data->RG_PAGE, MUIA_Group_ActivePage, 0);
      }

      set(obj, MUIA_Window_ActiveObject, data->ST_TO);

      if(MUI_Request(_app(obj), obj, MUIF_NONE, NULL, tr(MSG_WR_NORCPT_WARNING_GADGET), tr(MSG_WR_NORCPT_WARNING)) != 0)
      {
        // turn the action into writing a draft mail, but close the window nevertheless
        mode = WRITE_DRAFT;
      }
      else
        goto out;
    }
    else if(IsStrEmpty(addr) == FALSE)
    {
      // To: address
      comp.MailTo = addr;
    }

    // get the content of the Subject: String gadget and check if it is empty or not.
    comp.Subject = (char *)xget(data->ST_SUBJECT, MUIA_String_Contents);
    if(wmData->mode != NMM_REDIRECT && wmData->quietMode == FALSE && C->WarnSubject == TRUE &&
       IsStrEmpty(comp.Subject))
    {
      char subject[SIZE_SUBJECT];

      if(winOpen == TRUE)
      {
        // don't trigger notifications as this will change the active object
        nnset(data->RG_PAGE, MUIA_Group_ActivePage, 0);
      }

      set(obj, MUIA_Window_ActiveObject, data->ST_SUBJECT);

      subject[0] = '\0';
      if(StringRequest(subject, sizeof(subject), NULL, tr(MSG_WR_NOSUBJECTREQ), tr(MSG_Okay), NULL, tr(MSG_Cancel), FALSE, obj) <= 0)
      {
        goto out;
      }
      else
      {
        // copy the entered subject back to string object
        // this time we don't care whether a subject was entered or not
        set(data->ST_SUBJECT, MUIA_String_Contents, subject);
        comp.Subject = (char *)xget(data->ST_SUBJECT, MUIA_String_Contents);
      }
    }

    // then we check the CC string gadget
    addr = (char *)DoMethod(data->ST_CC, MUIM_RecipientString_Resolve, MUIF_RecipientString_Resolve_NoValid);
    if(addr == NULL)
    {
      ER_NewError(tr(MSG_ER_AliasNotFound), (STRPTR)xget(data->ST_CC, MUIA_String_Contents));

      if(winOpen == TRUE)
      {
        // don't trigger notifications as this will change the active object
        nnset(data->RG_PAGE, MUIA_Group_ActivePage, 0);
      }

      set(obj, MUIA_Window_ActiveObject, data->ST_CC);

      goto out;
    }
    else if(IsStrEmpty(addr) == FALSE)
    {
      comp.MailCC = addr;
    }

    // then we check the BCC string gadget
    addr = (char *)DoMethod(data->ST_BCC, MUIM_RecipientString_Resolve, MUIF_RecipientString_Resolve_NoValid);
    if(addr == NULL)
    {
      ER_NewError(tr(MSG_ER_AliasNotFound), (STRPTR)xget(data->ST_BCC, MUIA_String_Contents));

      if(winOpen == TRUE)
      {
        // don't trigger notifications as this will change the active object
        nnset(data->RG_PAGE, MUIA_Group_ActivePage, 0);
      }

      set(obj, MUIA_Window_ActiveObject, data->ST_BCC);

      goto out;
    }
    else if(IsStrEmpty(addr) == FALSE)
    {
      comp.MailBCC = addr;
    }
  }

  // set the write codeset for any mail to be written
  comp.codeset = wmData->codeset;

  // from here on a mail redirect window/operation doesn't need to take care
  // of reply-to, attachments checking, etc.
  if(wmData->mode != NMM_REDIRECT)
  {
    numAttachments = xget(data->LV_ATTACH, MUIA_NList_Entries);

    // remind the user if there are still no attachments
    if(mode != WRITE_DRAFT && data->attachmentRemind == MUIV_WriteWindow_AttachmentRemind_Later && numAttachments == 0)
    {
      if(MUI_Request(_app(obj), obj, MUIF_NONE, tr(MSG_WR_NO_ATTACHMENTS_TITLE), tr(MSG_WR_NO_ATTACHMENTS_GADS), tr(MSG_WR_NO_ATTACHMENTS)) != 0)
      {
        goto out;
      }
    }

    if(mode != WRITE_DRAFT)
    {
      // then we check the ReplyTo string gadget
      addr = (char *)DoMethod(data->ST_REPLYTO, MUIM_RecipientString_Resolve, MUIF_RecipientString_Resolve_NoValid);
      if(addr == NULL)
      {
        ER_NewError(tr(MSG_ER_AliasNotFound), (STRPTR)xget(data->ST_REPLYTO, MUIA_String_Contents));

        if(winOpen == TRUE)
        {
          // don't trigger notifications as this will change the active object
          nnset(data->RG_PAGE, MUIA_Group_ActivePage, 0);
        }

        set(obj, MUIA_Window_ActiveObject, data->ST_REPLYTO);

        goto out;
      }
      else if(IsStrEmpty(addr) == FALSE)
      {
        comp.ReplyTo = addr;
      }

      // now we search all To:,CC: and BCC: addresses and try to match them
      // against all mailing lists
      LockFolderListShared(G->folders);

      // walk through all folders and check if the
      // mailing list support matches
      ForEachFolderNode(G->folders, fnode)
      {
        struct Folder *curFolder = fnode->folder;

        if(curFolder != NULL && curFolder->MLSupport == TRUE &&
           curFolder->MLPattern[0] != '\0')
        {
          if((comp.MailTo != NULL && MatchNoCase(comp.MailTo, curFolder->MLPattern) == TRUE) ||
             (comp.MailCC != NULL && MatchNoCase(comp.MailCC, curFolder->MLPattern) == TRUE) ||
             (comp.MailBCC != NULL && MatchNoCase(comp.MailBCC, curFolder->MLPattern) == TRUE))
          {
            mlFolder = curFolder;
            break;
          }
        }
      }

      // if we found that a configued mailing list matches
      // we go and set Mail-Reply-To: and Mail-Followup-To:
      if(mlFolder != NULL && wmData->identity != NULL)
      {
        char address[SIZE_ADDRESS];
        comp.MailReplyTo = strdup(BuildAddress(address, sizeof(address), wmData->identity->address, wmData->identity->realname));
        if(IsStrEmpty(mlFolder->MLAddress) == FALSE)
          comp.MailFollowupTo = strdup(mlFolder->MLAddress);
      }

      // unlock the folder list again
      UnlockFolderList(G->folders);
    }

    // get the extra headers a user might have put into the mail
    comp.ExtHeader = (char *)xget(data->ST_EXTHEADER, MUIA_String_Contents);

    // In-Reply-To / References
    comp.inReplyToMsgID = wmData->inReplyToMsgID;
    comp.references = wmData->references;

    comp.Importance = 1-GetMUICycle(data->CY_IMPORTANCE);
    comp.RequestMDN = GetMUICheck(data->CH_MDN);
    comp.Signature = (struct SignatureNode *)xget(data->CY_SIGNATURE, MUIA_SignatureChooser_Signature);
    comp.Security = GetMUICycle(data->CY_SECURITY);
    comp.SelSecurity = comp.Security;

    if(comp.Security == SEC_DEFAULTS &&
       SetDefaultSecurity(&comp, obj) == FALSE)
    {
      goto out;
    }

    comp.DelSent = GetMUICheck(data->CH_DELSENT);
    comp.UserInfo = GetMUICheck(data->CH_ADDINFO);

    // we execute the POSTWRITE macro right before writing out
    // the message because the postwrite macro may want to modify the
    // text in the editor beforehand.
    DoMethod(_app(obj), MUIM_YAMApplication_StartMacro, MACRO_POSTWRITE, data->windowNumberStr);

    // export the text of our texteditor to a file in the currently selected codeset
    DoMethod(data->TE_EDIT, MUIM_MailTextEdit_SaveToFile, wmData->filename, wmData->codeset);

    // build the whole mail part list including the attachments
    // but don't delete temporary files when saving a draft mail
    if((comp.FirstPart = BuildPartsList(wmData, mode != WRITE_DRAFT)) == NULL)
    {
      goto out;
    }
  }
  else if(mode != WRITE_DRAFT)
    DoMethod(_app(obj), MUIM_YAMApplication_StartMacro, MACRO_POSTWRITE, data->windowNumberStr);

  comp.Mode = wmData->mode;
  comp.Identity = wmData->identity;

  outfolder = FO_GetFolderByType(mode == WRITE_DRAFT ? FT_DRAFTS : FT_OUTGOING, NULL);
  D(DBF_MAIL, "outfolder '%s'", outfolder->Name);

  ////////////////////////////////
  // now we have checked that all input data is actually present
  // and valid (e.g. GUI gadgets, etc.) so we check if this write window was
  // created with referencing any mails or if this is a new mail write window
  if((newMailList = CreateMailList()) != NULL)
  {
    struct MailNode *mnode;
    int cntRefMail = -1;

    // check if this write window has some associated
    // referenced mail
    if(wmData->refMailList != NULL)
      refMailList = CloneMailList(wmData->refMailList);
    else if((refMailList = CreateMailList()) != NULL)
      AddNewMailNode(refMailList, wmData->refMail);
    else
      goto out;

    // now iterate through the local refMailList
    ForEachMailNode(refMailList, mnode)
    {
      struct Mail *refMail = mnode->mail;
      struct Mail *newMail = NULL;
      char newMailFile[SIZE_PATHFILE];

      // count the number of reference
      // mails we processed already
      cntRefMail++;

      // forget the previous file name
      newMailFile[0] = '\0';

      D(DBF_MAIL, "draft mail %08lx '%s'", wmData->draftMail, wmData->draftMail != NULL ? wmData->draftMail->MailFile : "NULL");

      // now we check how the new mail file should be named
      // or created. As we iterate through all refMailList
      // nodes we only generate a new file for the mail redirecting
      // mode. All other modes can't have multiple output files
      if(cntRefMail == 0 || wmData->mode == NMM_REDIRECT)
      {
        enum NewMailMode newmode = wmData->mode;

        switch(wmData->mode)
        {
          case NMM_EDIT:
          {
            if(refMail != NULL && MailExists(refMail, outfolder) == TRUE)
            {
              GetMailFile(newMailFile, sizeof(newMailFile), outfolder, refMail);
              break;
            }
          }
          // continue

          case NMM_EDITASNEW:
            newmode = NMM_NEW;
          // continue

          default:
          {
            if(isDraftsFolder(outfolder) && wmData->draftMail != NULL && MailExists(wmData->draftMail, outfolder) == TRUE)
            {
              GetMailFile(newMailFile, sizeof(newMailFile), outfolder, wmData->draftMail);

              if(wmData->mode == NMM_EDIT)
                newmode = NMM_EDIT; // keep EDIT mode when we were editing a drafts mail.
            }
            else
              MA_NewMailFile(outfolder, newMailFile, sizeof(newMailFile));
          }
          break;
        }

        wmData->mode = newmode;
      }

      D(DBF_MAIL, "new mail file '%s' - %d", newMailFile, wmData->mode);
      if(IsStrEmpty(newMailFile) == FALSE)
      {
        // now open the new mail file for write operations
        if((comp.FH = fopen(newMailFile, "w")) != NULL)
        {
          struct ExtendedMail *email = NULL;

          // change file buffer settings
          setvbuf(comp.FH, NULL, _IOFBF, SIZE_FILEBUF);

          // Write out the message to our file and check that everything worked out fine.
          // Set a busy mouse pointer during this operation, since encoding large attachments
          // might take quite a long time.
          set(obj, MUIA_Window_Sleep, TRUE);
          comp.refMail = refMail;
          success = WriteOutMessage(&comp);
          set(obj, MUIA_Window_Sleep, FALSE);

          // close the file handle immediately
          fclose(comp.FH);
          comp.FH = NULL;

          // if the WriteOut operation didn't work
          // as expected stop here
          if(success == FALSE)
          {
            DeleteFile(newMailFile);
            goto out;
          }

          // stop any pending file notification.
          if(wmData->fileNotifyActive == TRUE)
          {
            EndNotify(wmData->notifyRequest);
            wmData->fileNotifyActive = FALSE;
          }

          if((email = MA_ExamineMail(outfolder, FilePart(newMailFile), C->EmailCache > 0 ? TRUE : FALSE)) != NULL)
          {
            if((newMail = CloneMail(&email->Mail)) != NULL)
            {
              struct Mail *replacedMail = NULL;

              if(mode == WRITE_DRAFT)
              {
                replacedMail = ReplaceMailInFolder(FilePart(newMailFile), newMail, outfolder);
                D(DBF_MAIL, "replaced mail file %08lx '%s' by %08lx '%s'", replacedMail, replacedMail != NULL ? replacedMail->MailFile : "NULL", newMail, newMail->MailFile);
              }
              else
              {
                AddMailToFolder(newMail, outfolder);
                D(DBF_MAIL, "added new mail file %08lx '%s'", newMail, newMail->MailFile);
                replacedMail = NULL;
              }

              // Now we have to check whether we have to add the To & CC addresses
              // to the emailCache
              if(C->EmailCache > 0)
              {
                DoMethod(_app(obj), MUIM_YAMApplication_AddToEmailCache, &newMail->To);

                // if this mail has more than one recipient we have to add the others too
                if(isMultiRCPTMail(newMail))
                {
                  int j;

                  for(j = 0; j < email->NumSTo; j++)
                    DoMethod(_app(obj), MUIM_YAMApplication_AddToEmailCache, &email->STo[j]);

                  for(j = 0; j < email->NumCC; j++)
                    DoMethod(_app(obj), MUIM_YAMApplication_AddToEmailCache, &email->CC[j]);

                  for(j = 0; j < email->NumBCC; j++)
                    DoMethod(_app(obj), MUIM_YAMApplication_AddToEmailCache, &email->BCC[j]);
                }
              }

              if(GetCurrentFolder() == outfolder)
              {
                if(replacedMail != NULL)
                {
                  // find the old mail, replace it and resort the list
                  LONG pos = MUIV_NList_GetPos_Start;

                  DoMethod(G->MA->GUI.PG_MAILLIST, MUIM_NList_GetPos, replacedMail, &pos);

                  set(G->MA->GUI.PG_MAILLIST, MUIA_NList_Quiet, TRUE);
                  DoMethod(G->MA->GUI.PG_MAILLIST, MUIM_NList_ReplaceSingle, newMail, pos, NOWRAP, ALIGN_LEFT);
                  DoMethod(G->MA->GUI.PG_MAILLIST, MUIM_NList_Sort);
                  set(G->MA->GUI.PG_MAILLIST, MUIA_NList_Quiet, FALSE);
                }
                else
                {
                  DoMethod(G->MA->GUI.PG_MAILLIST, MUIM_NList_InsertSingle, newMail, MUIV_NList_Insert_Sorted);
                }
              }

              // finally dereference and free the replaced mail
              if(replacedMail != NULL)
                DereferenceMail(replacedMail);

              MA_UpdateMailFile(newMail);

              // if this write operation was an edit mode
              // we have to check all existing readmail objects for
              // references and update them accordingly.
              if(wmData->mode == NMM_EDIT && refMail != NULL)
              {
                struct ReadMailData *rmData;

                // now we search through our existing readMailData
                // objects and see some of them are pointing to the old mail
                // and if so we signal them to display the new revised mail instead
                IterateList(&G->readMailDataList, struct ReadMailData *, rmData)
                {
                  if(rmData->mail == refMail)
                  {
                    if(rmData->readWindow != NULL)
                      DoMethod(rmData->readWindow, MUIM_ReadWindow_ReadMail, newMail);
                    else if(rmData->readMailGroup != NULL)
                      DoMethod(rmData->readMailGroup, MUIM_ReadMailGroup_ReadMail, newMail);
                  }
                }

                // remove and free the mail
                RemoveMailFromFolder(refMail, TRUE, TRUE);

                // update the references
                D(DBF_MAIL, "replace reference mail %08lx by %08lx", refMail, newMail);
                DereferenceMail(refMail);
                refMail = newMail;
                ReferenceMail(refMail);
              }
              else if(wmData->mode == NMM_NEW && refMail != NULL)
              {
                // update the references
                D(DBF_MAIL, "replace reference mail %08lx by %08lx", refMail, newMail);
                DereferenceMail(refMail);
                refMail = newMail;
                ReferenceMail(refMail);
              }

              // replace the mail pointer in the reference mail list
              mnode->mail = refMail;

              if(wmData->draftMail != NULL || mode == WRITE_DRAFT)
              {
                if(mode == WRITE_SEND || mode == WRITE_QUEUE)
                {
                  // delete the mail from the drafts folder
                  MA_DeleteSingle(wmData->draftMail, DELF_AT_ONCE);
                }

                // remember the new mail as draft mail
                wmData->draftMail = newMail;
                D(DBF_MAIL, "new draft mail %08lx '%s'", wmData->draftMail, wmData->draftMail->MailFile);
              }

              // add the new mail to our newMailList if
              // we have to sent it later
              AddNewMailNode(newMailList, newMail);
            }

            // cleanup the email structure
            MA_FreeEMailStruct(email);
          }
        }
        else
        {
          ER_NewError(tr(MSG_ER_CANNOT_CREATE_MAIL_FILE), newMailFile);
          goto out;
        }
      }

      // now we make sure the Disposition Notification is respected
      // and the status of the referenced mail updated accordingly.
      if(wmData->mode != NMM_NEW)
      {
        if(refMail != NULL && !isVirtualMail(refMail) && refMail->Folder != NULL &&
           !isOutgoingFolder(refMail->Folder) && !isDraftsFolder(refMail->Folder) && !isSentFolder(refMail->Folder))
        {
          // process MDN notifications
          if(hasStatusNew(refMail) || !hasStatusRead(refMail))
            RE_ProcessMDN(MDN_MODE_DISPLAY, refMail, FALSE, wmData->quietMode, obj);

          switch(wmData->mode)
          {
            case NMM_REPLY:
            {
              setStatusToReplied(refMail);
              DisplayStatistics(refMail->Folder, FALSE);
            }
            break;

            case NMM_FORWARD:
            case NMM_FORWARD_ATTACH:
            case NMM_FORWARD_INLINE:
            case NMM_REDIRECT:
            {
              // don't change the forwarded flag if we are just saving a draft mail
              if(mode != WRITE_DRAFT)
              {
                setStatusToForwarded(refMail);
                DisplayStatistics(refMail->Folder, FALSE);
              }
            }
            break;

            default:
              // nothing
            break;
          }
        }
      }

      // if requested we make sure we also
      // output a log entry about the write operation
      switch(wmData->mode)
      {
        case NMM_NEW:
        case NMM_EDITASNEW:
        {
          if(newMail != NULL)
            AppendToLogfile(LF_ALL, 10, tr(MSG_LOG_Creating), AddrName(newMail->To), newMail->Subject, numAttachments);
        }
        break;

        case NMM_REPLY:
        {
          if(refMail != NULL)
            AppendToLogfile(LF_ALL, 11, tr(MSG_LOG_Replying), AddrName(refMail->From), refMail->Subject);
          else
            AppendToLogfile(LF_ALL, 11, tr(MSG_LOG_Replying), "<unknown>", "<unknown>");
        }
        break;

        case NMM_FORWARD:
        case NMM_FORWARD_ATTACH:
        case NMM_FORWARD_INLINE:
        {
          if(newMail != NULL)
          {
            if(refMail != NULL)
              AppendToLogfile(LF_ALL, 12, tr(MSG_LOG_Forwarding), AddrName(refMail->From), refMail->Subject, AddrName(newMail->To));
            else
              AppendToLogfile(LF_ALL, 12, tr(MSG_LOG_Forwarding), "<unknown>", "<unknown>", AddrName(newMail->To));
          }
        }
        break;

        case NMM_REDIRECT:
        {
          if(newMail != NULL)
          {
            if(refMail != NULL)
              AppendToLogfile(LF_ALL, 13, tr(MSG_LOG_REDIRECT), AddrName(refMail->From), refMail->Subject, AddrName(newMail->To));
            else
              AppendToLogfile(LF_ALL, 13, tr(MSG_LOG_REDIRECT), "<unknown>", "<unknown>", AddrName(newMail->To));
          }
        }
        break;

        case NMM_EDIT:
        {
          if(newMail != NULL)
            AppendToLogfile(LF_ALL, 14, tr(MSG_LOG_Editing), AddrName(newMail->From), AddrName(newMail->To), newMail->Subject);
        }
        break;

        case NMM_SAVEDEC:
          // not used
        break;
      }
    }
  }

  if(mode == WRITE_DRAFT)
  {
    // the mail is no longer modified
    data->mailModified = FALSE;

    // we just saved the mail text, so it is no longer modified
    set(data->TE_EDIT, MUIA_TextEditor_HasChanged, FALSE);

    // forget any "auto save" action
    // if this method is called as an "auto save" action this flag will be restored right after the call
    data->autoSaved = FALSE;
  }

  if(msg->closeWindow == TRUE)
  {
    // cleanup certain references if the window is to be closed
    wmData->refMail = NULL;
    wmData->draftMail = NULL;

    if(wmData->refMailList != NULL)
    {
      DeleteMailList(wmData->refMailList);
      wmData->refMailList = NULL;
    }

    // cleanup the In-Reply-To / References stuff
    if(wmData->inReplyToMsgID != NULL)
    {
      dstrfree(wmData->inReplyToMsgID);
      wmData->inReplyToMsgID = NULL;
    }

    if(wmData->references != NULL)
    {
      dstrfree(wmData->references);
      wmData->references = NULL;
    }
  }

  // now we make sure we immediately send out the mail.
  if(mode == WRITE_SEND && newMailList != NULL)
  {
    struct UserIdentityNode *uin = wmData->identity;

    // close the write window now
    set(obj, MUIA_Window_Open, FALSE);

    if(uin->smtpServer != NULL)
    {
      struct MailNode *mnode;

      // mark the server as "in use"
      LockMailServer(uin->smtpServer);
      uin->smtpServer->useCount++;

      // add the mail to the global list of mails being sent currently
      LockMailList(G->mailsInTransfer);
      ForEachMailNode(newMailList, mnode)
      {
        AddNewMailNode(G->mailsInTransfer, mnode->mail);
      }
      UnlockMailList(G->mailsInTransfer);

      if(DoAction(NULL, TA_SendMails,
        TT_SendMails_UserIdentity, uin,
        TT_SendMails_Mails, newMailList,
        TT_SendMails_Mode, SENDMAIL_ACTIVE_USER,
        TAG_DONE) == NULL)
      {
        // starting the thread failed, the list of mails will be deleted below
        uin->smtpServer->useCount--;
        CleanMailsInTransfer(newMailList);
      }
      else
      {
        // starting the thread succeeded, the list of mails will be deleted within
        // the thread, thus we must erase the pointer here to avoid deleting it
        // below
        newMailList = NULL;
      }

      UnlockMailServer(uin->smtpServer);
    }
    else
      E(DBF_MAIL, "no SMTP server configured for user identity '%s'", uin->description);
  }

  // make sure to dispose the window data by calling
  // CleanupWriteMailData method as soon as this method is finished
  if(msg->closeWindow == TRUE)
    DoMethod(_app(obj), MUIM_Application_PushMethod, _app(obj), 2, MUIM_YAMApplication_CleanupWriteMailData, wmData);

  // update the statistics of the outgoing folder
  DisplayStatistics(outfolder, TRUE);

  // if we end up here then everything was successful
  success = TRUE;

out:
  if(newMailList != NULL)
    DeleteMailList(newMailList);

  if(refMailList != NULL)
    DeleteMailList(refMailList);

  // free the list of MIME parts but don't delete temporary
  // files when the window is left open
  FreePartsList(comp.FirstPart, msg->closeWindow);

  // free the compose structure
  FreeCompose(&comp);

  RETURN(success);
  return success;
}

///
/// DECLARE(DroppedFile)
// Handles drag&drop of a file which was dropped on the
// dockyicon or appicon
DECLARE(DroppedFile) // STRPTR fileName
{
  GETDATA;
  int mode;

  ENTER();

  mode = xget(data->RG_PAGE, MUIA_Group_ActivePage);
  if(mode == 0)
  {
    FILE *fh;

    if((fh = fopen(msg->fileName, "r")) != NULL)
    {
      char buffer[SIZE_LARGE];
      int j;
      int notascii = 0;
      int len = fread(buffer, 1, SIZE_LARGE-1, fh);

      buffer[len] = '\0';
      fclose(fh);

      for(j = 0; j < len; j++)
      {
        int c = (int)buffer[j];

        if((c < 32 || c > 127) &&
            c != '\t' && c != '\n')
        {
          notascii++;
        }
      }

      if(notascii && len/notascii <= 16)
        mode = 1;
    }
  }

  if(mode == 0)
  {
    char *text;

    if((text = TransformText(msg->fileName, ED_INSERT, "")) != NULL)
    {
      DoMethod(data->TE_EDIT, MUIM_TextEditor_InsertText, text, MUIV_TextEditor_InsertText_Cursor);
      free(text);
    }
    else
      DoMethod(obj, METHOD(AddAttachment), msg->fileName, NULL, FALSE);
  }
  else
    DoMethod(obj, METHOD(AddAttachment), msg->fileName, NULL, FALSE);

  RETURN(0);
  return 0;
}

///
/// DECLARE(DoAutoSave)
// check and perform an autosave of the mail text
DECLARE(DoAutoSave)
{
  GETDATA;

  ENTER();

  // do the autosave only if something was modified
  if(data->mailModified == TRUE || xget(data->TE_EDIT, MUIA_TextEditor_HasChanged) == TRUE)
  {
    // prevent the autosave from happening when one of the match windows of
    // the recipientstring objects are open or otherwise they are intermediately
    // closed and resolved as soon as the autosave happens.
    if(xget(data->ST_TO, MUIA_RecipientString_MatchwindowOpen) == FALSE &&
       xget(data->ST_CC, MUIA_RecipientString_MatchwindowOpen) == FALSE &&
       xget(data->ST_BCC, MUIA_RecipientString_MatchwindowOpen) == FALSE &&
       xget(data->ST_REPLYTO, MUIA_RecipientString_MatchwindowOpen) == FALSE)
    {
      if(DoMethod(obj, METHOD(ComposeMail), WRITE_DRAFT, FALSE) == TRUE)
      {
        // we must remember if the mail was automatically saved, since the editor object cannot
        // tell about changes anymore if they don't happen from now on.
        data->autoSaved = TRUE;

        D(DBF_MAIL, "saved mail text of write window #%d", data->windowNumber);
      }
    }
    else
      W(DBF_MAIL, "match window of recipientstring open, rejected autosave of write window #%d", data->windowNumber);
  }
  else
    D(DBF_MAIL, "no changes found in editor, no need to save an autosave file");

  RETURN(0);
  return 0;
}

///
/// DECLARE(CancelAction)
// CancelAction - User clicked the close button
DECLARE(CancelAction)
{
  GETDATA;
  BOOL closeWindow = TRUE;

  ENTER();

  if(data->wmData->quietMode == FALSE)
  {
    // ask the user what to do if the mail text was modified but not yet automatically saved
    if(data->mailModified == TRUE ||
       data->autoSaved == TRUE ||
       (data->wmData->mode != NMM_REDIRECT && xget(data->TE_EDIT, MUIA_TextEditor_HasChanged) == TRUE))
    {
      switch(MUI_Request(_app(obj), obj, MUIF_NONE, NULL, tr(MSG_WR_DISCARDCHANGES_GADGET), tr(MSG_WR_DiscardChanges)))
      {
        case 0:
        {
          // cancel and keep window open
          closeWindow = FALSE;
        }
        break;

        case 1:
        {
          // save as draft (ComposeMail method will close the window)
          DoMethod(obj, METHOD(ComposeMail), WRITE_DRAFT, TRUE);

          // set closeWindow to FALSE beause ComposeMail already
          // cleans up / closes the window
          closeWindow = FALSE;
        }
        break;

        case 2:
        {
          // discard the mail and
          // remove a previously saved draft mail from the drafts folder, but only if the
          // draft mail was not the one being edited a second time
          D(DBF_MAIL, "about to discard mail %08lx in mode %08lx", data->wmData->draftMail, data->wmData->mode);
          if(data->wmData->mode != NMM_EDIT && data->wmData->draftMail != NULL)
            MA_DeleteSingle(data->wmData->draftMail, DELF_AT_ONCE);
        }
        break;
      }
    }
  }

  // check if we have to close/discard the window
  // However, please note that because we do kill the window upon closing it
  // we have to use MUIM_Application_PushMethod instead of calling the
  // CleanupWriteMailData method directly
  if(closeWindow == TRUE)
    DoMethod(_app(obj), MUIM_Application_PushMethod, _app(obj), 2, MUIM_YAMApplication_CleanupWriteMailData, data->wmData);

  RETURN(0);
  return 0;
}

///
/// DECLARE(MailFileModified)
//
DECLARE(MailFileModified)
{
  GETDATA;
  struct DateStamp currentFileChangeTime;

  ENTER();

  // First we have to check if the date stamp of the file really did change, because notifications
  // are triggered by *any* change, i.e. setting a comment, changing the protection flags, changing
  // the contents. Since some editors (CubicIDE for example) do not only change the contents, but
  // also modify the protection bits and the file comment as well, we may receive more than just one
  // notification for the actual change of contents.
  if(ObtainFileInfo(data->wmData->filename, FI_DATE, &currentFileChangeTime) == TRUE)
  {
    if(CompareDates(&data->wmData->lastFileChangeTime, &currentFileChangeTime) > 0)
    {
      BOOL keep = FALSE;

      D(DBF_UTIL, "received notification that the tempfile of write window %ld have changed [%s]", data->windowNumber, data->wmData->filename);

      // check if the content of the TextEditor.mcc gadget changed
      // as well and if so warn the user
      if(xget(data->TE_EDIT, MUIA_TextEditor_HasChanged) == TRUE)
      {
        // warn the user that both the tempfile and the content of the TextEditor.mcc
        // changed.
        if(MUI_Request(_app(obj), obj, MUIF_NONE, tr(MSG_FCHANGE_WARN_TITLE),
                                                  tr(MSG_YesNoReq),
                                                  tr(MSG_FCHANGE_WARN), data->windowNumber+1) == 0)
        {
          // cancel / keep old text
          keep = TRUE;
        }
      }

      if(keep == FALSE)
      {
        // let the TextEditor.mcc load the modified file and mark it as changed again
        DoMethod(obj, METHOD(LoadText), NULL, TRUE);

        // remember this new date stamp
        memcpy(&data->wmData->lastFileChangeTime, &currentFileChangeTime, sizeof(data->wmData->lastFileChangeTime));
      }
    }
  }

  RETURN(0);
  return 0;
}

///
/// DECLARE(IdentityChanged)
//
DECLARE(IdentityChanged) // struct UserIdentityNode *uin;
{
  GETDATA;

  ENTER();

  // the user has changed the identity, thus we update certain things
  // in the write window according to the settings in the user identity.

  // first we update things in the write window GUI
  setstring(data->ST_FROM_OVERRIDE, msg->uin->address);
  set(data->ST_TO, MUIA_RecipientString_ActiveIdentity, msg->uin);
  xset(data->ST_CC, MUIA_String_Contents, msg->uin->mailCC,
                    MUIA_RecipientString_ActiveIdentity, msg->uin);
  xset(data->ST_BCC, MUIA_String_Contents, msg->uin->mailBCC,
                     MUIA_RecipientString_ActiveIdentity, msg->uin);

  // just go on in non redirection mode
  if(data->wmData->mode != NMM_REDIRECT)
  {
    set(data->CY_SIGNATURE, MUIA_SignatureChooser_Signature, msg->uin->signature);
    set(data->ST_REPLYTO, MUIA_String_Contents, msg->uin->mailReplyTo);
    set(data->ST_EXTHEADER, MUIA_String_Contents, msg->uin->extraHeaders);
    set(data->CH_DELSENT, MUIA_Selected, msg->uin->saveSentMail == FALSE);
    set(data->CH_ADDINFO, MUIA_Selected, msg->uin->addPersonalInfo);
    set(data->CH_MDN, MUIA_Selected, msg->uin->requestMDN);
    set(data->ST_REPLYTO, MUIA_RecipientString_ActiveIdentity, msg->uin);

    // check if we have to show the CC/BCC/ReplyTo string objects
    // in case we filled in sensible information
    if(msg->uin->mailCC[0] != '\0')
      DoMethod(obj, METHOD(ShowRecipientObject), MUIV_WriteWindow_RcptType_CC);

    if(msg->uin->mailBCC[0] != '\0')
      DoMethod(obj, METHOD(ShowRecipientObject), MUIV_WriteWindow_RcptType_BCC);

    if(msg->uin->mailReplyTo[0] != '\0')
      DoMethod(obj, METHOD(ShowRecipientObject), MUIV_WriteWindow_RcptType_ReplyTo);
  }

  // save a link to the userIdentity in the wmData
  data->wmData->identity = msg->uin;

  // the mail is modified
  data->mailModified = TRUE;

  RETURN(0);
  return 0;
}

///
/// DECLARE(HideRecipientObject)
//
DECLARE(HideRecipientObject) // enum RcptType rtype
{
  GETDATA;
  ENTER();

  if(DoMethod(data->GR_HEADER, MUIM_Group_InitChange))
  {
    switch(msg->rtype)
    {
      case MUIV_WriteWindow_RcptType_From:
      {
        // we only remove it if it is already shown
        if(data->fromRcptHidden == FALSE)
        {
          DoMethod(data->GR_HEADER, OM_REMMEMBER, data->LB_FROM);
          DoMethod(data->GR_HEADER, OM_REMMEMBER, data->CY_FROM);
          data->fromRcptHidden = TRUE;
        }
      }
      break;

      case MUIV_WriteWindow_RcptType_FromOverride:
      {
        // we only remove it if it is already shown
        if(data->fromOverrideRcptHidden == FALSE)
        {
          DoMethod(data->GR_HEADER, OM_REMMEMBER, data->LB_FROM_OVERRIDE);
          DoMethod(data->GR_HEADER, OM_REMMEMBER, data->GR_FROM_OVERRIDE);
          data->fromOverrideRcptHidden = TRUE;
        }
      }
      break;

      case MUIV_WriteWindow_RcptType_CC:
      {
        // we only remove it if it is already shown
        if(data->ccRcptHidden == FALSE)
        {
          DoMethod(data->GR_HEADER, OM_REMMEMBER, data->LB_CC);
          DoMethod(data->GR_HEADER, OM_REMMEMBER, data->GR_CC);
          data->ccRcptHidden = TRUE;

          // make sure to set the menuitem object state correctly
          nnset(data->MN_CC, MUIA_Menuitem_Checked, FALSE);
        }
      }
      break;

      case MUIV_WriteWindow_RcptType_BCC:
      {
        // we only remove it if it is already shown
        if(data->bccRcptHidden == FALSE)
        {
          DoMethod(data->GR_HEADER, OM_REMMEMBER, data->LB_BCC);
          DoMethod(data->GR_HEADER, OM_REMMEMBER, data->GR_BCC);
          data->bccRcptHidden = TRUE;

          // make sure to set the menuitem object state correctly
          nnset(data->MN_BCC, MUIA_Menuitem_Checked, FALSE);
        }
      }
      break;

      case MUIV_WriteWindow_RcptType_ReplyTo:
      {
        // we only remove it if it is already shown
        if(data->replyToRcptHidden == FALSE)
        {
          DoMethod(data->GR_HEADER, OM_REMMEMBER, data->LB_REPLYTO);
          DoMethod(data->GR_HEADER, OM_REMMEMBER, data->GR_REPLYTO);
          data->replyToRcptHidden = TRUE;

          // make sure to set the menuitem object state correctly
          nnset(data->MN_REPLYTO, MUIA_Menuitem_Checked, FALSE);
        }
      }
      break;

      case MUIV_WriteWindow_RcptType_To:
        // do nothing
      break;
    }

    DoMethod(data->GR_HEADER, MUIM_Group_ExitChange);
  }

  RETURN(0);
  return 0;
}

///
/// DECLARE(ShowRecipientObject)
//
DECLARE(ShowRecipientObject) // enum RcptType rtype
{
  GETDATA;

  ENTER();

  if(DoMethod(data->GR_HEADER, MUIM_Group_InitChange))
  {
    struct {
      ULONG MethodID;
      Object *objs[16];
    } sortMsg;
    int objcnt;

    switch(msg->rtype)
    {
      case MUIV_WriteWindow_RcptType_From:
      {
        // we only show it if it is already hidden
        if(data->fromRcptHidden == TRUE)
        {
          DoMethod(data->GR_HEADER, OM_ADDMEMBER, data->LB_FROM);
          DoMethod(data->GR_HEADER, OM_ADDMEMBER, data->CY_FROM);

          data->fromRcptHidden = FALSE;
        }
      }
      break;

      case MUIV_WriteWindow_RcptType_FromOverride:
      {
        // we only show it if it is already hidden
        if(data->fromOverrideRcptHidden == TRUE)
        {
          DoMethod(data->GR_HEADER, OM_ADDMEMBER, data->LB_FROM_OVERRIDE);
          DoMethod(data->GR_HEADER, OM_ADDMEMBER, data->GR_FROM_OVERRIDE);

          data->fromOverrideRcptHidden = FALSE;
        }
      }
      break;

      case MUIV_WriteWindow_RcptType_CC:
      {
        // we only show it if it is already hidden
        if(data->ccRcptHidden == TRUE)
        {
          DoMethod(data->GR_HEADER, OM_ADDMEMBER, data->LB_CC);
          DoMethod(data->GR_HEADER, OM_ADDMEMBER, data->GR_CC);

          data->ccRcptHidden = FALSE;

          // make sure to set the menuitem object state correctly
          nnset(data->MN_CC, MUIA_Menuitem_Checked, TRUE);
        }
      }
      break;

      case MUIV_WriteWindow_RcptType_BCC:
      {
        // we only show it if it is already hidden
        if(data->bccRcptHidden == TRUE)
        {
          DoMethod(data->GR_HEADER, OM_ADDMEMBER, data->LB_BCC);
          DoMethod(data->GR_HEADER, OM_ADDMEMBER, data->GR_BCC);

          data->bccRcptHidden = FALSE;

          // make sure to set the menuitem object state correctly
          nnset(data->MN_BCC, MUIA_Menuitem_Checked, TRUE);
        }
      }
      break;

      case MUIV_WriteWindow_RcptType_ReplyTo:
      {
        // we only show it if it is already hidden
        if(data->replyToRcptHidden == TRUE)
        {
          DoMethod(data->GR_HEADER, OM_ADDMEMBER, data->LB_REPLYTO);
          DoMethod(data->GR_HEADER, OM_ADDMEMBER, data->GR_REPLYTO);

          data->replyToRcptHidden = FALSE;

          // make sure to set the menuitem object state correctly
          nnset(data->MN_REPLYTO, MUIA_Menuitem_Checked, TRUE);
        }
      }
      break;

      case MUIV_WriteWindow_RcptType_To:
        // do nothing
      break;
    }

    // set up the parameters for MUIM_Group_Sort as MUIM_Group_MoveMember
    // seems to be broken in MUI 3.8
    sortMsg.MethodID = MUIM_Group_Sort;

    objcnt = 0;
    if(data->fromRcptHidden == FALSE)
    {
      sortMsg.objs[objcnt] = data->LB_FROM; objcnt++;
      sortMsg.objs[objcnt] = data->CY_FROM; objcnt++;
    }

    if(data->fromOverrideRcptHidden == FALSE)
    {
      sortMsg.objs[objcnt] = data->LB_FROM_OVERRIDE; objcnt++;
      sortMsg.objs[objcnt] = data->GR_FROM_OVERRIDE; objcnt++;
    }

    sortMsg.objs[objcnt] = data->LB_TO; objcnt++;
    sortMsg.objs[objcnt] = data->GR_TO; objcnt++;

    if(data->ccRcptHidden == FALSE)
    {
      sortMsg.objs[objcnt] = data->LB_CC; objcnt++;
      sortMsg.objs[objcnt] = data->GR_CC; objcnt++;
    }

    if(data->bccRcptHidden == FALSE)
    {
      sortMsg.objs[objcnt] = data->LB_BCC; objcnt++;
      sortMsg.objs[objcnt] = data->GR_BCC; objcnt++;
    }

    // all the other objects are just part of a normal write
    // window and not a redirecting window
    if(data->wmData->mode != NMM_REDIRECT)
    {
      if(data->replyToRcptHidden == FALSE)
      {
        sortMsg.objs[objcnt] = data->LB_REPLYTO; objcnt++;
        sortMsg.objs[objcnt] = data->GR_REPLYTO; objcnt++;
      }
      sortMsg.objs[objcnt] = data->LB_SUBJECT; objcnt++;
      sortMsg.objs[objcnt] = data->ST_SUBJECT; objcnt++;
    }

    // terminate the array
    sortMsg.objs[objcnt] = NULL;

    // sort the objects
    DoMethodA(data->GR_HEADER, (Msg)&sortMsg);

    // make sure to show only one "From:" label
    if(data->fromOverrideRcptHidden == FALSE)
      set(data->LB_FROM_OVERRIDE, MUIA_Group_ActivePage, (NumberOfUserIdentities(&C->userIdentityList) == 1) ? 1 : 0);

    DoMethod(data->GR_HEADER, MUIM_Group_ExitChange);
  }

  RETURN(0);
  return 0;
}

///
/// DECLARE(MenuToggleRecipientObject)
//
DECLARE(MenuToggleRecipientObject) // enum RcptType rtype
{
  GETDATA;
  BOOL show = TRUE;
  BOOL saveConfig = FALSE;
  ENTER();

  switch(msg->rtype)
  {
    case MUIV_WriteWindow_RcptType_From:
      show = (data->fromRcptHidden == TRUE);
    break;

    case MUIV_WriteWindow_RcptType_FromOverride:
      show = (data->fromOverrideRcptHidden == TRUE);
    break;

    case MUIV_WriteWindow_RcptType_CC:
    {
      show = (data->ccRcptHidden == TRUE);

      // if the user used the menuitem we set a new default
      // in the configuration
      if(C->ShowRcptFieldCC != show)
      {
        // save new value to global config
        C->ShowRcptFieldCC = show;

        // also save it to the temp CE struct in
        // case the config window exists (is open)
        if(G->ConfigWinObject != NULL && CE != NULL)
          CE->ShowRcptFieldCC = show;

        saveConfig = TRUE;
      }
    }
    break;

    case MUIV_WriteWindow_RcptType_BCC:
    {
      show = (data->bccRcptHidden == TRUE);

      // if the user used the menuitem we set a new default
      // in the configuration
      if(C->ShowRcptFieldBCC != show)
      {
        // save new value to global config
        C->ShowRcptFieldBCC = show;

        // also save it to the temp CE struct in
        // case the config window exists (is open)
        if(G->ConfigWinObject != NULL && CE != NULL)
          CE->ShowRcptFieldBCC = show;

        saveConfig = TRUE;
      }
    }
    break;

    case MUIV_WriteWindow_RcptType_ReplyTo:
    {
      show = (data->replyToRcptHidden == TRUE);

      // if the user used the menuitem we set a new default
      // in the configuration
      if(C->ShowRcptFieldReplyTo != show)
      {
        // save new value to global config
        C->ShowRcptFieldReplyTo = show;

        // also save it to the temp CE struct in
        // case the config window exists (is open)
        if(G->ConfigWinObject != NULL && CE != NULL)
          CE->ShowRcptFieldReplyTo = show;

        saveConfig = TRUE;
      }
    }
    break;

    case MUIV_WriteWindow_RcptType_To:
      // do nothing
    break;
  }

  // make sure to show/hide the recipient object
  if(show == TRUE)
    DoMethod(obj, METHOD(ShowRecipientObject), msg->rtype);
  else
    DoMethod(obj, METHOD(HideRecipientObject), msg->rtype);

  // then we save the new default state for the
  // manually hidden/shown recipient object
  if(saveConfig == TRUE)
    SaveConfig(C, G->CO_PrefsFile, TRUE);

  RETURN(0);
  return 0;
}

///
/// DECLARE(UpdateSignatures)
// update the signature menu
DECLARE(UpdateSignatures)
{
  GETDATA;
  int i;
  struct SignatureNode *activeSig = data->wmData->identity->signature;
  struct SignatureNode *sn;
  int activeSigIndex = 0;
  Object *item;

  ENTER();

  // first remove all previous items and notifications
  DoMethod(data->CY_SIGNATURE,  MUIM_KillNotifyObj, MUIA_Cycle_Active, data->MI_SIGNATURE);
  for(i = 0; i < MAXSIG_MENU; i++)
  {
    if(data->MI_SIGNATURES[i] != NULL)
    {
      DoMethod(obj, MUIM_KillNotify, WMEN_SIGN0+i);
      DoMethod(data->MI_SIGNATURE, MUIM_Family_Remove, data->MI_SIGNATURES[i]);
      MUI_DisposeObject(data->MI_SIGNATURES[i]);
      data->MI_SIGNATURES[i] = NULL;
    }
  }

  // update the signature chooser
  DoMethod(data->CY_SIGNATURE, MUIM_SignatureChooser_UpdateSignatures);

  // add the "no signature" menu item
  item = MenuitemCheck(tr(MSG_CO_IDENTITY_NOSIGNATURE), "0", TRUE, FALSE, 0, 0xff & ~(1<<0), WMEN_SIGN0);
  if(item != NULL)
  {
    data->MI_SIGNATURES[0] = item;
    DoMethod(data->MI_SIGNATURE, MUIM_Family_AddTail, item);
    // set up the notifications
    DoMethod(item, MUIM_Notify, MUIA_Menuitem_Trigger, MUIV_EveryTime, data->CY_SIGNATURE, 3, MUIM_Set, MUIA_Cycle_Active, 0);
    DoMethod(data->CY_SIGNATURE, MUIM_Notify, MUIA_Cycle_Active, 0, data->MI_SIGNATURE, 4, MUIM_SetUData, WMEN_SIGN0, MUIA_Menuitem_Checked, TRUE);
  }

  // add the first 7 signatures from the config
  i = 1;
  IterateList(&C->signatureList, struct SignatureNode *, sn)
  {
    if(sn->active == TRUE)
    {
      if(i < MAXSIG_MENU)
      {
        // the first 3 signatures get a shortcut
        switch(i)
        {
          case 1:
            item = MenuitemCheck(sn->description, "7", TRUE, FALSE, 0, 0xff & ~(1<<1), WMEN_SIGN1);
          break;

          case 2:
            item = MenuitemCheck(sn->description, "8", TRUE, FALSE, 0, 0xff & ~(1<<2), WMEN_SIGN2);
          break;

          case 3:
            item = MenuitemCheck(sn->description, "9", TRUE, FALSE, 0, 0xff & ~(1<<3), WMEN_SIGN3);
          break;

          default:
            item = MenuitemCheck(sn->description, NULL, TRUE, FALSE, 0, 0xff & ~(1<<i), WMEN_SIGN0+i);
          break;
        }

        if(item != NULL)
        {
          data->MI_SIGNATURES[i] = item;
          DoMethod(data->MI_SIGNATURE, MUIM_Family_AddTail, item);
          // set up the notifications
          DoMethod(item, MUIM_Notify, MUIA_Menuitem_Trigger, MUIV_EveryTime, data->CY_SIGNATURE, 3, MUIM_Set, MUIA_Cycle_Active, i);
          DoMethod(data->CY_SIGNATURE, MUIM_Notify, MUIA_Cycle_Active, i, data->MI_SIGNATURE, 4, MUIM_SetUData, WMEN_SIGN0+i, MUIA_Menuitem_Checked, TRUE);
        }

        // if the current identity uses a signature then
        // rembember the current one if it matches
        if(activeSig != NULL && sn->id == activeSig->id)
        {
          activeSigIndex = i;
        }
      }
      else
      {
        // maximum reached, bail out
        break;
      }

      i++;
    }
  }

  // check the item which belongs to the identity's signature
  set(data->MI_SIGNATURES[activeSigIndex], MUIA_Menuitem_Checked, TRUE);

  RETURN(0);
  return 0;
}

///
/// DECLARE(UpdateIdentities)
// update the identity chooser
DECLARE(UpdateIdentities)
{
  GETDATA;

  ENTER();

  DoMethod(data->CY_FROM, MUIM_IdentityChooser_UpdateIdentities);

  RETURN(0);
  return 0;
}

///
/// DECLARE(HandleAppMessage)
// Hook to catch the MUIA_AppMessage from a workbench icon drop operation
// Note, that this must be a hook or otherwise the "struct AppMessage"
// is not valid anymore at the time this function is executed.
DECLARE(HandleAppMessage) // struct AppMessage *appmsg
{
  ENTER();

  if(msg->appmsg != NULL)
  {
    int i;

    // let's walk through all arguments in the appMessage
    for(i=0; i < msg->appmsg->am_NumArgs; i++)
    {
      struct WBArg *wa = &msg->appmsg->am_ArgList[i];
      char buf[SIZE_PATHFILE];

      NameFromLock(wa->wa_Lock, buf, sizeof(buf));

      if(wa->wa_Name != NULL && strlen(wa->wa_Name) > 0)
      {
        AddPart(buf, (char *)wa->wa_Name, sizeof(buf));

        // call WR_App to let it put in the text of the file
        // to the write window
        DoMethod(obj, METHOD(DroppedFile), buf);
      }
      else
      {
        // open the usual requester, but let it point to the dropped drawer
        DoMethod(obj, METHOD(RequestAttachment), buf);
      }
    }
  }

  RETURN(0);
  return 0;
}

///
/// DECLARE(InsertABookTreenode)
DECLARE(InsertABookTreenode) // enum RcptType type, Object *tree, struct MUI_NListtree_TreeNode *tn
{
  struct ABookNode *abn = (struct ABookNode *)msg->tn->tn_User;

  ENTER();

  switch(abn->type)
  {
    case ABNT_USER:
    {
      // insert the address
      DoMethod(obj, METHOD(AddRecipient), msg->type, abn->Alias);
    }
    break;

    case ABNT_LIST:
    {
      char *ptr;

      if((ptr = abn->ListMembers) != NULL)
      {
        while(*ptr != '\0')
        {
          char *nptr;

          if((nptr = strchr(ptr, '\n')) != NULL)
            *nptr = '\0';
          else
            break;

          // insert the address
          DoMethod(obj, METHOD(AddRecipient), msg->type, ptr);

          *nptr = '\n';
          ptr = nptr;

          ptr++;
        }
      }
    }
    break;

    case ABNT_GROUP:
    {
      ULONG pos = MUIV_NListtree_GetEntry_Position_Head;
      struct MUI_NListtree_TreeNode *tn = msg->tn;

      do
      {
        tn = (struct MUI_NListtree_TreeNode *)DoMethod(msg->tree, MUIM_NListtree_GetEntry, tn, pos, MUIV_NListtree_GetEntry_Flag_SameLevel);
        if(tn == NULL)
          break;

        DoMethod(obj, METHOD(InsertABookTreenode), msg->type, msg->tree, tn);

        pos = MUIV_NListtree_GetEntry_Position_Next;
      }
      while(TRUE);
    }
    break;
  }

  RETURN(0);
  return 0;
}

///
/// DECLARE(CodesetChanged)
// method that is called when the codeset in the write window is manually
// changed by the user.
DECLARE(CodesetChanged) // char *codesetName
{
  GETDATA;
  ENTER();

  // find the selected codeset and default to the global one if it
  // could not be found
  if((data->wmData->codeset = CodesetsFind(msg->codesetName,
                                           CSA_CodesetList,       G->codesetsList,
                                           CSA_FallbackToDefault, FALSE,
                                           TAG_DONE)) == NULL)
  {
    data->wmData->codeset = G->writeCodeset;
  }

  nnset(data->PO_CODESET, MUIA_CodesetPopup_Codeset, strippedCharsetName(data->wmData->codeset));

  RETURN(0);
  return 0;
}

///
/// DECLARE(MatchedKeyword)
// a keyword from the attachment keyword list has been typed
DECLARE(MatchedKeyword) // const char *keyword
{
  GETDATA;

  ENTER();

  D(DBF_GUI, "matched attachment keyword '%s'", SafeStr(msg->keyword));
  // pop up the attachment reminder if
  // - it is not disabled yet
  // - there are no attachments yet
  // - it is yet invisible
  if(data->attachmentRemind == MUIV_WriteWindow_AttachmentRemind_None &&
     xget(data->LV_ATTACH, MUIA_NList_Entries) == 0 &&
     xget(data->GR_ATTACH_REMIND, MUIA_ShowMe) == FALSE)
  {
    // the method is better call asynchronously instead of synchronously to avoid possible
    // graphical glitches, because the MUIA_TextEditor_MatchedKeyword attribute is triggered
    // from within the input handling
    DoMethod(_app(obj), MUIM_Application_PushMethod, data->GR_ATTACH_REMIND, 3, MUIM_Set, MUIA_ShowMe, TRUE);
  }

  RETURN(0);
  return 0;
}

///