/************************************************************/
/* Windows Write, Copyright 1985-1992 Microsoft Corporation */
/************************************************************/

/* running.c -- code to handle editing of running header and footer */

#define NOGDICAPMASKS
#define NOVIRTUALKEYCODES
#define NOWINSTYLES
#define NOSYSMETRICS
#define NOICON
#define NOKEYSTATE
#define NOSYSCOMMANDS
#define NOSHOWWINDOW
//#define NOATOM
//#define NOGDI
#define NOFONT
#define NOBRUSH
#define NOCLIPBOARD
#define NOCOLOR
#define NOCREATESTRUCT
#define NODRAWTEXT
#define NOMB
#define NOMETAFILE
#define NOMINMAX
#define NOOPENFILE
#define NOPEN
#define NOREGION
#define NOSCROLL
#define NOSOUND
#define NOTEXTMETRIC
#define NOWH
#define NOWINOFFSETS
#define NOWNDCLASS
#define NOCOMM
#include <windows.h>

#include "mw.h"
#include "machdefs.h"
#define NOKCCODES
#include "ch.h"
#include "docdefs.h"
#include "cmddefs.h"
#include "editdefs.h"
#include "propdefs.h"
#include "prmdefs.h"
#include "wwdefs.h"
#include "dlgdefs.h"
#include "menudefs.h"
#include "str.h"
#if defined(OLE)
#include "obj.h"
#endif

#ifdef JAPAN //T-HIROYN Win3.1
#include "kanji.h"
#endif

int NEAR EditHeaderFooter();

    /* Current allowable cp range for display/edit/scroll */
extern typeCP cpMinCur;
extern typeCP cpMacCur;

extern struct DOD (**hpdocdod)[];
extern struct WWD rgwwd[];
extern int docCur;
extern int docScrap;
extern int vfSeeSel;
extern struct SEL selCur;
extern struct PAP vpapAbs;
extern struct SEP vsepNormal;
extern HANDLE       vhWnd;
extern HANDLE       hMmwModInstance;
extern HANDLE       hParentWw;
#ifdef INEFFLOCKDOWN
extern FARPROC      lpDialogRunningHead;
#else
BOOL far PASCAL DialogRunningHead(HWND, unsigned, WORD, LONG);
FARPROC lpDialogRunningHead = NULL;
#endif
extern HANDLE       vhDlgRunningHead;
extern CHAR     stBuf[255];
extern int      utCur;
extern int      ferror;
extern int      vccpFetch;
extern int      vcchFetch;
extern CHAR     *vpchFetch;
extern struct CHP   vchpFetch;
extern typeCP       vcpLimParaCache;
extern HWND     vhWndMsgBoxParent;

    /* Min, Max cp's for header, footer */
typeCP cpMinHeader=cp0;
typeCP cpMacHeader=cp0;
typeCP cpMinFooter=cp0;
typeCP cpMacFooter=cp0;

    /* Min cp for document less header, footer */
    /* Header & footer always appear at the beginning */
typeCP cpMinDocument=cp0;

    /* The following variables are used in this module only */

#define cchWinTextSave  80
static CHAR     (**hszWinTextSave)[]=NULL;
static struct PAP   *ppapDefault;

    /* cpFirst and selection are saved in these during header/footer edit */
typeCP       cpFirstDocSave;
struct SEL   selDocSave;


HWND vhDlgRunning;



fnEditRunning(imi)
{   /* Enter mode so that user is editing the current document's
       running header or footer in the same window as he was editing
       the document, with the header/footer info in the dialog box
       NOT currently in focus ..pault */

#ifndef INEFFLOCKDOWN
  if (!lpDialogRunningHead)
    if (!(lpDialogRunningHead = MakeProcInstance(DialogRunningHead, hMmwModInstance)))
      {
      WinFailure();
      return;
      }
#endif

  Assert(imi == imiHeader || imi == imiFooter);

  if (wwdCurrentDoc.fEditHeader || wwdCurrentDoc.fEditFooter)
  {
    SetFocus(vhDlgRunningHead);
    return;
  }

  if (imi == imiHeader)
    wwdCurrentDoc.fEditHeader = TRUE;
  else
    wwdCurrentDoc.fEditFooter = TRUE;

  EditHeaderFooter();
  if (ferror)
    {    /* Not enough memory to stabilize the running head environs */
    if (wwdCurrentDoc.fEditHeader)
      wwdCurrentDoc.fEditHeader = FALSE;
    else
      wwdCurrentDoc.fEditFooter = FALSE;
    return;
    }
  vhDlgRunningHead = CreateDialog(hMmwModInstance,
                                 MAKEINTRESOURCE(wwdCurrentDoc.fEditHeader ?
                                                dlgRunningHead : dlgFooter),
                                 hParentWw, lpDialogRunningHead);
  if (vhDlgRunningHead)
    {
    SetFocus(wwdCurrentDoc.wwptr);
    }
 else
    { /* recover and bail out */
    fnEditDocument();
#ifdef WIN30
    WinFailure();
#else
    Error(IDPMTNoMemory);
#endif
    }
}



int NEAR EditHeaderFooter()
{   /* Setup for edit of header or footer */
 extern HWND hParentWw;

 int fHeader=wwdCurrentDoc.fEditHeader;
 CHAR szWinTextSave[ cchWinTextSave ];
 typeCP cpFirst;
 typeCP cpLim;
#ifdef DEBUG
    /* TEST Assumption: No changes take place in running head/foot cp range
       during an interval in which no head/foot edits take place */
 typeCP cpMinDocT=cpMinDocument;

 ValidateHeaderFooter( docCur );
 Assert( cpMinDocT == cpMinDocument );
#endif

 if (fHeader)
    {
    cpFirst = cpMinHeader;
    cpLim = cpMacHeader;
    }
 else
    {
    cpFirst = cpMinFooter;
    cpLim = cpMacFooter;
    }

 Assert( wwdCurrentDoc.fEditHeader != wwdCurrentDoc.fEditFooter );

    /* Save the cpFirst of the document window so we get a clean
       transition back to where we were in the document*/
 cpFirstDocSave = wwdCurrentDoc.cpFirst;
 selDocSave = selCur;

 TrashCache();
 TrashWw( wwDocument );

 if (!FWriteOk( fwcNil ))
    goto DontEdit;

 if ( cpFirst == cpLim )
    {
    /* If we are editing the header/footer for the first time in this document,
       insert a para end mark to hold the running h/f properties */
    extern struct PAP *vppapNormal;
    struct PAP papT;

    blt( vppapNormal, &papT, cwPAP );
    papT.rhc = (wwdCurrentDoc.fEditHeader) ?
             rhcDefault : rhcDefault + RHC_fBottom;

    InsertEolPap( docCur, cpFirst, &papT );
    if (ferror)
    return;
    ValidateHeaderFooter( docCur );
    cpLim += ccpEol;
    }
 else
    {
    extern int vccpFetch;
    typeCP cp;

    /* Test for a special case: loading a WORD document which has been
       properly set up to have running head/foot under MEMO.  We must
       force the para end mark at the end of the header/footer to be
       a fresh run.  This is so we will see an end mark when editing one
       of these.  FormatLine only checks for cpMacCur at the start of a run. */

    Assert( cpLim - cpFirst >= ccpEol );

    if ( (cp = cpLim - ccpEol) > cpFirst )
    {
    FetchCp( docCur, cp - 1, 0, fcmBoth );

    if ( vccpFetch > 1)
        {   /* char run does not end with char before EOL */
        /* Insert a char, then delete it */
        extern struct CHP vchpNormal;
        CHAR ch='X';

        InsertRgch( docCur, cp, &ch, 1, &vchpNormal, NULL );
        if (ferror)
        return;
        Replace( docCur, cp, (typeCP) 1, fnNil, fc0, fc0 );
        if (ferror)
        return;
        }
    }
    }

DontEdit:

 /* Save current window text; set string */

 GetWindowText( hParentWw, (LPSTR)szWinTextSave, cchWinTextSave );
 if (FNoHeap(hszWinTextSave=HszCreate( (PCH)szWinTextSave )))
    {
    hszWinTextSave = NULL;
    }
  else
    {
    extern CHAR szHeader[];
    extern CHAR szFooter[];

    SetWindowText( hParentWw, fHeader ? (LPSTR)szHeader:(LPSTR)szFooter );
    }

   /* Set editing limits to just the cp range of the header/footer,
       minus the "invisible" terminating EOL */
 wwdCurrentDoc.cpFirst = wwdCurrentDoc.cpMin = cpMinCur = cpFirst;
 wwdCurrentDoc.cpMac = cpMacCur = CpMax( cpMinCur, cpLim - ccpEol );

    /* Leave the cursor at the beginning of the header/footer regardless */
 Select( cpMinCur, cpMinCur );
    /* Show the display here instead of waiting for Idle() because it looks
       better to have the head/foot text come up right away instead of waiting
       for the dialog box to come up */
 UpdateDisplay( FALSE );
 vfSeeSel = TRUE;   /* Tell Idle() to scroll the selection into view */
 NoUndo();
 ferror = FALSE;    /* If we got this far, we want to go into running
               head mode regardless of errors */
}




fnEditDocument()
{   /* Return to editing document after editing header/footer */
 extern HWND hParentWw;

 Assert( wwdCurrentDoc.fEditFooter != wwdCurrentDoc.fEditHeader );

    /* Restore original window name */
 if (hszWinTextSave != NULL)
    {
    SetWindowText( hParentWw, (LPSTR) (**hszWinTextSave) );
    FreeH( hszWinTextSave );
    hszWinTextSave = NULL;
    }

 TrashCache();

 ValidateHeaderFooter( docCur );    /* This will update from the results of
                       the header/footer edit */
 TrashCache();
 wwdCurrentDoc.cpMin = cpMinCur = cpMinDocument;
 wwdCurrentDoc.cpMac = cpMacCur = CpMacText( docCur );

 TrashWw( wwDocument );
 wwdCurrentDoc.fEditHeader = FALSE;
 wwdCurrentDoc.fEditFooter = FALSE;

    /* Restore saved selection, cpFirst for document */
 wwdCurrentDoc.cpFirst = cpFirstDocSave;
 Select( selDocSave.cpFirst, selDocSave.cpLim );

 Assert( wwdCurrentDoc.cpFirst >= cpMinCur &&
     wwdCurrentDoc.cpFirst <= cpMacCur );

 NoUndo();
 vhDlgRunningHead = (HANDLE)NULL;
}




BOOL far PASCAL DialogRunningHead( hDlg, message, wParam, lParam )
HWND    hDlg;           /* Handle to the dialog box */
unsigned message;
WORD wParam;
LONG lParam;
{
    /* This routine handles input to the Header/Footer dialog box. */

    extern BOOL vfPrinterValid;

    RECT rc;
    CHAR *pch = &stBuf[0];
    struct SEP **hsep = (**hpdocdod)[docCur].hsep;
    struct SEP *psep;
    static int fChecked;
    typeCP dcp;

    switch (message)
    {
    case WM_INITDIALOG:
        vhDlgRunning = hDlg;    /* Put dialog handle in a global for
                       ESC key in document functionality */
        CachePara(docCur, selCur.cpFirst);
        ppapDefault = &vpapAbs;

        FreezeHp();
        /* Get a pointer to the section properties. */
        psep = (hsep == NULL) ? &vsepNormal : *hsep;

        CheckDlgButton(hDlg, idiRHFirst, (ppapDefault->rhc & RHC_fFirst));
        if (wwdCurrentDoc.fEditHeader)
        {
        CchExpZa(&pch, psep->yaRH1, utCur, cchMaxNum);
        }
        else /* footer dialog box */
        {
#ifdef  KOREA    /* 91.3.17 want to guarantee Default >= MIN, Sangl */
              if (vfPrinterValid)
                {   extern int dyaPrOffset;
                    extern int dyaPrPage;
                CchExpZa(&pch, imax(psep->yaMac - psep->yaRH2,
            vsepNormal.yaMac - dyaPrOffset -  dyaPrPage),utCur, cchMaxNum);
                 }
              else
                CchExpZa(&pch, psep->yaMac - psep->yaRH2, utCur, cchMaxNum);
#else
        CchExpZa( &pch, psep->yaMac - psep->yaRH2, utCur, cchMaxNum);
#endif
        }
        SetDlgItemText(hDlg, idiRHDx, (LPSTR)stBuf);
        MeltHp();
        break;

    case WM_ACTIVATE:
        if (wParam)
        {
        vhWndMsgBoxParent = hDlg;
        }
    return(FALSE); /* so that we leave the activate message to
    the dialog manager to take care of setting the focus correctly */

    case WM_COMMAND:
            switch (wParam)
        {
        int dya;

        case idiRHFirst:
        CheckDlgButton( hDlg, idiRHFirst, !IsDlgButtonChecked(hDlg, idiRHFirst));
        (**hpdocdod) [docCur].fDirty = TRUE;
        break;
        case idiRHInsertPage:
        if (FWriteOk( fwcInsert ))
            {   /* Insert page # at insertion pt */
            extern struct CHP vchpFetch, vchpSel;
            extern int vfSeeSel;
            CHAR ch=schPage;
            struct CHP chp;

            if (selCur.cpFirst == selCur.cpLim)
            {   /* Sel is insertion point -- get props from
                               the vchpSel kludge */
            blt( &vchpSel, &chp, cwCHP );
            }
            else
            {
            FetchCp( docCur, selCur.cpFirst, 0, fcmProps );
            blt( &vchpFetch, &chp, cwCHP );
            }

            chp.fSpecial = TRUE;

#ifdef JAPAN //T-HIROYN Win3.1
            if(NATIVE_CHARSET != GetCharSetFromChp(&chp)) {
                SetFtcToPchp(&chp, GetKanjiFtc(&chp));
            }
#endif

            SetUndo( uacInsert, docCur, selCur.cpFirst, (typeCP) 1,
             docNil, cpNil, cp0, 0 );
            InsertRgch( docCur, selCur.cpFirst, &ch, 1, &chp, NULL );

            vfSeeSel = TRUE;
            }
        break;

        case idiRHClear:
        /* Clear running head/foot */
        dcp = cpMacCur-cpMinCur;

#if defined(OLE)
        {
            BOOL bIsOK;

            ObjPushParms(docCur);
            Select(cpMinCur,cpMacCur);
            bIsOK = ObjDeletionOK(OBJ_DELETING);
            ObjPopParms(TRUE);

            if (!bIsOK)
                break;
        }
#endif

        if (dcp > 0 && FWriteOk( fwcDelete ))
            {
            NoUndo();
            SetUndo( uacDelNS, docCur, cpMinCur, dcp,
             docNil, cpNil, cp0, 0 );
            Replace( docCur, cpMinCur, dcp, fnNil, fc0, fc0 );
            }
        break;

        case idiOk: /* return to document */
BackToDoc:
        if (!FPdxaPosIt(&dya, hDlg, idiRHDx))
            {
            break;
            }
        else if (vfPrinterValid)
            {
            extern struct SEP vsepNormal;
            extern int dxaPrOffset;
            extern int dyaPrOffset;
            extern int dxaPrPage;
            extern int dyaPrPage;
            extern struct WWD rgwwd[];

            int dyaPrBottom = imax(0, vsepNormal.yaMac - dyaPrOffset -
              dyaPrPage);


            if (FUserZaLessThanZa(dya, (wwdCurrentDoc.fEditHeader ?
              dyaPrOffset : dyaPrBottom)))
            {
            int dxaPrRight = imax(0, vsepNormal.xaMac - dxaPrOffset
              - dxaPrPage);

            EnableExcept(vhDlgRunningHead, FALSE);
            ErrorBadMargins(hDlg, dxaPrOffset, dxaPrRight,
              dyaPrOffset, dyaPrBottom);
            EnableExcept(vhDlgRunningHead, TRUE);
            SelectIdiText(hDlg, idiRHDx);
            SetFocus(GetDlgItem(hDlg, idiRHDx));
            break;
            }
            }


        DoFormatRHText( dya, IsDlgButtonChecked( hDlg, idiRHFirst ) );
        fnEditDocument();
        /* force repaint to the whole client area */
        GetClientRect(vhWnd, (LPRECT)&rc);
        InvalidateRect(vhWnd, (LPRECT)&rc, FALSE);
        vhWndMsgBoxParent = (HWND)NULL;
        DestroyWindow(hDlg);
        break;

        case idiCancel:
        goto BackToDoc;
        default:
        return(FALSE);
        }
        break;

#if WINVER < 0x300
    /* Don't really need to process this */
    case WM_CLOSE:
        goto BackToDoc;
#endif

#ifndef INEFFLOCKDOWN
    case WM_NCDESTROY:
        FreeProcInstance(lpDialogRunningHead);
        lpDialogRunningHead = NULL;
        /* fall through to return false */
#endif

    default:
        return(FALSE);
    }
    return(TRUE);
}
/* end of DialogRunningHead */




DoFormatRHText( dya, fFirstPage)
int dya;
int fFirstPage;
{   /* Format cp range for running head/foot currently being edited
       to have the passed running head properties */
extern typeCP vcpLimParaCache;

CHAR rgb[4];
int fHeader=wwdCurrentDoc.fEditHeader;

    /* Note that the Min value for the part we were editing has not changed
       as a result of the edit, so no ValidateHeaderFooter is required */
typeCP cpMin=fHeader ? cpMinHeader : cpMinFooter;
int rhc;
struct SEP **hsep = (**hpdocdod)[docCur].hsep;
struct SEP *psep;

 if (!FWriteOk( fwcNil ))
    return;

/* Ensure that this document has a valid section property
descriptor. */
if (hsep == NULL)
    {
    if (FNoHeap(hsep = (struct SEP **)HAllocate(cwSEP)))
    {
    return;
    }
    blt(&vsepNormal, *hsep, cwSEP);
    (**hpdocdod)[docCur].hsep = hsep;
    }
psep = *hsep;

/* Set running head distance from top/bottom; this is a Section
   property.  This assumes the MEMO model: one section */
if (fHeader)
    psep->yaRH1 = dya;
else
    psep->yaRH2 = psep->yaMac - dya;

/* For MEMO, running heads appear on both odd and even pages;
   appearance on first page is optional */
rhc = RHC_fOdd + RHC_fEven;
if (fFirstPage)
    rhc += RHC_fFirst;
if (!fHeader)
    rhc += RHC_fBottom;

/* Set running head PARA properties by adding an appropriate sprm */

    /* Set CpMacCur to include the "hidden" Eol; this will prevent
       AddOneSprm from adding an extraneous EOL */
CachePara( docCur, CpMax( cpMinCur, cpMacCur-1 ) );
Assert( vpapAbs.rhc != 0 );
cpMacCur = CpMax( cpMacCur, vcpLimParaCache );

selCur.cpFirst = cpMinCur;  /* Expand selection to entire area so sprm */
selCur.cpLim = cpMacCur;    /* applies to it all */

rgb [0] = sprmPRhc;
rgb [1] = rhc;
AddOneSprm(rgb, FALSE);

} /* end of DoFormatRHText */




MakeRunningCps( doc, cp, dcp )
int doc;
typeCP  cp;
typeCP  dcp;
{   /* Make the cp range suitable for inclusion in a runninng head or foot.
       This means: (1) Apply a Sprm to the whole thing so it is formatted
       as a running head/foot, (2) Remove any chSects, replacing them
       with Eol's */
 extern struct UAB vuab;
 CHAR   rgb [4];
 int    rhc;
 int    fAdjCpMacCur;
 typeCP cpLimPara;
 typeCP cpT;
 struct SEL selSave;

 if (dcp==cp0 || !FWriteOk( fwcNil ))
    return;

 selSave = selCur;

 /* Scan the cp range, replacing chSects with Eols */

 for ( cpT = cp;
       CachePara( doc, cpT ), (cpLimPara=vcpLimParaCache) <= cp + dcp;
       cpT = cpLimPara )
    {
    typeCP cpLastPara=cpLimPara-1;

    Assert( cpLimPara > cpT );  /* Otherwise we are locked in the loop */

    FetchCp( doc, cpLastPara, 0, fcmChars );
    if (*vpchFetch == chSect)
    {
    struct PAP papT;

    CachePara( doc, cpT );
    papT = vpapAbs;

    Replace( doc, cpLastPara+ccpEol, (typeCP)1, fnNil, fc0, fc0 );
    InsertEolPap( doc, cpLastPara, &papT );

    if (ferror)
        {
        NoUndo();
        break;
        }

        /* Adjust Undo count to account for extra insertion */
    vuab.dcp += (typeCP)(ccpEol-1);
    CachePara( doc, cpT );
    cpLimPara = vcpLimParaCache;
    }
    }

 /* Apply a Sprm that makes everything a running head/foot */

 rhc = RHC_fOdd + RHC_fEven;
 if (wwdCurrentDoc.fEditFooter)
    rhc += RHC_fBottom;

 selCur.cpFirst = cp;            /* OK to just assign to selCur */
 selCur.cpLim   = cp + dcp;      /* because AddOneSprm will handle */

 /* We must temporarily set cpMacCur so that it includes the Eol
    at the end of the header/footer range. Otherwise, AddOneSprm
    may decide it needs to insert a superfluous Eol */

 CachePara( docCur, selCur.cpLim-1 );
 if (fAdjCpMacCur = (vcpLimParaCache > cpMacCur))
    cpMacCur += ccpEol;

 rgb [0] = sprmPRhc;
 rgb [1] = rhc;
 AddOneSprm(rgb, FALSE);    /* Do not set UNDO; we want to undo the paste,
                   which will take care of undoing the sprm */
 if (fAdjCpMacCur)
     cpMacCur -= ccpEol;

 Select( selSave.cpFirst, selCur.cpLim );
}