Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * draw.c
 * Copyright (C) 1998-2005 A.J. van Os; Released under GPL
 *
 * Description:
 * Functions to deal with the Draw format
 */

#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include "DeskLib:KeyCodes.h"
#include "DeskLib:Error.h"
#include "DeskLib:Menu.h"
#include "DeskLib:Template.h"
#include "DeskLib:Window.h"
#include "DeskLib:EventMsg.h"
#include "flexlib:flex.h"
#include "drawfile.h"
#include "antiword.h"

/* The work area must be a little bit larger than the diagram */
#define WORKAREA_EXTENSION          5
/* Diagram memory */
#define INITIAL_SIZE            32768   /* 32k */
#define EXTENSION_SIZE           4096   /*  4k */
/* Main window title */
#define WINDOW_TITLE_LEN           28
#define FILENAME_TITLE_LEN      (WINDOW_TITLE_LEN - 10)


#if !defined(__GNUC__)
int
flex_alloc(flex_ptr anchor, int n)
{
        void    *pvTmp;

        TRACE_MSG("flex_alloc");

        if (anchor == NULL || n < 0) {
                return 0;
        }
        if (n == 0) {
                n = 1;
        }
        pvTmp = malloc(n);
        if (pvTmp == NULL) {
                return 0;
        }
        *anchor = pvTmp;
        return 1;
} /* end of flex_alloc */

void
flex_free(flex_ptr anchor)
{
        TRACE_MSG("flex_free");

        if (anchor == NULL || *anchor == NULL) {
                return;
        }
        free(*anchor);
        *anchor = NULL;
} /* end of flex_free */

int
flex_extend(flex_ptr anchor, int newsize)
{
        void    *pvTmp;

        TRACE_MSG("flex_extend");

        if (anchor == NULL || newsize < 0) {
                return 0;
        }
        if (newsize == 0) {
                newsize = 1;
        }
        pvTmp = realloc(*anchor, newsize);
        if (pvTmp == NULL) {
                return 0;
        }
        *anchor = pvTmp;
        return 1;
} /* end of flex_extend */
#endif /* !__GNUC__ */

/*
 * vCreateMainWindow - create the Main window
 *
 * remark: does not return if the Main window can't be created
 */
static window_handle
tCreateMainWindow(void)
{
        window_handle   tMainWindow;

        TRACE_MSG("tCreateMainWindow");

        tMainWindow = Window_Create("MainWindow", template_TITLEMIN);
        if (tMainWindow == 0) {
                werr(1, "I can't find the 'MainWindow' template");
        }
        return tMainWindow;
} /* end of tCreateMainWindow */

/*
 * vCreateScaleWindow - create the Scale view window
 *
 * remark: does not return if the Scale view window can't be created
 */
static window_handle
tCreateScaleWindow(void)
{
        window_handle   tScaleWindow;

        TRACE_MSG("tCreateScaleWindow");

        tScaleWindow = Window_Create("ScaleView", template_TITLEMIN);
        if (tScaleWindow == 0) {
                werr(1, "I can't find the 'ScaleView' template");
        }
        return tScaleWindow;
} /* end of tCreateScaleWindow */

/*
 * pCreateDiagram - create and initialize a diagram
 *
 * remark: does not return if the diagram can't be created
 */
diagram_type *
pCreateDiagram(const char *szTask, const char *szFilename)
{
        diagram_type    *pDiag;
        options_type    tOptions;
        window_handle   tMainWindow, tScaleWindow;
        wimp_box        tBox;

        TRACE_MSG("pCreateDiagram");

        fail(szTask == NULL || szTask[0] == '\0');

        /* Create the main window */
        tMainWindow = tCreateMainWindow();

        /* Create the scale view window */
        tScaleWindow = tCreateScaleWindow();

        /* Get the necessary memory */
        pDiag = xmalloc(sizeof(diagram_type));
        if (flex_alloc((flex_ptr)&pDiag->tInfo.data, INITIAL_SIZE) != 1) {
                werr(1, "Memory allocation failed, unable to continue");
        }

        /* Initialize the diagram */
        vGetOptions(&tOptions);
        pDiag->tMainWindow = tMainWindow;
        pDiag->tScaleWindow = tScaleWindow;
        pDiag->iScaleFactorCurr = tOptions.iScaleFactor;
        pDiag->iScaleFactorTemp = tOptions.iScaleFactor;
        pDiag->tMemorySize = INITIAL_SIZE;
        tBox.min.x = 0;
        tBox.min.y = -(Drawfile_ScreenToDraw(32 + 3) * 8 + 1);
        tBox.max.x = Drawfile_ScreenToDraw(16) * MIN_SCREEN_WIDTH + 1;
        tBox.max.y = 0;
        Error_CheckFatal(Drawfile_CreateDiagram(&pDiag->tInfo,
                                        pDiag->tMemorySize, szTask, tBox));
        DBG_DEC(pDiag->tInfo.length);
        pDiag->lXleft = 0;
        pDiag->lYtop = 0;
        strncpy(pDiag->szFilename,
                        szBasename(szFilename), sizeof(pDiag->szFilename) - 1);
        pDiag->szFilename[sizeof(pDiag->szFilename) - 1] = '\0';
        /* Return success */
        return pDiag;
} /* end of pCreateDiagram */

/*
 * bDestroyDiagram - remove a diagram by freeing the memory it uses
 */
BOOL
bDestroyDiagram(event_pollblock *pEvent, void *pvReference)
{
        diagram_type    *pDiag;
        window_handle   tWindow;

        TRACE_MSG("bDestroyDiagram");

        fail(pEvent == NULL);
        fail(pvReference == NULL);

        if (pEvent == NULL || pvReference == NULL) {
                return FALSE;
        }

        pDiag = (diagram_type *)pvReference;

        switch (pEvent->type) {
        case event_CLOSE:
                tWindow = pEvent->data.openblock.window;
                break;
        case event_KEY:
                tWindow = pEvent->data.key.caret.window;
                break;
        default:
                DBG_DEC(pEvent->type);
                return FALSE;
        }
        if (tWindow != pDiag->tMainWindow) {
                return FALSE;
        }

        /* Delete the main window */
        Window_Delete(pDiag->tMainWindow);
        pDiag->tMainWindow = 0;

        /* Delete the scale window */
        Window_Delete(pDiag->tScaleWindow);
        pDiag->tScaleWindow = 0;

#if defined(__GNUC__)
        /*
         * Remove all references to the diagram that will be free-ed
         * by undoing the EventMsg_Claim's from within the Menu_Warn's
         */
        while (EventMsg_ReleaseSpecific(message_MENUWARNING, window_ANY,
                                        bSaveTextfile, pDiag))
                ; /* EMPTY */
        while (EventMsg_ReleaseSpecific(message_MENUWARNING, window_ANY,
                                        bSaveDrawfile, pDiag))
                ; /* EMPTY */
        while (EventMsg_ReleaseSpecific(message_MENUWARNING, window_ANY,
                                        bScaleOpenAction, pDiag))
                ; /* EMPTY */
#endif /* __GNUC__ */

        /* Free the memory */
        if (pDiag->tInfo.data != NULL && pDiag->tMemorySize != 0) {
                flex_free((flex_ptr)&pDiag->tInfo.data);
        }
        /* Just to be on the save side */
        pDiag->tInfo.data = NULL;
        pDiag->tInfo.length = 0;
        pDiag->tMemorySize = 0;

        /* Destroy the diagram itself */
        pDiag = xfree(pDiag);
        return TRUE;
} /* end of bDestroyDiagram */

/*
 * vExtendDiagramSize - make sure the diagram is big enough
 */
static void
vExtendDiagramSize(diagram_type *pDiag, size_t tSize)
{
        TRACE_MSG("vExtendDiagramSize");

        fail(pDiag == NULL || tSize % 4 != 0);

        while (pDiag->tInfo.length + tSize > pDiag->tMemorySize) {
                if (flex_extend((flex_ptr)&pDiag->tInfo.data,
                                pDiag->tMemorySize + EXTENSION_SIZE) != 1) {
                        werr(1, "Memory extend failed, unable to continue");
                }
                pDiag->tMemorySize += EXTENSION_SIZE;
                NO_DBG_DEC(pDiag->tMemorySize);
        }
        TRACE_MSG("end of vExtendDiagramSize");
} /* end of vExtendDiagramSize */

/*
 * vPrologue2 - prologue part 2; add a font list to a diagram
 */
void
vPrologue2(diagram_type *pDiag, int iWordVersion)
{
        drawfile_object *pNew;
        const font_table_type   *pTmp;
        char    *pcTmp;
        size_t  tRealSize, tSize;
        int     iCount;

        TRACE_MSG("vPrologue2");

        fail(pDiag == NULL);

        if (tGetFontTableLength() == 0) {
                return;
        }
        tRealSize = offsetof(drawfile_object, data);
        pTmp = NULL;
        while ((pTmp = pGetNextFontTableRecord(pTmp)) != NULL) {
                tRealSize += 2 + strlen(pTmp->szOurFontname);
        }
        DBG_DEC(tRealSize);
        tSize = ROUND4(tRealSize);
        vExtendDiagramSize(pDiag, tSize);
        pNew = xmalloc(tSize);
        memset(pNew, 0, tSize);
        pNew->type = drawfile_TYPE_FONT_TABLE;
        pNew->size = tSize;
        pcTmp = (char *)&pNew->data.font_table.font_def[0].font_ref;
        iCount = 0;
        pTmp = NULL;
        while ((pTmp = pGetNextFontTableRecord(pTmp)) != NULL) {
                *pcTmp = ++iCount;
                pcTmp++;
                strcpy(pcTmp, pTmp->szOurFontname);
                pcTmp += 1 + strlen(pTmp->szOurFontname);
        }
        Error_CheckFatal(Drawfile_AppendObject(&pDiag->tInfo,
                        pDiag->tMemorySize, pNew, TRUE));
        pNew = xfree(pNew);
} /* end of vPrologue2 */

/*
 * vSubstring2Diagram - put a sub string into a diagram
 */
void
vSubstring2Diagram(diagram_type *pDiag,
        char *szString, size_t tStringLength, long lStringWidth,
        UCHAR ucFontColor, USHORT usFontstyle, drawfile_fontref tFontRef,
        USHORT usFontSize, USHORT usMaxFontSize)
{
        drawfile_object *pNew;
        long    lSizeX, lSizeY, lOffset, l20, lYMove;
        size_t  tRealSize, tSize;

        TRACE_MSG("vSubstring2Diagram");

        fail(pDiag == NULL || szString == NULL);
        fail(pDiag->lXleft < 0);
        fail(tStringLength != strlen(szString));
        fail(usFontSize < MIN_FONT_SIZE || usFontSize > MAX_FONT_SIZE);
        fail(usMaxFontSize < MIN_FONT_SIZE || usMaxFontSize > MAX_FONT_SIZE);
        fail(usFontSize > usMaxFontSize);

        if (szString[0] == '\0' || tStringLength == 0) {
                return;
        }

        if (tFontRef == 0) {
                lOffset = Drawfile_ScreenToDraw(2);
                l20 = Drawfile_ScreenToDraw(32 + 3);
                lSizeX = Drawfile_ScreenToDraw(16);
                lSizeY = Drawfile_ScreenToDraw(32);
        } else {
                lOffset = lToBaseLine(usMaxFontSize);
                l20 = lWord2DrawUnits20(usMaxFontSize);
                lSizeX = lWord2DrawUnits00(usFontSize);
                lSizeY = lWord2DrawUnits00(usFontSize);
        }

        lYMove = 0;

        /* Up for superscript */
        if (bIsSuperscript(usFontstyle)) {
                lYMove = lMilliPoints2DrawUnits((((long)usFontSize + 1) / 2) * 375);
        }
        /* Down for subscript */
        if (bIsSubscript(usFontstyle)) {
                lYMove = -lMilliPoints2DrawUnits((long)usFontSize * 125);
        }

        tRealSize = offsetof(drawfile_object, data);
        tRealSize += sizeof(drawfile_text) + tStringLength;
        tSize = ROUND4(tRealSize);
        vExtendDiagramSize(pDiag, tSize);
        pNew = xmalloc(tSize);
        memset(pNew, 0, tSize);
        pNew->type = drawfile_TYPE_TEXT;
        pNew->size = tSize;
        pNew->data.text.bbox.min.x = (int)pDiag->lXleft;
        pNew->data.text.bbox.min.y = (int)(pDiag->lYtop + lYMove);
        pNew->data.text.bbox.max.x = (int)(pDiag->lXleft + lStringWidth);
        pNew->data.text.bbox.max.y = (int)(pDiag->lYtop + l20 + lYMove);
        pNew->data.text.fill.value = (int)ulColor2Color(ucFontColor);
        pNew->data.text.bg_hint.value = 0xffffff00;     /* White */
        pNew->data.text.style.font_ref = tFontRef;
        pNew->data.text.style.reserved[0] = 0;
        pNew->data.text.style.reserved[1] = 0;
        pNew->data.text.style.reserved[2] = 0;
        pNew->data.text.xsize = (int)lSizeX;
        pNew->data.text.ysize = (int)lSizeY;
        pNew->data.text.base.x = (int)pDiag->lXleft;
        pNew->data.text.base.y = (int)(pDiag->lYtop + lOffset + lYMove);
        strncpy(pNew->data.text.text, szString, tStringLength);
        pNew->data.text.text[tStringLength] = '\0';
        Error_CheckFatal(Drawfile_AppendObject(&pDiag->tInfo,
                        pDiag->tMemorySize, pNew, TRUE));
        pNew = xfree(pNew);
        /*draw_translateText(&pDiag->tInfo);*/
        pDiag->lXleft += lStringWidth;
        TRACE_MSG("leaving vSubstring2Diagram");
} /* end of vSubstring2Diagram */

/*
 * vImage2Diagram - put an image into a diagram
 */
void
vImage2Diagram(diagram_type *pDiag, const imagedata_type *pImg,
        UCHAR *pucImage, size_t tImageSize)
{
        drawfile_object *pNew;
        long    lWidth, lHeight;
        size_t  tRealSize, tSize;

        TRACE_MSG("vImage2Diagram");

        fail(pDiag == NULL);
        fail(pImg == NULL);
        fail(pDiag->lXleft < 0);
        fail(pImg->eImageType != imagetype_is_dib &&
             pImg->eImageType != imagetype_is_jpeg);

        DBG_DEC_C(pDiag->lXleft != 0, pDiag->lXleft);

        lWidth = lPoints2DrawUnits(pImg->iHorSizeScaled);
        lHeight = lPoints2DrawUnits(pImg->iVerSizeScaled);
        DBG_DEC(lWidth);
        DBG_DEC(lHeight);

        pDiag->lYtop -= lHeight;

        tRealSize = offsetof(drawfile_object, data);
        switch (pImg->eImageType) {
        case imagetype_is_dib:
                tRealSize += sizeof(drawfile_sprite) + tImageSize;
                tSize = ROUND4(tRealSize);
                vExtendDiagramSize(pDiag, tSize);
                pNew = xmalloc(tSize);
                memset(pNew, 0, tSize);
                pNew->type = drawfile_TYPE_SPRITE;
                pNew->size = tSize;
                pNew->data.sprite.bbox.min.x = (int)pDiag->lXleft;
                pNew->data.sprite.bbox.min.y = (int)pDiag->lYtop;
                pNew->data.sprite.bbox.max.x = (int)(pDiag->lXleft + lWidth);
                pNew->data.sprite.bbox.max.y = (int)(pDiag->lYtop + lHeight);
                memcpy(&pNew->data.sprite.header, pucImage, tImageSize);
                break;
        case imagetype_is_jpeg:
#if defined(DEBUG)
                (void)bGetJpegInfo(pucImage, tImageSize);
#endif /* DEBUG */
                tRealSize += sizeof(drawfile_jpeg) + tImageSize;
                tSize = ROUND4(tRealSize);
                vExtendDiagramSize(pDiag, tSize);
                pNew = xmalloc(tSize);
                memset(pNew, 0, tSize);
                pNew->type = drawfile_TYPE_JPEG;
                pNew->size = tSize;
                pNew->data.jpeg.bbox.min.x = (int)pDiag->lXleft;
                pNew->data.jpeg.bbox.min.y = (int)pDiag->lYtop;
                pNew->data.jpeg.bbox.max.x = (int)(pDiag->lXleft + lWidth);
                pNew->data.jpeg.bbox.max.y = (int)(pDiag->lYtop + lHeight);
                pNew->data.jpeg.width = (int)lWidth;
                pNew->data.jpeg.height = (int)lHeight;
                pNew->data.jpeg.xdpi = 90;
                pNew->data.jpeg.ydpi = 90;
                pNew->data.jpeg.trfm.entries[0][0] = 0x10000;
                pNew->data.jpeg.trfm.entries[0][1] = 0;
                pNew->data.jpeg.trfm.entries[1][0] = 0;
                pNew->data.jpeg.trfm.entries[1][1] = 0x10000;
                pNew->data.jpeg.trfm.entries[2][0] = (int)pDiag->lXleft;
                pNew->data.jpeg.trfm.entries[2][1] = (int)pDiag->lYtop;
                pNew->data.jpeg.len = tImageSize;
                memcpy(pNew->data.jpeg.data, pucImage, tImageSize);
                break;
        default:
                DBG_DEC(pImg->eImageType);
                pNew = NULL;
                break;
        }

        Error_CheckFatal(Drawfile_AppendObject(&pDiag->tInfo,
                                        pDiag->tMemorySize, pNew, TRUE));
        pNew = xfree(pNew);
        pDiag->lXleft = 0;
} /* end of vImage2Diagram */

/*
 * bAddDummyImage - add a dummy image
 *
 * return TRUE when successful, otherwise FALSE
 */
BOOL
bAddDummyImage(diagram_type *pDiag, const imagedata_type *pImg)
{
        drawfile_object *pNew;
        int     *piTmp;
        long    lWidth, lHeight;
        size_t  tRealSize, tSize;

        TRACE_MSG("bAddDummyImage");

        fail(pDiag == NULL);
        fail(pImg == NULL);
        fail(pDiag->lXleft < 0);

        if (pImg->iVerSizeScaled <= 0 || pImg->iHorSizeScaled <= 0) {
                return FALSE;
        }

        DBG_DEC_C(pDiag->lXleft != 0, pDiag->lXleft);

        lWidth = lPoints2DrawUnits(pImg->iHorSizeScaled);
        lHeight = lPoints2DrawUnits(pImg->iVerSizeScaled);

        pDiag->lYtop -= lHeight;

        tRealSize = offsetof(drawfile_object, data);
        tRealSize += sizeof(drawfile_path) + (14 - 1) * sizeof(int);
        tSize = ROUND4(tRealSize);
        vExtendDiagramSize(pDiag, tSize);
        pNew = xmalloc(tSize);
        memset(pNew, 0, tSize);
        pNew->type = drawfile_TYPE_PATH;
        pNew->size = tSize;
        pNew->data.path.bbox.min.x = (int)pDiag->lXleft;
        pNew->data.path.bbox.min.y = (int)pDiag->lYtop;
        pNew->data.path.bbox.max.x = (int)(pDiag->lXleft + lWidth);
        pNew->data.path.bbox.max.y = (int)(pDiag->lYtop + lHeight);
        pNew->data.path.fill.value = -1;
        pNew->data.path.outline.value = 0x4d4d4d00;     /* Gray 70 percent */
        pNew->data.path.width = (int)lMilliPoints2DrawUnits(500);
        pNew->data.path.style.flags = 0;
        pNew->data.path.style.reserved = 0;
        pNew->data.path.style.cap_width = 0;
        pNew->data.path.style.cap_length = 0;
        piTmp = pNew->data.path.path;
        *piTmp++ = drawfile_PATH_MOVE_TO;
        *piTmp++ = pNew->data.path.bbox.min.x;
        *piTmp++ = pNew->data.path.bbox.min.y;
        *piTmp++ = drawfile_PATH_LINE_TO;
        *piTmp++ = pNew->data.path.bbox.min.x;
        *piTmp++ = pNew->data.path.bbox.max.y;
        *piTmp++ = drawfile_PATH_LINE_TO;
        *piTmp++ = pNew->data.path.bbox.max.x;
        *piTmp++ = pNew->data.path.bbox.max.y;
        *piTmp++ = drawfile_PATH_LINE_TO;
        *piTmp++ = pNew->data.path.bbox.max.x;
        *piTmp++ = pNew->data.path.bbox.min.y;
        *piTmp++ = drawfile_PATH_CLOSE_LINE;
        *piTmp++ = drawfile_PATH_END_PATH;

        Error_CheckFatal(Drawfile_AppendObject(&pDiag->tInfo,
                                        pDiag->tMemorySize, pNew, TRUE));
        pNew = xfree(pNew);
        pDiag->lXleft = 0;
        return TRUE;
} /* end of bAddDummyImage */

/*
 * vMove2NextLine - move to the next line
 */
void
vMove2NextLine(diagram_type *pDiag, drawfile_fontref tFontRef,
        USHORT usFontSize)
{
        long    l20;

        TRACE_MSG("vMove2NextLine");

        fail(pDiag == NULL);
        fail(usFontSize < MIN_FONT_SIZE || usFontSize > MAX_FONT_SIZE);

        if (tFontRef == 0) {
                l20 = Drawfile_ScreenToDraw(32 + 3);
        } else {
                l20 = lWord2DrawUnits20(usFontSize);
        }
        pDiag->lYtop -= l20;
} /* end of vMove2NextLine */

/*
 * Create an start of paragraph (Phase 1)
 */
void
vStartOfParagraph1(diagram_type *pDiag, long lBeforeIndentation)
{
        TRACE_MSG("vStartOfParagraph1");

        fail(pDiag == NULL);
        fail(lBeforeIndentation < 0);

        pDiag->lXleft = 0;
        pDiag->lYtop -= lMilliPoints2DrawUnits(lBeforeIndentation);
} /* end of vStartOfParagraph1 */

/*
 * Create an start of paragraph (Phase 2)
 * DUMMY function
 */
void
vStartOfParagraph2(diagram_type *pDiag)
{
        TRACE_MSG("vStartOfParagraph2");
} /* end of vStartOfParagraph2 */

/*
 * Create an end of paragraph
 */
void
vEndOfParagraph(diagram_type *pDiag,
        drawfile_fontref tFontRef, USHORT usFontSize, long lAfterIndentation)
{
        TRACE_MSG("vEndOfParagraph");

        fail(pDiag == NULL);
        fail(usFontSize < MIN_FONT_SIZE || usFontSize > MAX_FONT_SIZE);
        fail(lAfterIndentation < 0);

        pDiag->lXleft = 0;
        pDiag->lYtop -= lMilliPoints2DrawUnits(lAfterIndentation);
} /* end of vEndOfParagraph */

/*
 * Create an end of page
 */
void
vEndOfPage(diagram_type *pDiag, long lAfterIndentation, BOOL bNewSection)
{
        TRACE_MSG("vEndOfPage");

        fail(pDiag == NULL);
        fail(lAfterIndentation < 0);

        pDiag->lXleft = 0;
        pDiag->lYtop -= lMilliPoints2DrawUnits(lAfterIndentation);
} /* end of vEndOfPage */

/*
 * vSetHeaders - set the headers
 * DUMMY function
 */
void
vSetHeaders(diagram_type *pDiag, USHORT usIstd)
{
        TRACE_MSG("vSetHeaders");
} /* end of vSetHeaders */

/*
 * Create a start of list
 * DUMMY function
 */
void
vStartOfList(diagram_type *pDiag, UCHAR ucNFC, BOOL bIsEndOfTable)
{
        TRACE_MSG("vStartOfList");
} /* end of vStartOfList */

/*
 * Create an end of list
 * DUMMY function
 */
void
vEndOfList(diagram_type *pDiag)
{
        TRACE_MSG("vEndOfList");
} /* end of vEndOfList */

/*
 * Create a start of a list item
 * DUMMY function
 */
void
vStartOfListItem(diagram_type *pDiag, BOOL bNoMarks)
{
        TRACE_MSG("vStartOfListItem");
} /* end of vStartOfListItem */

/*
 * Create an end of a table
 * DUMMY function
 */
void
vEndOfTable(diagram_type *pDiag)
{
        TRACE_MSG("vEndOfTable");
} /* end of vEndTable */

/*
 * Add a table row
 * DUMMY function
 *
 * Returns TRUE when conversion type is XML
 */
BOOL
bAddTableRow(diagram_type *pDiag, char **aszColTxt,
        int iNbrOfColumns, const short *asColumnWidth, UCHAR ucBorderInfo)
{
        TRACE_MSG("bAddTableRow");

        return FALSE;
} /* end of bAddTableRow */

/*
 * vForceRedraw - force a redraw of the main window
 */
static void
vForceRedraw(diagram_type *pDiag)
{
        window_state            tWindowState;
        window_redrawblock      tRedraw;
        int     x0, y0, x1, y1;

        TRACE_MSG("vForceRedraw");

        fail(pDiag == NULL);

        DBG_DEC(pDiag->iScaleFactorCurr);

        /* Read the size of the current diagram */
        Drawfile_QueryBox(&pDiag->tInfo, &tRedraw.rect, TRUE);
        /* Adjust the size of the work area */
        x0 = tRedraw.rect.min.x * pDiag->iScaleFactorCurr / 100 - 1;
        y0 = tRedraw.rect.min.y * pDiag->iScaleFactorCurr / 100 - 1;
        x1 = tRedraw.rect.max.x * pDiag->iScaleFactorCurr / 100 + 1;
        y1 = tRedraw.rect.max.y * pDiag->iScaleFactorCurr / 100 + 1;
        /* Work area extension */
        x0 -= WORKAREA_EXTENSION;
        y0 -= WORKAREA_EXTENSION;
        x1 += WORKAREA_EXTENSION;
        y1 += WORKAREA_EXTENSION;
        Window_SetExtent(pDiag->tMainWindow, x0, y0, x1, y1);
        /* Widen the box slightly to be sure all the edges are drawn */
        x0 -= 5;
        y0 -= 5;
        x1 += 5;
        y1 += 5;
        /* Force the redraw */
        Window_ForceRedraw(pDiag->tMainWindow, x0, y0, x1, y1);
        /* Reopen the window to show the correct size */
        Error_CheckFatal(Wimp_GetWindowState(pDiag->tMainWindow, &tWindowState));
        tWindowState.openblock.behind = -1;
        Error_CheckFatal(Wimp_OpenWindow(&tWindowState.openblock));
} /* end of vForceRedraw */

/*
 * vShowDiagram - put the diagram on the screen
 */
void
vShowDiagram(diagram_type *pDiag)
{
        wimp_box        tRect;
        int     x0, y0, x1, y1;

        TRACE_MSG("vShowDiagram");

        fail(pDiag == NULL);

        Window_Show(pDiag->tMainWindow, open_NEARLAST);
        Drawfile_QueryBox(&pDiag->tInfo, &tRect, TRUE);
        /* Work area extension */
        x0 = tRect.min.x - WORKAREA_EXTENSION;
        y0 = tRect.min.y - WORKAREA_EXTENSION;
        x1 = tRect.max.x + WORKAREA_EXTENSION;
        y1 = tRect.max.y + WORKAREA_EXTENSION;
        Window_SetExtent(pDiag->tMainWindow, x0, y0, x1, y1);
        vForceRedraw(pDiag);
} /* end of vShowDiagram */

/*
 * vMainButtonClick - handle mouse buttons clicks for the main screen
 */
void
vMainButtonClick(mouse_block *pMouse)
{
        caret_block     tCaret;
        window_state    ws;

        TRACE_MSG("vMainButtonClick");

        fail(pMouse == NULL);

        DBG_DEC(pMouse->button.data.select);
        DBG_DEC(pMouse->button.data.adjust);
        DBG_DEC(pMouse->window);
        DBG_DEC(pMouse->icon);

        if (pMouse->window >= 0 &&
            pMouse->icon == -1 &&
            (pMouse->button.data.select || pMouse->button.data.adjust)) {
                /* Get the input focus */
                Error_CheckFatal(Wimp_GetWindowState(pMouse->window, &ws));
                tCaret.window = pMouse->window;
                tCaret.icon = -1;
                tCaret.offset.x = pMouse->pos.x - ws.openblock.screenrect.min.x;
                tCaret.offset.y = pMouse->pos.y - ws.openblock.screenrect.max.y;
                tCaret.height = (int)BIT(25);
                tCaret.index = 0;
                Error_CheckFatal(Wimp_SetCaretPosition(&tCaret));
        }
} /* end of vMainButtonClick */

/*
 * bMainKeyPressed - handle pressed keys for the main window
 */
BOOL
bMainKeyPressed(event_pollblock *pEvent, void *pvReference)
{
        diagram_type    *pDiag;

        TRACE_MSG("bMainKeyPressed");

        fail(pEvent == NULL);
        fail(pEvent->type != event_KEY);
        fail(pvReference == NULL);

        pDiag = (diagram_type *)pvReference;

        fail(pEvent->data.key.caret.window != pDiag->tMainWindow);


        switch (pEvent->data.key.code) {
        case keycode_CTRL_F2:           /* Ctrl F2 */
                bDestroyDiagram(pEvent, pvReference);
                break;
        case keycode_F3:                /* F3 */
                bSaveDrawfile(pEvent, pvReference);
                break;
        case keycode_SHIFT_F3:          /* Shift F3 */
                bSaveTextfile(pEvent, pvReference);
                break;
        default:
                DBG_DEC(pEvent->data.key.code);
                Error_CheckFatal(Wimp_ProcessKey(pEvent->data.key.code));
        }
        return TRUE;
} /* end of bMainKeyPressed */

/*
 * bRedrawMainWindow - redraw the main window
 */
BOOL
bRedrawMainWindow(event_pollblock *pEvent, void *pvReference)
{
        window_redrawblock      tBlock;
        diagram_type    *pDiag;
        drawfile_info   *pInfo;
        double          dScaleFactor;
        BOOL            bMore;

        TRACE_MSG("bRedrawMainWindow");

        fail(pEvent == NULL);
        fail(pEvent->type != event_REDRAW);
        fail(pvReference == NULL);

        pDiag = (diagram_type *)pvReference;

        fail(pDiag->tMainWindow != pEvent->data.openblock.window);
        fail(pDiag->iScaleFactorCurr < MIN_SCALE_FACTOR);
        fail(pDiag->iScaleFactorCurr > MAX_SCALE_FACTOR);

        dScaleFactor = (double)pDiag->iScaleFactorCurr / 100.0;
        pInfo = &pDiag->tInfo;

        tBlock.window = pEvent->data.openblock.window;
        Error_CheckFatal(Wimp_RedrawWindow(&tBlock, &bMore));

        /* If there is no real diagram just go thru the motions */
        while (bMore) {
                if (pInfo->data != NULL && pInfo->length != 0) {
                        Error_CheckFatal(Drawfile_RenderDiagram(pInfo,
                                                &tBlock, dScaleFactor));
                }
                Error_CheckFatal(Wimp_GetRectangle(&tBlock, &bMore));
        }
        return TRUE;
} /* end of bRedrawMainWindow */

/*
 * bScaleOpenAction - action to be taken when the Scale view window opens
 */
BOOL
bScaleOpenAction(event_pollblock *pEvent, void *pvReference)
{
        window_state    tWindowState;
        diagram_type    *pDiag;

        TRACE_MSG("bScaleOpenAction");

        fail(pEvent == NULL);
        fail(pEvent->type != event_SEND);
        fail(pEvent->data.message.header.action != message_MENUWARN);
        fail(pvReference == NULL);

        pDiag = (diagram_type *)pvReference;

        if (menu_currentopen != pDiag->pSaveMenu ||
            pEvent->data.message.data.menuwarn.selection[0] != SAVEMENU_SCALEVIEW) {
                return FALSE;
        }

        Error_CheckFatal(Wimp_GetWindowState(pDiag->tScaleWindow,
                                                &tWindowState));
        if (tWindowState.flags.data.open) {
                /* The window is already open */
                return TRUE;
        }

        DBG_MSG("vScaleOpenAction for real");

        pDiag->iScaleFactorTemp = pDiag->iScaleFactorCurr;
        vUpdateWriteableNumber(pDiag->tScaleWindow,
                        SCALE_SCALE_WRITEABLE, pDiag->iScaleFactorTemp);
        Window_Show(pDiag->tScaleWindow, open_UNDERPOINTER);
        return TRUE;
} /* end of bScaleOpenAction */

/*
 * vSetTitle - set the title of a window
 */
void
vSetTitle(diagram_type *pDiag)
{
        char    szTitle[WINDOW_TITLE_LEN];

        TRACE_MSG("vSetTitle");

        fail(pDiag == NULL);
        fail(pDiag->szFilename[0] == '\0');

        (void)sprintf(szTitle, "%.*s at %d%%",
                                FILENAME_TITLE_LEN,
                                pDiag->szFilename,
                                pDiag->iScaleFactorCurr % 1000);
        if (strlen(pDiag->szFilename) > FILENAME_TITLE_LEN) {
                szTitle[FILENAME_TITLE_LEN - 1] = OUR_ELLIPSIS;
        }

        Window_SetTitle(pDiag->tMainWindow, szTitle);
} /* end of vSetTitle */

/*
 * vScaleButtonClick - handle a mouse button click in the Scale view window
 */
void
vScaleButtonClick(mouse_block *pMouse, diagram_type *pDiag)
{
        BOOL    bCloseWindow, bRedraw;

        TRACE_MSG("vScaleButtonClick");

        fail(pMouse == NULL || pDiag == NULL);
        fail(pMouse->window != pDiag->tScaleWindow);

        bCloseWindow = FALSE;
        bRedraw = FALSE;
        switch (pMouse->icon) {
        case SCALE_CANCEL_BUTTON:
                bCloseWindow = TRUE;
                pDiag->iScaleFactorTemp = pDiag->iScaleFactorCurr;
                break;
        case SCALE_SCALE_BUTTON:
                bCloseWindow = TRUE;
                bRedraw = pDiag->iScaleFactorCurr != pDiag->iScaleFactorTemp;
                pDiag->iScaleFactorCurr = pDiag->iScaleFactorTemp;
                break;
        case SCALE_50_PCT:
                pDiag->iScaleFactorTemp = 50;
                break;
        case SCALE_75_PCT:
                pDiag->iScaleFactorTemp = 75;
                break;
        case SCALE_100_PCT:
                pDiag->iScaleFactorTemp = 100;
                break;
        case SCALE_150_PCT:
                pDiag->iScaleFactorTemp = 150;
                break;
        default:
                DBG_DEC(pMouse->icon);
                break;
        }
        if (bCloseWindow) {
                /* Close the scale window */
                Error_CheckFatal(Wimp_CloseWindow(pMouse->window));
                if (bRedraw) {
                        /* Redraw the main window */
                        vSetTitle(pDiag);
                        vForceRedraw(pDiag);
                }
        } else {
                vUpdateWriteableNumber(pMouse->window,
                                SCALE_SCALE_WRITEABLE,
                                pDiag->iScaleFactorTemp);
        }
} /* end of vScaleButtonClick */

/*
 * bScaleKeyPressed - handle pressed keys for the scale window
 */
BOOL
bScaleKeyPressed(event_pollblock *pEvent, void *pvReference)
{
        icon_block      tIcon;
        diagram_type    *pDiag;
        caret_block     *pCaret;
        char            *pcChar;
        int             iTmp;

        TRACE_MSG("bScaleKeyPressed");

        fail(pEvent == NULL);
        fail(pEvent->type != event_KEY);
        fail(pvReference == NULL);

        pCaret = &pEvent->data.key.caret;
        pDiag = (diagram_type *)pvReference;

        fail(pEvent->data.key.caret.window != pDiag->tScaleWindow);

        DBG_DEC_C(pCaret->icon != SCALE_SCALE_WRITEABLE, pCaret->icon);
        DBG_DEC_C(pCaret->icon == SCALE_SCALE_WRITEABLE, pEvent->data.key.code);

        if (pEvent->data.key.code != '\r' ||
            pCaret->icon != SCALE_SCALE_WRITEABLE) {
                Error_CheckFatal(Wimp_ProcessKey(pEvent->data.key.code));
                return TRUE;
        }

        Error_CheckFatal(Wimp_GetIconState(pCaret->window, pCaret->icon, &tIcon));
        if (!tIcon.flags.data.text || !tIcon.flags.data.indirected) {
                werr(1, "Icon %d must be indirected text", (int)pCaret->icon);
        }
        iTmp = (int)strtol(tIcon.data.indirecttext.buffer, &pcChar, 10);
        if (*pcChar != '\0' && *pcChar != '\r') {
                DBG_DEC(*pcChar);
        } else if (iTmp < MIN_SCALE_FACTOR) {
                pDiag->iScaleFactorTemp = MIN_SCALE_FACTOR;
        } else if (iTmp > MAX_SCALE_FACTOR) {
                pDiag->iScaleFactorTemp = MAX_SCALE_FACTOR;
        } else {
                pDiag->iScaleFactorTemp = iTmp;
        }
        pDiag->iScaleFactorCurr = pDiag->iScaleFactorTemp;
        /* Close the scale window */
        Error_CheckFatal(Wimp_CloseWindow(pCaret->window));
        /* Redraw the main window */
        vSetTitle(pDiag);
        vForceRedraw(pDiag);
        return TRUE;
} /* end of bScaleKeyPressed */