jens-maus/yam

View on GitHub
src/mui/ConfigWindow.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: Config window of the application

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

#include "ConfigWindow_cl.h"

#include <proto/muimaster.h>
#include <libraries/iffparse.h>
#include <mui/NListview_mcc.h>

#include "YAM.h"
#include "YAM_error.h"

#include "mui/AddressBookConfigPage.h"
#include "mui/ConfigPage.h"
#include "mui/ConfigPageList.h"
#include "mui/FiltersConfigPage.h"
#include "mui/FirstStepsConfigPage.h"
#include "mui/IdentitiesConfigPage.h"
#include "mui/LookFeelConfigPage.h"
#include "mui/MimeConfigPage.h"
#include "mui/MixedConfigPage.h"
#include "mui/ReadConfigPage.h"
#include "mui/ReplyForwardConfigPage.h"
#include "mui/ScriptsConfigPage.h"
#include "mui/SecurityConfigPage.h"
#include "mui/SignatureConfigPage.h"
#include "mui/SpamConfigPage.h"
#include "mui/StartupQuitConfigPage.h"
#include "mui/TCPIPConfigPage.h"
#include "mui/UpdateConfigPage.h"
#include "mui/WriteConfigPage.h"
#include "mui/YAMApplication.h"

#include "Config.h"
#include "FileInfo.h"
#include "Locale.h"
#include "Requesters.h"

#include "Debug.h"

/* CLASSDATA
struct Data
{
  Object *GR_PAGE;
  Object *PG_PAGES[cp_Max];

  enum ConfigPage visiblePage;

  char windowTitle[SIZE_DEFAULT];
  char screenTitle[SIZE_DEFAULT];

  BOOL visitedPages[cp_Max];
  BOOL updateAll;
};
*/

/* INCLUDE
#include "ConfigPageList.h"
*/

#define MUIV_ConfigWindow_Close_Cancel 0
#define MUIV_ConfigWindow_Close_Use    1
#define MUIV_ConfigWindow_Close_Save   2

/* Private functions */
/// NewPrefsFile
// sets the name of the configuration file
static void NewPrefsFile(struct IClass *cl, Object *obj, char *fname)
{
  GETDATA;

  strlcpy(G->CO_PrefsFile, fname, sizeof(G->CO_PrefsFile));
  snprintf(data->windowTitle, sizeof(data->windowTitle), "%s (%s)", tr(MSG_MA_MConfig), fname);

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

///

/* Overloaded Methods */
/// OVERLOAD(OM_NEW)
OVERLOAD(OM_NEW)
{
  static struct PageList page[cp_Max];
  static struct PageList *pages[cp_Max + 1];
  int i;
  Object *BT_SAVE;
  Object *BT_USE;
  Object *BT_CANCEL;
  Object *NLV_PAGE;
  Object *LV_PAGE;
  Object *GR_PAGE;
  Object *PG_PAGES[cp_Max];

  // menu item enums
  enum { CMEN_OPEN = 1201, CMEN_SAVEAS, CMEN_SAVEWOPRIV, CMEN_DEF, CMEN_DEFALL, CMEN_LAST, CMEN_REST };

  ENTER();

  for(i = cp_FirstSteps; i < cp_Max; i++)
  {
    page[i].Offset = i;
    pages[i] = &page[i];
  }
  pages[cp_Max] = NULL;

  // put some labels on our configpagelist objects
  page[cp_FirstSteps  ].PageLabel = MSG_CO_CrdFirstSteps;
  page[cp_TCPIP       ].PageLabel = MSG_CO_CrdTCPIP;
  page[cp_Identities  ].PageLabel = MSG_CO_CRDIDENTITIES;
  page[cp_Filters     ].PageLabel = MSG_CO_CrdFilters;
  page[cp_Spam        ].PageLabel = MSG_CO_CRDSPAMFILTER;
  page[cp_Read        ].PageLabel = MSG_CO_CrdRead;
  page[cp_Write       ].PageLabel = MSG_CO_CrdWrite;
  page[cp_ReplyForward].PageLabel = MSG_CO_GR_REPLYFORWARD;
  page[cp_Signature   ].PageLabel = MSG_CO_CrdSignature;
  page[cp_Security    ].PageLabel = MSG_CO_CrdSecurity;
  page[cp_StartupQuit ].PageLabel = MSG_CO_GR_STARTUPQUIT;
  page[cp_MIME        ].PageLabel = MSG_CO_CrdMIME;
  page[cp_AddressBook ].PageLabel = MSG_CO_CrdABook;
  page[cp_Scripts     ].PageLabel = MSG_CO_GR_SCRIPTS;
  page[cp_Mixed       ].PageLabel = MSG_CO_CrdMixed;
  page[cp_LookFeel    ].PageLabel = MSG_CO_CRDLOOKFEEL;
  page[cp_Update      ].PageLabel = MSG_CO_CrdUpdate;

  if((obj = DoSuperNew(cl, obj,

    MUIA_HelpNode, "Configuration",
    MUIA_Window_Menustrip, MenustripObject,
      MenuChild, MenuObject,
        MUIA_Menu_Title, tr(MSG_MA_Project),
        MUIA_Menu_CopyStrings, FALSE,
        MenuChild, Menuitem(tr(MSG_CO_Open), "O", TRUE, FALSE, CMEN_OPEN),
        MenuChild, Menuitem(tr(MSG_CO_SaveAs), "A", TRUE, FALSE, CMEN_SAVEAS),
        MenuChild, Menuitem(tr(MSG_CO_SAVE_WITHOUT_PRIVATE_DATA), NULL, TRUE, FALSE, CMEN_SAVEWOPRIV),
      End,
      MenuChild, MenuObject,
        MUIA_Menu_Title, tr(MSG_CO_Edit),
        MUIA_Menu_CopyStrings, FALSE,
        MenuChild, Menuitem(tr(MSG_CO_ResetDefaults), "D", TRUE, FALSE, CMEN_DEF),
        MenuChild, Menuitem(tr(MSG_CO_ResetAll), "E", TRUE, FALSE, CMEN_DEFALL),
        MenuChild, Menuitem(tr(MSG_CO_LastSaved), "L", TRUE, FALSE, CMEN_LAST),
        MenuChild, Menuitem(tr(MSG_CO_Restore), "R", TRUE, FALSE, CMEN_REST),
      End,
    End,
    MUIA_Window_ID, MAKE_ID('C','O','N','F'),
    WindowContents, VGroup,
      Child, HGroup,
        Child, NLV_PAGE = NListviewObject,
          MUIA_CycleChain,  TRUE,
          MUIA_NListview_NList, LV_PAGE = ConfigPageListObject,
            MUIA_NList_Format,        "",
            MUIA_NList_Title,         FALSE,
            MUIA_NList_AdjustWidth,   TRUE,
            MUIA_NList_AutoVisible,   TRUE,
            MUIA_NList_MinLineHeight, 16,
            MUIA_NList_SourceArray,   pages,
            MUIA_NList_Active,        MUIV_NList_Active_Top,
          End,
        End,
        Child, GR_PAGE = PageGroup,
          NoFrame,
          MUIA_Group_ActivePage, 0,
          Child, PG_PAGES[cp_FirstSteps]   = FirstStepsConfigPageObject, End,
          Child, PG_PAGES[cp_TCPIP]        = TCPIPConfigPageObject, End,
          Child, PG_PAGES[cp_Identities]   = IdentitiesConfigPageObject, End,
          Child, PG_PAGES[cp_Filters   ]   = FiltersConfigPageObject, End,
          Child, PG_PAGES[cp_Spam]         = SpamConfigPageObject, End,
          Child, PG_PAGES[cp_Read]         = ReadConfigPageObject, End,
          Child, PG_PAGES[cp_Write]        = WriteConfigPageObject, End,
          Child, PG_PAGES[cp_ReplyForward] = ReplyForwardConfigPageObject, End,
          Child, PG_PAGES[cp_Signature]    = SignatureConfigPageObject, End,
          Child, PG_PAGES[cp_Security]     = SecurityConfigPageObject, End,
          Child, PG_PAGES[cp_StartupQuit]  = StartupQuitConfigPageObject, End,
          Child, PG_PAGES[cp_MIME]         = MimeConfigPageObject, End,
          Child, PG_PAGES[cp_AddressBook]  = AddressBookConfigPageObject, End,
          Child, PG_PAGES[cp_Scripts]      = ScriptsConfigPageObject, End,
          Child, PG_PAGES[cp_Mixed]        = MixedConfigPageObject, End,
          Child, PG_PAGES[cp_LookFeel]     = LookFeelConfigPageObject, End,
          Child, PG_PAGES[cp_Update]       = UpdateConfigPageObject, End,
        End,
      End,

      Child, RectangleObject,
        MUIA_Rectangle_HBar, TRUE,
        MUIA_FixHeight,      4,
      End,

      Child, HGroup,
        MUIA_Group_SameWidth, TRUE,
        Child, BT_SAVE   = MakeButton(tr(MSG_CO_Save)),
        Child, BT_USE    = MakeButton(tr(MSG_CO_Use)),
        Child, BT_CANCEL = MakeButton(tr(MSG_CO_Cancel)),
      End,
    End,

    TAG_MORE, inittags(msg))) != NULL)
  {
    GETDATA;

    data->GR_PAGE = GR_PAGE;
    for(i = 0; i < cp_Max; i++)
      data->PG_PAGES[i] = PG_PAGES[i];

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

    xset(obj, MUIA_Window_DefaultObject, NLV_PAGE,
              MUIA_Window_Title, tr(MSG_MA_MConfig),
              MUIA_Window_ScreenTitle, CreateScreenTitle(data->screenTitle, sizeof(data->screenTitle), tr(MSG_MA_MConfig)));

    SetHelp(BT_SAVE,   MSG_HELP_CO_BT_SAVE);
    SetHelp(BT_USE,    MSG_HELP_CO_BT_USE);
    SetHelp(BT_CANCEL, MSG_HELP_CO_BT_CANCEL);

    DoMethod(obj,        MUIM_Notify, MUIA_Window_MenuAction,   CMEN_OPEN,       obj, 1, METHOD(OpenConfig));
    DoMethod(obj,        MUIM_Notify, MUIA_Window_MenuAction,   CMEN_SAVEAS,     obj, 2, METHOD(SaveConfigAs), TRUE);
    DoMethod(obj,        MUIM_Notify, MUIA_Window_MenuAction,   CMEN_SAVEWOPRIV, obj, 2, METHOD(SaveConfigAs), FALSE);
    DoMethod(obj,        MUIM_Notify, MUIA_Window_MenuAction,   CMEN_DEF,        obj, 2, METHOD(ResetToDefault), FALSE);
    DoMethod(obj,        MUIM_Notify, MUIA_Window_MenuAction,   CMEN_DEFALL,     obj, 2, METHOD(ResetToDefault), TRUE);
    DoMethod(obj,        MUIM_Notify, MUIA_Window_MenuAction,   CMEN_LAST,       obj, 1, METHOD(LastSaved));
    DoMethod(obj,        MUIM_Notify, MUIA_Window_MenuAction,   CMEN_REST,       obj, 1, METHOD(Restore));
    DoMethod(LV_PAGE,    MUIM_Notify, MUIA_NList_Active,        MUIV_EveryTime,  obj, 2, METHOD(ChangePage), MUIV_TriggerValue);
    DoMethod(BT_SAVE,    MUIM_Notify, MUIA_Pressed,             FALSE,           obj, 2, METHOD(Close), MUIV_ConfigWindow_Close_Save);
    DoMethod(BT_USE,     MUIM_Notify, MUIA_Pressed,             FALSE,           obj, 2, METHOD(Close), MUIV_ConfigWindow_Close_Use);
    DoMethod(BT_CANCEL,  MUIM_Notify, MUIA_Pressed,             FALSE,           obj, 2, METHOD(Close), MUIV_ConfigWindow_Close_Cancel);
    DoMethod(obj,        MUIM_Notify, MUIA_Window_CloseRequest, TRUE,            obj, 2, METHOD(Close), MUIV_ConfigWindow_Close_Cancel);

    // set up a cross-page notification to let all pages react on changes on all other pages
    // currently only the TCPIP and Signature pages cause an update on the Identities page
    for(i = 0; i < cp_Max; i++)
    {
      LONG j;

      for(j = 0; j < cp_Max; j++)
      {
        if(j != i)
          DoMethod(PG_PAGES[i], MUIM_Notify, MUIA_ConfigPage_ConfigUpdate, MUIV_EveryTime, PG_PAGES[j], 2, MUIM_ConfigPage_ConfigUpdate, MUIV_TriggerValue);
      }
    }

    // set up the "First steps" page
    DoMethod(PG_PAGES[cp_FirstSteps], MUIM_ConfigPage_ConfigToGUI, CE);
  }

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

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

  switch(((struct opGet *)msg)->opg_AttrID)
  {
    case ATTR(UpdateAll):    *store = data->updateAll; data->updateAll = FALSE; return TRUE;
    case ATTR(VisiblePage):  *store = data->visiblePage; return TRUE;
    case ATTR(VisitedPages): *store = (IPTR)data->visitedPages; return TRUE;
  }

  return DoSuperMethodA(cl, obj, msg);
}

///

/* Public Methods */
/// DECLARE(ConfigToGUI)
DECLARE(ConfigToGUI) // enum ConfigPage page
{
  GETDATA;

  ENTER();

  if(msg->page >= cp_FirstSteps && msg->page < cp_Max)
  {
    DoMethod(data->PG_PAGES[msg->page], MUIM_ConfigPage_ConfigToGUI, CE);
  }

  RETURN(0);
  return 0;
}

///
/// DECLARE(GUIToConfig)
DECLARE(GUIToConfig) // enum ConfigPage page
{
  GETDATA;

  ENTER();

  if(msg->page >= cp_FirstSteps && msg->page < cp_Max)
  {
    DoMethod(data->PG_PAGES[msg->page], MUIM_ConfigPage_GUIToConfig);
  }

  RETURN(0);
  return 0;
}

///
/// DECLARE(ChangePage)
// selects a different page of the configuration
DECLARE(ChangePage) // enum ConfigPage page
{
  GETDATA;

  ENTER();

  if(msg->page >= cp_FirstSteps && msg->page < cp_Max)
  {
    // save the settings of the formerly visible page first
    DoMethod(data->PG_PAGES[data->visiblePage], MUIM_ConfigPage_GUIToConfig);

    // remember the new page and mark it as visited
    data->visiblePage = msg->page;
    data->visitedPages[msg->page] = TRUE;

    #if defined(__amigaos3__)
    // The follow lines are a workaround for OS3.x only because MUI 3.8 is buggy.
    // It seems that changing the Poppen Object Color on MUI 3.8 is only visible if the
    // page is currently the ActivePage, So we make the page active before setting the
    // colors. We do that only on pages with Poppen Objects (currently only cp_Read).
    // Making the page active a second time after CO_SetConfig() should not have
    // negative effects.
    if(msg->page == cp_Read)
      set(data->GR_PAGE, MUIA_Group_ActivePage, msg->page);
    #endif

    // set the settings of the new page
    DoMethod(data->PG_PAGES[msg->page], MUIM_ConfigPage_ConfigToGUI);

    set(data->GR_PAGE, MUIA_Group_ActivePage, msg->page);
  }

  RETURN(0);
  return 0;
}

///
/// DECLARE(OpenConfig)
// opens a different configuration file
DECLARE(OpenConfig)
{
  GETDATA;
  struct FileReqCache *frc;

  ENTER();

  if((frc = ReqFile(ASL_CONFIG, obj, tr(MSG_CO_Open), REQF_NONE, G->MA_MailDir, "")) != NULL)
  {
    char cname[SIZE_PATHFILE];

    AddPath(cname, frc->drawer, frc->file, sizeof(cname));
    if(LoadConfig(CE, cname) == 1)
      NewPrefsFile(cl, obj, cname);

    // resolve any unset folder IDs
    ResolveConfigFolders(CE);
    DoMethod(data->PG_PAGES[data->visiblePage], MUIM_ConfigPage_ConfigToGUI, CE);

    // remember to update all config items in ValidateConfig()
    data->updateAll = TRUE;
  }

  RETURN(0);
  return 0;
}

///
/// DECLARE(SaveConfigAs)
// saves configuration to a file using an alternative name
DECLARE(SaveConfigAs) // ULONG savePrivateData
{
  GETDATA;
  struct FileReqCache *frc;

  ENTER();

  if((frc = ReqFile(ASL_CONFIG, obj, msg->savePrivateData ? tr(MSG_CO_SaveAs) : tr(MSG_CO_SAVE_WITHOUT_PRIVATE_DATA), REQF_SAVEMODE, G->MA_MailDir, "")) != NULL)
  {
    char cname[SIZE_PATHFILE];

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

    if(FileExists(cname) == FALSE ||
       MUI_Request(_app(obj), obj, MUIF_NONE, tr(MSG_MA_ConfirmReq), tr(MSG_YesNoReq2), tr(MSG_FILE_OVERWRITE), frc->file) != 0)
    {
      // first let the currently visible page flush any pending changes to the configuration
      DoMethod(data->PG_PAGES[data->visiblePage], MUIM_ConfigPage_GUIToConfig);

      // forbid automatically saving the config after validation,
      // it will be saved anyway later
      ValidateConfig(CE, TRUE, FALSE);
      // resolve any changed folder IDs
      ResolveConfigFolders(CE);
      NewPrefsFile(cl, obj, cname);
      SaveConfig(CE, cname, msg->savePrivateData);
    }
  }

  RETURN(0);
  return 0;
}

///
/// DECLARE(ResetToDefault)
// resets configuration (or a part of it)
DECLARE(ResetToDefault) // ULONG all
{
  GETDATA;

  ENTER();

  if(msg->all == TRUE)
  {
    SetDefaultConfig(CE, cp_AllPages);

    // remember to update all config items in ValidateConfig()
    data->updateAll = TRUE;
  }
  else
    SetDefaultConfig(CE, data->visiblePage);

  DoMethod(data->PG_PAGES[data->visiblePage], MUIM_ConfigPage_ConfigToGUI, CE);

  RETURN(0);
  return 0;
}

///
/// DECLARE(LastSaved)
// reloads configuration from file
DECLARE(LastSaved)
{
  GETDATA;

  ENTER();

  LoadConfig(CE, G->CO_PrefsFile);

  DoMethod(data->PG_PAGES[data->visiblePage], MUIM_ConfigPage_ConfigToGUI, CE);

  // remember to update all config items in ValidateConfig()
  data->updateAll = TRUE;

  RETURN(0);
  return 0;
}

///
/// DECLARE(Restore)
// makes all changes undone
DECLARE(Restore)
{
  GETDATA;

  ENTER();

  ClearConfig(CE);
  CopyConfig(CE, C);

  DoMethod(data->PG_PAGES[data->visiblePage], MUIM_ConfigPage_ConfigToGUI, CE);

  RETURN(0);
  return 0;
}

///
/// DECLARE(Close)
// closes configuration window
DECLARE(Close) // ULONG how
{
  GETDATA;

  ENTER();

  // there is no point to do anything here if YAM is terminating
  if(G->Terminating == FALSE)
  {
    BOOL gotSemaphore = FALSE;
    // If the configuration is to be used/save we must exclusively obtain the semaphore
    // to avoid destroying the mail server nodes which might be in use by active POP3 or
    // SMTP transfers. If the window is just to be closed we can go on without a lock.
    if(msg->how == MUIV_ConfigWindow_Close_Cancel || (gotSemaphore = AttemptSemaphore(G->configSemaphore)) != FALSE)
    {
      BOOL configsEqual;
      BOOL close = TRUE;

      // make sure we have the latest state of the config in CE
      DoMethod(data->PG_PAGES[data->visiblePage], MUIM_ConfigPage_GUIToConfig);

      // now we compare the current config against
      // the temporary config
      configsEqual = CompareConfigs(C, CE);

      // check if we should copy our edited configuration
      // to the real one or if we should just free/drop it
      if(msg->how != MUIV_ConfigWindow_Close_Cancel)
      {
        // before we copy over the configuration, we
        // check if it was changed at all
        if(configsEqual == FALSE)
        {
          D(DBF_CONFIG, "configuration found to be different");

          // check for certain important state changes and do another comparison
          if(CheckConfigDiffs(data->visitedPages) == TRUE)
            configsEqual = CompareConfigs(C, CE);

          if(configsEqual == FALSE)
          {
            struct Config *tmpC;

            // just swap the pointers instead of clearing and copying the whole stuff
            tmpC = C;
            C = CE;
            CE = tmpC;
            // the up to now "current" configuration will be freed below
          }
        }
        else
          D(DBF_CONFIG, "config wasn't altered, skipped copy operations.");

        // validate that C has valid values
        // forbid automatically saving the config after validation
        ValidateConfig(C, TRUE, FALSE);

        // resolve any changed folder IDs
        ResolveConfigFolders(C);

        // we save the configuration if the user
        // has pressed on 'Save' only.
        if(msg->how == MUIV_ConfigWindow_Close_Save)
          SaveConfig(C, G->CO_PrefsFile, TRUE);
      }
      else if(configsEqual == FALSE)
      {
        // check if configs are equal and if not ask the user how to proceed
        int res = MUI_Request(_app(obj), obj, MUIF_NONE,
                              tr(MSG_CO_CONFIGWARNING_TITLE),
                              tr(MSG_CO_CONFIGWARNING_BT),
                              tr(MSG_CO_CONFIGWARNING));

        // if user pressed Abort or ESC
        // then we keep the window open.
        if(res == 0)
          close = FALSE;
      }

      if(close == TRUE)
      {
        // then we free our temporary config structure
        FreeConfig(CE);
        CE = NULL;

        // Dipose&Close the config window stuff
        DoMethod(_app(obj), MUIM_Application_PushMethod, _app(obj), 1, MUIM_YAMApplication_CloseConfigWindow);
      }

      // release the config semaphore again if we obtained it before
      if(gotSemaphore == TRUE)
        ReleaseSemaphore(G->configSemaphore);
    }
    else
      ER_NewError(tr(MSG_CO_CONFIG_IS_LOCKED));
  }

  RETURN(0);
  return 0;
}

///