From b623f5953691b2a0614e6f1f4def86bdbb9a4113 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Sat, 8 Aug 2020 11:53:00 +0200 Subject: New upstream version 5.2.0Beta2.1 --- app/wlib/mswlib/CMakeLists.txt | 19 +- app/wlib/mswlib/backgnd.c | 220 +++++++++ app/wlib/mswlib/mswbitmap.c | 46 +- app/wlib/mswlib/mswbutt.c | 21 +- app/wlib/mswlib/mswchksm.c | 125 ----- app/wlib/mswlib/mswdraw.c | 833 +++++++++++++++++++++----------- app/wlib/mswlib/mswedit.c | 210 ++++---- app/wlib/mswlib/mswint.h | 23 +- app/wlib/mswlib/mswlist.c | 2 +- app/wlib/mswlib/mswmenu.c | 8 +- app/wlib/mswlib/mswmisc.c | 266 +++++++--- app/wlib/mswlib/mswmsg.c | 9 +- app/wlib/mswlib/mswpref.c | 1 + app/wlib/mswlib/mswprint.c | 46 +- app/wlib/mswlib/mswsplash.c | 8 +- app/wlib/mswlib/mswtext.c | 63 ++- app/wlib/mswlib/simple-gettext.c | 3 +- app/wlib/mswlib/unittest/CMakeLists.txt | 11 + app/wlib/mswlib/unittest/utf8test.c | 65 +++ app/wlib/mswlib/utf8conv.c | 210 ++++++++ 20 files changed, 1547 insertions(+), 642 deletions(-) create mode 100644 app/wlib/mswlib/backgnd.c delete mode 100644 app/wlib/mswlib/mswchksm.c create mode 100644 app/wlib/mswlib/unittest/CMakeLists.txt create mode 100644 app/wlib/mswlib/unittest/utf8test.c create mode 100644 app/wlib/mswlib/utf8conv.c (limited to 'app/wlib/mswlib') diff --git a/app/wlib/mswlib/CMakeLists.txt b/app/wlib/mswlib/CMakeLists.txt index 82d8371..07558f9 100644 --- a/app/wlib/mswlib/CMakeLists.txt +++ b/app/wlib/mswlib/CMakeLists.txt @@ -1,12 +1,13 @@ +find_package(FreeImage REQUIRED) + FILE(GLOB HEADERS *.h) SET(SOURCES -# checksum.c + backgnd.c getopt.c mswbox.c mswbutt.c mswbitmap.c - mswchksm.c mswchoic.c mswcolor.c mswdraw.c @@ -19,14 +20,15 @@ SET(SOURCES mswpref.c mswprint.c mswsplash.c - mswstatus.c + mswstatus.c mswtext.c gwin32.c simple-gettext.c + utf8conv.c ) +include_directories(${FREEIMAGE_INCLUDE_PATH}) INCLUDE_DIRECTORIES(${XTrkCAD_BINARY_DIR}) -# INCLUDE_DIRECTORIES(${XTRKCAD_BINARY_DIR}) IF(XTRKCAD_USE_GETTEXT) IF(WIN32) @@ -37,4 +39,13 @@ ENDIF(XTRKCAD_USE_GETTEXT) ADD_LIBRARY(xtrkcad-wlib ${HEADERS} ${SOURCES}) TARGET_LINK_LIBRARIES(xtrkcad-wlib Htmlhelp msimg32 shlwapi) +target_link_libraries(xtrkcad-wlib ${FREEIMAGE_LIBRARY}) + +install(FILES + ${FREEIMAGE_SHAREDLIB} + DESTINATION ${XTRKCAD_BIN_INSTALL_DIR} + ) +if(XTRKCAD_TESTING AND CMOCKA_FOUND) + add_subdirectory( unittest ) +endif() diff --git a/app/wlib/mswlib/backgnd.c b/app/wlib/mswlib/backgnd.c new file mode 100644 index 0000000..d35f19a --- /dev/null +++ b/app/wlib/mswlib/backgnd.c @@ -0,0 +1,220 @@ +/** \file backgnd.c +* Layout background image +*/ + +/* XTrkCad - Model Railroad CAD +* Copyright (C) 2018 Martin Fischer +* +* 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. +*/ + +#include + +#include +#include "i18n.h" +#include "mswint.h" + +static char *lastErrorMessage; /**< store last message from FreeImage */ +#define ERRORPUNCTUATION " : " + +/** + * FreeImage error handler + * \param fif Format / Plugin responsible for the error + * \param message Error message + */ + +static void +HandleFreeImageError(FREE_IMAGE_FORMAT fif, const char *message) +{ + unsigned totalLength = strlen(message) + 1; + + if (fif != FIF_UNKNOWN) { + totalLength += strlen(FreeImage_GetFormatFromFIF(fif)) + strlen(ERRORPUNCTUATION); + } + + lastErrorMessage = malloc(totalLength); + + if (fif != FIF_UNKNOWN) { + sprintf(lastErrorMessage, + "%s" ERRORPUNCTUATION "%s", + FreeImage_GetFormatFromFIF(fif), + message); + } else { + strcpy(lastErrorMessage, message); + } +} + +/** +* Load the background image +* \param bd drawing context +* \param path filename for image file, if NULL the existing background will be removed +* \param error returned error message +* \return -1 unsupported or invalid file, 0 success, 1 background removed +*/ + +int +wDrawSetBackground(wDraw_p bd, char * path, char ** error) +{ + FREE_IMAGE_FORMAT fif = FIF_UNKNOWN; + + FreeImage_SetOutputMessage(HandleFreeImageError); + + if (lastErrorMessage) { + free(lastErrorMessage); + lastErrorMessage = NULL; + } + + if (path) { + // check the file signature and deduce its format + // (the second argument is currently not used by FreeImage) + fif = FreeImage_GetFileType(path, 0); + + if (fif == FIF_UNKNOWN) { + // no signature ? + // try to guess the file format from the file extension + fif = FreeImage_GetFIFFromFilename(path); + } + + // check that the plugin has reading capabilities ... + if ((fif != FIF_UNKNOWN) && FreeImage_FIFSupportsReading(fif)) { + // ok, let's load the file + bd->background = FreeImage_Load(fif, path, 0); + + // unless a bad file format, we are done ! + if (!bd->background) { + *error = lastErrorMessage; + return (-1); + } else { + return (0); + } + } else { + *error = strdup(_("Image file is invalid or cannot be read.")); + return (-1); + } + } else { + if (bd->background) { + FreeImage_Unload(bd->background); + bd->background = 0; + } + + return (1); + } +} + +/** +* Draw background to screen. The background will be sized and rotated before being shown. The bitmap +* is scaled so that the width is equal to size. The height is changed proportionally. +* +* \param bd drawing context +* \param pos_x, pos_y bitmap position +* \param size desired width after scaling +* \param angle +* \param screen visibility of bitmap in percent +*/ + +void +wDrawShowBackground(wDraw_p bd, wPos_t pos_x, wPos_t pos_y, wPos_t size, + wAngle_t angle, int screen) +{ + if (bd->background) { + double scale; + FIBITMAP *tmp; + FIBITMAP *rotated; + + if (size == 0) { + scale = 1.0; + } else { + scale = (double)size / FreeImage_GetWidth(bd->background); + } + + tmp = FreeImage_RescaleRect(bd->background, + (int)((double)FreeImage_GetWidth(bd->background) * scale), + (int)((double)FreeImage_GetHeight(bd->background) * scale), + 0, + 0, + FreeImage_GetWidth(bd->background), + FreeImage_GetHeight(bd->background), + FILTER_BILINEAR, + 0); + FreeImage_AdjustColors(tmp, screen, -screen, 1.0, FALSE); + FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(tmp); + + switch (image_type) { + case FIT_BITMAP: + switch (FreeImage_GetBPP(tmp)) { + case 8: { + BYTE color = 255; + rotated = FreeImage_Rotate(tmp, angle, &color); + } + break; + + case 24: // we could also use 'RGBTRIPLE color' here + case 32: { + RGBQUAD color = { 255, 255, 255, 0 }; + // for 24-bit images, the first 3 bytes will be read + // for 32-bit images, the first 4 bytes will be read + rotated = FreeImage_Rotate(tmp, angle, &color); + } + break; + } + + break; + + case FIT_UINT16: { + WORD color = 255; + rotated = FreeImage_Rotate(tmp, angle, &color); + } + break; + + case FIT_RGB16: // we could also use 'FIRGB16 color' here + case FIT_RGBA16: { + FIRGBA16 color = { 255, 255, 255, 0 }; + // for RGB16 images, the first 3 WORD will be read + // for RGBA16 images, the first 4 WORD will be read + rotated = FreeImage_Rotate(tmp, angle, &color); + } + break; + + case FIT_FLOAT: { + float color = 1.0F; + rotated = FreeImage_Rotate(tmp, angle, &color); + } + break; + + case FIT_RGBF: // we could also use 'FIRGBF color' here + case FIT_RGBAF: { + FIRGBAF color = { 1, 1, 1, 0 }; + // for RGBF images, the first 3 float will be read + // for RGBAF images, the first 4 float will be read + rotated = FreeImage_Rotate(tmp, angle, &color); + } + break; + } + + SetDIBitsToDevice(bd->hDc, + pos_x, + bd->h - pos_y - FreeImage_GetHeight(rotated), + FreeImage_GetWidth(rotated), + FreeImage_GetHeight(rotated), + 0, 0, + 0, + FreeImage_GetHeight(rotated), + FreeImage_GetBits(rotated), + FreeImage_GetInfo(rotated), + DIB_RGB_COLORS); + FreeImage_Unload(tmp); + FreeImage_Unload(rotated); + } +} \ No newline at end of file diff --git a/app/wlib/mswlib/mswbitmap.c b/app/wlib/mswlib/mswbitmap.c index e369e78..95b8a69 100644 --- a/app/wlib/mswlib/mswbitmap.c +++ b/app/wlib/mswlib/mswbitmap.c @@ -24,10 +24,12 @@ #include #include #include +#include #include #include #include #include +#include "misc.h" #include "mswint.h" #include "i18n.h" @@ -177,14 +179,15 @@ void mswDrawIcon( memset( bmiInfo->bmiColors, 0, bm->colorcnt * sizeof( RGBQUAD )); memset( &bmiInfo->bmiColors[ bm->transparent ], 0xFF, sizeof( RGBQUAD )); } + StretchDIBits(hDc, offw, offh, - bmiInfo->bmiHeader.biWidth, - bmiInfo->bmiHeader.biHeight, - 0, 0, - bmiInfo->bmiHeader.biWidth, - bmiInfo->bmiHeader.biHeight, - bm->pixels, bmiInfo, - DIB_RGB_COLORS, SRCAND); + (int)ceil(bmiInfo->bmiHeader.biWidth*scaleIcon), + (int)ceil(bmiInfo->bmiHeader.biHeight*scaleIcon), + 0, 0, + bmiInfo->bmiHeader.biWidth, + bmiInfo->bmiHeader.biHeight, + bm->pixels, bmiInfo, + DIB_RGB_COLORS, SRCAND); /* now paint the bitmap with transparent set to black */ if( bm->type == mswIcon_bitmap ) { @@ -221,16 +224,16 @@ void mswDrawIcon( } memset( &bmiInfo->bmiColors[ bm->transparent ], 0, sizeof( RGBQUAD )); } - + /* show the bitmap */ StretchDIBits(hDc, offw, offh, - bmiInfo->bmiHeader.biWidth, - bmiInfo->bmiHeader.biHeight, - 0, 0, - bmiInfo->bmiHeader.biWidth, - bmiInfo->bmiHeader.biHeight, - bm->pixels, bmiInfo, - DIB_RGB_COLORS, SRCPAINT); + (int)ceil(bmiInfo->bmiHeader.biWidth*scaleIcon), + (int)ceil(bmiInfo->bmiHeader.biHeight*scaleIcon), + 0, 0, + bmiInfo->bmiHeader.biWidth, + bmiInfo->bmiHeader.biHeight, + bm->pixels, bmiInfo, + DIB_RGB_COLORS, SRCPAINT); /* forget the data */ free( bmiInfo ); @@ -434,11 +437,14 @@ wIcon_p wIconCreatePixMap( char *pm[]) /* look up pixel info in color table */ k = 0; - while( pixel != keys[ k ] ) + while(k < col && pixel != keys[ k ] ) k++; - - /* save the index into color table */ - *(cq + j) = k; + if (pixel == keys[k]) { + /* save the index into color table */ + *(cq + j) = k; + } else { + *(cq + j) = 0; + } } } free( keys ); @@ -507,4 +513,4 @@ wBitmapCreate( wWin_p parent, wPos_t x, wPos_t y, long option, wIcon_p iconP ) control->data = iconP; return (wControl_p)control; -} \ No newline at end of file +} diff --git a/app/wlib/mswlib/mswbutt.c b/app/wlib/mswlib/mswbutt.c index d213695..16f31c1 100644 --- a/app/wlib/mswlib/mswbutt.c +++ b/app/wlib/mswlib/mswbutt.c @@ -37,11 +37,7 @@ int kludge12 = 0; ***************************************************************************** */ - - static XWNDPROC oldButtProc = NULL; -static XWNDPROC newButtProc; - struct wButton_t { WOBJ_COMMON @@ -88,9 +84,9 @@ static void drawButton( COLORREF colF; #define LEFT (0) -#define RIGHT (bm->w+10) +#define RIGHT (LONG)ceil(bm->w*scaleIcon+10) #define TOP (0) -#define BOTTOM (bm->h+10) +#define BOTTOM (LONG)ceil(bm->h*scaleIcon+10) /* get the lightest and the darkest color to use */ colL = GetSysColor( COLOR_BTNHIGHLIGHT ); @@ -239,6 +235,7 @@ static LRESULT buttPush( wControl_p b, HWND hWnd, UINT message, WPARAM wParam, L DRAWITEMSTRUCT * di = (DRAWITEMSTRUCT *)lParam; wBool_t selected; + switch (message) { case WM_COMMAND: if (bb->action /*&& !bb->busy*/) { @@ -253,8 +250,8 @@ static LRESULT buttPush( wControl_p b, HWND hWnd, UINT message, WPARAM wParam, L break; mi->CtlType = ODT_BUTTON; mi->CtlID = wParam; - mi->itemWidth = bb->w; - mi->itemHeight = bb->h; + mi->itemWidth = (UINT)ceil(bb->w*scaleIcon); + mi->itemHeight = (UINT)ceil(bb->h*scaleIcon); } return 0L; case WM_DRAWITEM: @@ -369,8 +366,8 @@ wButton_p wButtonCreate( b->selected = 0; mswComputePos( (wControl_p)b, x, y ); if (b->option&BO_ICON) { - width = bm->w+10; - h = bm->h+10; + width = (wPos_t)ceil(bm->w*scaleIcon)+10; + h = (int)ceil(bm->h*scaleIcon)+10; b->icon = bm; } else { width = (wPos_t)(width*mswScale); @@ -405,5 +402,9 @@ wButton_p wButtonCreate( } if ( !mswThickFont ) SendMessage( b->hWnd, WM_SETFONT, (WPARAM)mswLabelFont, 0L ); + + + InvalidateRect(b->hWnd, &rect, TRUE); + return b; } diff --git a/app/wlib/mswlib/mswchksm.c b/app/wlib/mswlib/mswchksm.c deleted file mode 100644 index 602c204..0000000 --- a/app/wlib/mswlib/mswchksm.c +++ /dev/null @@ -1,125 +0,0 @@ -#include -#include -#include "../include/wlib.h" -#ifdef WINDOWS -#include -#include "mswint.h" -#endif - -#define HEWHDROFFSET (0x3C) - -static FILE * openfile( const char * fn, const char * mode, long * fileSize ) -{ - unsigned short PageCnt; - long FileSize; - FILE *fp; - struct stat Stat; - fp = fopen( fn, mode ); - if (fp == NULL) { - perror( "fopen" ); - return NULL; - } - fread( &PageCnt, sizeof(PageCnt), 1, fp ); /* Read past signature */ - fread( &PageCnt, sizeof(PageCnt), 1, fp ); /* Read past pagesize */ - FileSize = PageCnt; - fread( &PageCnt, sizeof(PageCnt), 1, fp ); /* Read past pagesize */ - if ( FileSize == 0L ) - FileSize = PageCnt * 512L; - else - FileSize += (PageCnt - 1) * 512L; - *fileSize = FileSize; - stat( fn, &Stat ); - *fileSize = (long)Stat.st_size; - fprintf( stderr, "size1 = %ld, size2 = %ld\n", FileSize, (long)Stat.st_size ); - return fp; -} - - -static unsigned short mswCheck16( FILE * fp, long FileSize, unsigned short * sum16stored ) -{ - unsigned short int sum16, NxtInt; - long x; - unsigned char NxtChar; - sum16 = 0; - fseek(fp, 0, SEEK_SET); - - for (x=0L; x #include #include @@ -16,8 +34,12 @@ #else #define wFont_t tagLOGFONT #endif + +#include "misc.h" #include "mswint.h" +#include +wBool_t wDrawDoTempDraw = TRUE; /* ***************************************************************************** * @@ -39,6 +61,8 @@ static long clrOp = 0xbb0226; #define CENTERMARK_LENGTH 6 +bool bDrawMainBM = 0; + #ifdef SLOW static wPos_t XPIX2INCH( wDraw_p d, int ix ) { @@ -119,6 +143,31 @@ void wDrawDelayUpdate( { } +wBool_t wDrawSetTempMode( + wDraw_p bd, + wBool_t bTemp ) +{ + wBool_t rc = bd->bTempMode; + bd->bTempMode = bTemp; + if (rc == FALSE && bTemp == TRUE) { + // Main to Temp drawing + // Copy mainBM to tempBM + wDrawClearTemp( bd ); + if (bDrawMainBM) return rc; + HDC hDcOld = CreateCompatibleDC(bd->hDc); + HBITMAP hBmOld = SelectObject(hDcOld, bd->hBmMain); + SelectObject(bd->hDc, bd->hBmTemp); + BitBlt(bd->hDc, 0, 0, + bd->w, bd->h, + hDcOld, 0, 0, + SRCCOPY); + SelectObject(hDcOld, hBmOld); + DeleteDC(hDcOld); + bd->bCopiedMain = TRUE; + } + return rc; +} + /** * Sets the proper pen and composition for the next drawing operation * @@ -130,68 +179,83 @@ void wDrawDelayUpdate( * \param dc IN color * \param dopt IN ???? */ - static void setDrawMode( - HDC hDc, wDraw_p d, wDrawWidth dw, wDrawLineType_e lt, wDrawColor dc, wDrawOpts dopt ) { - int mode; + long centerPen[] = {40,10,20,10}; + long phantomPen[] = {40,10,20,10,20,10}; + HPEN hOldPen; static wDraw_p d0; static wDrawWidth dw0 = -1; static wDrawLineType_e lt0 = (wDrawLineType_e)-1; static wDrawColor dc0 = -1; - static int mode0 = -1; static LOGBRUSH logBrush = { 0, 0, 0 }; DWORD penStyle; + if ( wDrawDoTempDraw && (dopt & wDrawOptTemp) ) + SelectObject(d->hDc, d->hBmTemp); + else + SelectObject(d->hDc, d->hBmMain); + if ( d->hasPalette ) { int winPaletteClock = mswGetPaletteClock(); if ( d->paletteClock < winPaletteClock ) { - RealizePalette( hDc ); + RealizePalette( d->hDc ); d->paletteClock = winPaletteClock; } } - if (dopt & wDrawOptTemp) { - mode = R2_NOTXORPEN; - } else { - mode = R2_COPYPEN; - } - SetROP2( hDc, mode ); - if ( d == d0 && mode == mode0 && dw0 == dw && lt == lt0 && dc == dc0 ) + SetROP2( d->hDc, R2_COPYPEN ); + if ( d == d0 && dw0 == dw && lt == lt0 && dc == dc0 ) return; // make sure that the line width is at least 1! if( !dw ) dw++; - d0 = d; mode0 = mode; dw0 = dw; lt0 = lt; dc0 = dc; + d0 = d; dw0 = dw; lt0 = lt; dc0 = dc; + + void * penarray = NULL; + int penarray_size = 0; logBrush.lbColor = mswGetColor(d->hasPalette,dc); if ( lt==wDrawLineSolid ) { penStyle = PS_GEOMETRIC | PS_SOLID; if ( noFlatEndCaps == FALSE ) penStyle |= PS_ENDCAP_FLAT; - d->hPen = ExtCreatePen( penStyle, - dw, - &logBrush, - 0, - NULL ); - /*colorPalette.palPalEntry[dc] );*/ - } else { - d->hPen = CreatePen( PS_DOT, 0, mswGetColor( d->hasPalette, dc ) ); - } - hOldPen = SelectObject( hDc, d->hPen ); + } else if (lt == wDrawLineDot) { + penStyle = PS_GEOMETRIC | PS_DOT; + } else if (lt == wDrawLineDash) { + penStyle = PS_GEOMETRIC | PS_DASH; + } else if (lt == wDrawLineDashDot) { + penStyle = PS_GEOMETRIC | PS_DASHDOT; + } else if ( lt == wDrawLineDashDotDot){ + penStyle = PS_GEOMETRIC | PS_DASHDOTDOT; + } else if ( lt == wDrawLineCenter) { + penStyle = PS_GEOMETRIC | PS_USERSTYLE; + penarray = ¢erPen; + penarray_size = sizeof(centerPen)/sizeof(long); + } else if ( lt == wDrawLinePhantom) { + penStyle = PS_GEOMETRIC | PS_USERSTYLE; + penarray = &phantomPen; + penarray_size = sizeof(phantomPen) / sizeof(long); + } else + penStyle = PS_GEOMETRIC | PS_SOLID; + d->hPen = ExtCreatePen( penStyle, + dw, + &logBrush, + penarray_size, + penarray ); + hOldPen = SelectObject( d->hDc, d->hPen ); DeleteObject( hOldPen ); } static void setDrawBrush( - HDC hDc, wDraw_p d, wDrawColor dc, wDrawOpts dopt ) @@ -200,7 +264,7 @@ static void setDrawBrush( static wDraw_p d0; static wDrawColor dc0 = -1; - setDrawMode( hDc, d, 0, wDrawLineSolid, dc, dopt ); + setDrawMode( d, 0, wDrawLineSolid, dc, dopt ); if ( d == d0 && dc == dc0 ) return; @@ -208,7 +272,7 @@ static void setDrawBrush( d->hBrush = CreateSolidBrush( mswGetColor(d->hasPalette,dc) ); - hOldBrush = SelectObject( hDc, d->hBrush ); + hOldBrush = SelectObject( d->hDc, d->hBrush ); DeleteObject( hOldBrush ); } @@ -270,7 +334,7 @@ void wDrawLine( { POINT p0, p1; RECT rect; - setDrawMode( d->hDc, d, dw, lt, dc, dopt ); + setDrawMode( d, dw, lt, dc, dopt ); p0.x = XINCH2PIX(d,p0x); p0.y = YINCH2PIX(d,p0y); p1.x = XINCH2PIX(d,p1x); @@ -381,7 +445,7 @@ void wDrawArc( pe.x = XINCH2PIX(d,(wPos_t)pex); pe.y = YINCH2PIX(d,(wPos_t)pey); - setDrawMode( d->hDc, d, dw, lt, dc, dopt ); + setDrawMode( d, dw, lt, dc, dopt ); if (dw == 0) dw = 1; @@ -495,7 +559,7 @@ void wDrawPoint( return; if ( p0.x >= d->w || p0.y >= d->h ) return; - setDrawMode( d->hDc, d, 0, wDrawLineSolid, dc, dopt ); + setDrawMode( d, 0, wDrawLineSolid, dc, dopt ); SetPixel( d->hDc, p0.x, p0.y, mswGetColor(d->hasPalette,dc) /*colorPalette.palPalEntry[dc]*/ ); if (d->hWnd) { @@ -689,6 +753,7 @@ void wDrawGetTextSize( wPos_t *w, wPos_t *h, wPos_t *d, + wPos_t *a, wDraw_p bd, const char * text, wFont_p fp, @@ -717,12 +782,25 @@ void wDrawGetTextSize( *w = XPIXELSTOINCH( bd, x ); *h = YPIXELSTOINCH( bd, y ); *d = YPIXELSTOINCH(bd, textMetric.tmDescent ); + *a = YPIXELSTOINCH(bd, textMetric.tmAscent ); SelectObject( bd->hDc, prevFont ); DeleteObject( newFont ); fp->lfHeight = oldLfHeight; } - +/** + * Draw text + * + * \param d device context + * \param px position x + * \param py position y + * \param angle drawing angle + * \param text text to print + * \param fp font + * \param siz font size + * \param dc color + * \param dopts drawing options + */ void wDrawString( wDraw_p d, wPos_t px, @@ -736,8 +814,6 @@ void wDrawString( { int x, y; HFONT newFont, prevFont; - HDC newDc; - HBITMAP oldBm, newBm; DWORD extent; int w, h; RECT rect; @@ -756,61 +832,47 @@ void wDrawString( y = YINCH2PIX(d,py) + (int)(mswcos(angle)*fp->lfHeight-0.5); if (noNegDrawArgs > 0 && (x < 0 || y < 0)) { + DeleteObject(newFont); return; } - if (dopts & wDrawOptTemp) { - setDrawMode(d->hDc, d, 0, wDrawLineSolid, dc, dopts); - newDc = CreateCompatibleDC(d->hDc); - prevFont = SelectObject(newDc, newFont); - extent = GetTextExtent(newDc, CAST_AWAY_CONST text, strlen(text)); - w = LOWORD(extent); - h = HIWORD(extent); + setDrawMode( d, 0, wDrawLineSolid, dc, dopts ); + prevFont = SelectObject(d->hDc, newFont); + SetBkMode(d->hDc, TRANSPARENT); - if (h > w) { - w = h; + if (dopts & wDrawOutlineFont) { + HPEN oldPen; + BeginPath(d->hDc); + TextOut(d->hDc, x, y, text, strlen(text)); + EndPath(d->hDc); + + // Now draw outline text + oldPen = SelectObject(d->hDc, + CreatePen(PS_SOLID, 1, + mswGetColor(d->hasPalette, dc))); + StrokePath(d->hDc); + SelectObject(d->hDc, oldPen); + } else { + COLORREF old; + + old = SetTextColor(d->hDc, mswGetColor(d->hasPalette, + dc)); + TextOut(d->hDc, x, y, text, strlen(text)); + SetTextColor(d->hDc, old); } - newBm = CreateCompatibleBitmap(d->hDc, w*2, w*2); - oldBm = SelectObject(newDc, newBm); - rect.top = rect.left = 0; - rect.bottom = rect.right = w*2; - FillRect(newDc, &rect, GetStockObject(WHITE_BRUSH)); - TextOut(newDc, w, w, text, strlen(text)); - BitBlt(d->hDc, x-w, y-w, w*2, w*2, newDc, 0, 0, tmpOp); - SelectObject(newDc, oldBm); - DeleteObject(newBm); - SelectObject(newDc, prevFont); - DeleteDC(newDc); - - if (d->hWnd) { - rect.top = y-(w+1); - rect.bottom = y+(w+1); - rect.left = x-(w+1); - rect.right = x+(w+1); - myInvalidateRect(d, &rect); - } - } else { - COLORREF old; - prevFont = SelectObject(d->hDc, newFont); - SetBkMode(d->hDc, TRANSPARENT); - old = SetTextColor(d->hDc, mswGetColor(d->hasPalette, - dc)); - TextOut(d->hDc, x, y, text, strlen(text)); - SetTextColor(d->hDc, old); extent = GetTextExtent(d->hDc, CAST_AWAY_CONST text, strlen(text)); SelectObject(d->hDc, prevFont); w = LOWORD(extent); h = HIWORD(extent); if (d->hWnd) { - rect.top = y-(w+h+1); - rect.bottom = y+(w+h+1); - rect.left = x-(w+h+1); - rect.right = x+(w+h+1); + rect.top = y - (w + h + 1); + rect.bottom = y + (w + h + 1); + rect.left = x - (w + h + 1); + rect.right = x + (w + h + 1); myInvalidateRect(d, &rect); } - } DeleteObject(newFont); fp->lfHeight = oldLfHeight; @@ -846,9 +908,9 @@ wFontSize_t wSelectedFontSize( void ) return fontSize; } -void wSetSelectedFontSize(int size) +void wSetSelectedFontSize(wFontSize_t size) { - fontSize = (wFontSize_t)size; + fontSize = size; } /* @@ -870,10 +932,18 @@ void wDrawFilledRectangle( wDrawColor color, wDrawOpts opts ) { + int mode; RECT rect; if (d == NULL) return; - setDrawBrush( d->hDc, d, color, opts ); + setDrawBrush( d, color, opts ); + if (opts & wDrawOptTransparent) { + mode = R2_NOTXORPEN; + } + else { + mode = R2_COPYPEN; + } + SetROP2(d->hDc, mode); rect.left = XINCH2PIX(d,px); rect.right = XINCH2PIX(d,px+sx); rect.top = YINCH2PIX(d,py+sy); @@ -903,103 +973,230 @@ void wDrawFilledRectangle( } #ifdef DRAWFILLPOLYLOG -static FILE * logF; + static FILE * logF; #endif -static int wFillPointsMax = 0; -static POINT * wFillPoints; + +static dynArr_t wFillPoints_da; +static dynArr_t wFillType_da; + +#define POINTTYPE(N) DYNARR_N( BYTE, wFillType_da, (N) ) +#define POINTPOS(N) DYNARR_N( POINT, wFillPoints_da, (N) ) + +/** + * Add a point definition to the list. The clipping rectangle is recalculated to + * include the new point. + * + * \param d IN drawing context + * \param pk IN index of new point + * \param pp IN pointer to the point's coordinates + * \param type IN line type + * \param pr IN/OUT clipping rectangle + */ static void addPoint( - int * pk, - POINT * pp, - RECT * pr ) + wDraw_p d, + int pk, + coOrd * pp, + BYTE type, RECT * pr) { + POINT p; + p.x = XINCH2PIX(d, pp->x); + p.y = YINCH2PIX(d, pp->y); + #ifdef DRAWFILLPOLYLOG -fprintf( logF, " q[%d] = {%d,%d}\n", *pk, pp->x, pp->y ); + fprintf(logF, " q[%d] = {%d,%d}\n", pk, p.x, p.y); #endif - if ( *pk > 0 && - wFillPoints[(*pk)-1].x == pp->x && wFillPoints[(*pk)-1].y == pp->y ) - return; - wFillPoints[ (*pk)++ ] = *pp; - if (pp->xleft) - pr->left = pp->x; - if (pp->x>pr->right) - pr->right = pp->x; - if (pp->ytop) - pr->top = pp->y; - if (pp->y>pr->bottom) - pr->bottom = pp->y; + + DYNARR_N(POINT, wFillPoints_da, pk) = p; + DYNARR_N(BYTE, wFillType_da, pk) = type; + + if (p.x < pr->left) { + pr->left = p.x; + } + if (p.x > pr->right) { + pr->right = p.x; + } + if (p.y < pr->top) { + pr->top = p.y; + } + if (p.y > pr->bottom) { + pr->bottom = p.y; + } } -void wDrawFilledPolygon( - wDraw_p d, - wPos_t p[][2], - int cnt, - wDrawColor color, - wDrawOpts opts ) -{ - RECT rect; - int i, k; - POINT p0, p1, q0, q1; - static POINT zero = { 0, 0 }; - wBool_t p1Clipped; +/** + * Draw a polyline consisting of straights with smoothed or rounded corners. + * Optionally the area can be filled. + * + * \param d IN drawing context + * \param node IN 2 dimensional array of coordinates + * \param type IN type of corener (vertex, smooth or round) + * \param cnt IN number of points + * \param color IN color + * \param dw IN line width + * \param lt IN line type + * \param opts IN drawing options + * \param fill IN area will be filled if true + * \param open IN do not close area + */ - if (d == NULL) - return; - if (cnt*2 > wFillPointsMax) { - wFillPoints = realloc( wFillPoints, cnt * 2 * sizeof *(POINT*)NULL ); - if (wFillPoints == NULL) { - fputs("can't realloc wFillPoints\n", stderr); - abort(); +void wDrawPolygon( + wDraw_p d, + wPos_t node[][2], + wPolyLine_e type[], + wIndex_t cnt, + wDrawColor color, + wDrawWidth dw, + wDrawLineType_e lt, + wDrawOpts opts, + int fill, + int open) +{ + RECT rect; + int i, prevNode, nextNode; + int pointCount = 0; + coOrd endPoint0, endPoint1, controlPoint0, controlPoint1; + coOrd point, startingPoint; + BOOL rc; + int closed = 0; + + if (d == NULL) { + return; + } + + // make sure the array for the points is large enough + // worst case are rounded corners that require 4 points + DYNARR_RESET(POINT,wFillPoints_da); + DYNARR_SET(POINT,wFillPoints_da,(cnt + 1) * 4); + DYNARR_RESET(BYTE,wFillType_da); + DYNARR_SET(POINT,wFillType_da, (cnt + 1) * 4); + + BeginPath(d->hDc); + + if (fill) { + int mode; + setDrawBrush(d, color, opts); + if (opts & wDrawOptTransparent) { + mode = R2_NOTXORPEN; } - wFillPointsMax = cnt*2; - } - setDrawBrush( d->hDc, d, color, opts ); - p1.x = rect.left = rect.right = XINCH2PIX(d,p[cnt-1][0]-1); - p1.y = rect.top = rect.bottom = YINCH2PIX(d,p[cnt-1][1]+1); -#ifdef DRAWFILLPOLYLOG -logF = fopen( "log.txt", "a" ); -fprintf( logF, "\np[%d] = {%d,%d}\n", cnt-1, p1.x, p1.y ); -#endif - p1Clipped = FALSE; - for ( i=k=0; i 0 && ( q0.x > q0.y ) != ( wFillPoints[k-1].x > wFillPoints[k-1].y ) ) - addPoint( &k, &zero, &rect ); - addPoint( &k, &q0, &rect ); - } - addPoint( &k, &q1, &rect ); - p1Clipped = ( q1.x != p1.x || q1.y != p1.y ); + else { + mode = R2_COPYPEN; } - } - if ( p1Clipped && - ( wFillPoints[k-1].x > wFillPoints[k-1].y ) != ( wFillPoints[0].x > wFillPoints[0].y ) ) - addPoint( &k, &zero, &rect ); + SetROP2(d->hDc, mode); + + } else { + setDrawMode(d, dw, lt, color, opts); + } + + rect.left = rect.right = XINCH2PIX(d,node[cnt-1][0]-1); + rect.top = rect.bottom = YINCH2PIX(d,node[cnt-1][1]+1); + #ifdef DRAWFILLPOLYLOG -fflush( logF ); -fclose( logF ); + logF = fopen("log.txt", "a"); + fprintf(logF, "\np[%d] = {%d,%d}\n", cnt-1, node[0][0], node[0][1]); #endif - if ( k <= 2 ) - return; - Polygon( d->hDc, wFillPoints, k ); - if (d->hWnd) { - rect.top--; - rect.left--; - rect.bottom++; - rect.right++; - myInvalidateRect( d, &rect ); - } + + for (i=0; i 0) && (distNext > 0)) { + double ratio = sqrt(distPrev / distNext); + if (distPrev < distNext) { + endPoint1.x = ((nextXDistance*ratio) / 2) + node[i][0]; + endPoint1.y = ((nextYDistance*ratio) / 2) + node[i][1]; + } else { + endPoint0.x = node[i][0] - (prevXDistance / (2 * ratio)); + endPoint0.y = node[i][1] - (prevYDistance / (2 * ratio)); + } + } + // experience says that the best look is achieved if the + // control points are in the middle between end point and node + controlPoint0.x = (node[i][0] - endPoint0.x) / 2 + endPoint0.x; + controlPoint0.y = (node[i][1] - endPoint0.y) / 2 + endPoint0.y; + + controlPoint1.x = (endPoint1.x - node[i][0]) / 2 + node[i][0]; + controlPoint1.y = (endPoint1.y - node[i][1]) / 2 + node[i][1]; + } else { + controlPoint0 = point; + controlPoint1 = point; + } + } + + if (i==0) { + if (type1 == wPolyLineStraight || open) { + // for straight lines or open shapes use the starting point as passed + addPoint(d, pointCount++, &point, PT_MOVETO, &rect); + startingPoint = point; + } else { + // for Bezier begin with the calculated starting point + addPoint(d, pointCount++, &endPoint0, PT_MOVETO, &rect); + addPoint(d, pointCount++, &controlPoint0, PT_BEZIERTO, &rect); + addPoint(d, pointCount++, &controlPoint1, PT_BEZIERTO, &rect); + addPoint(d, pointCount++, &endPoint1, PT_BEZIERTO, &rect); + startingPoint = endPoint0; + } + } else { + if (type1 == wPolyLineStraight || (open && (i==cnt-1))) { + addPoint(d, pointCount++, &point, PT_LINETO, &rect); + } else { + if (i==cnt-1 && !open) { + closed = TRUE; + } + addPoint(d, pointCount++, &endPoint0, PT_LINETO, &rect); + addPoint(d, pointCount++, &controlPoint0, PT_BEZIERTO, &rect); + addPoint(d, pointCount++, &controlPoint1, PT_BEZIERTO, &rect); + addPoint(d, pointCount++, &endPoint1, + PT_BEZIERTO | (closed ? PT_CLOSEFIGURE : 0), &rect); + } + } + } + + if (!open && !closed) { + addPoint(d, pointCount++, &startingPoint, PT_LINETO, &rect); + } + rc = PolyDraw(d->hDc, wFillPoints_da.ptr, wFillType_da.ptr, pointCount); + + EndPath(d->hDc); + + if (fill && !open) { + FillPath(d->hDc); + } else { + StrokePath(d->hDc); + } + + if (d->hWnd) { + rect.top--; + rect.left--; + rect.bottom++; + rect.right++; + myInvalidateRect(d, &rect); + } } #define MAX_FILLCIRCLE_POINTS (30) @@ -1022,7 +1219,7 @@ void wDrawFilledCircle( p1.x = XINCH2PIX(d,x+r); p1.y = YINCH2PIX(d,y-r)+1; - setDrawBrush( d->hDc, d, color, opts ); + setDrawBrush( d, color, opts ); if ( noNegDrawArgs > 0 && ( p0.x < 0 || p0.y < 0 ) ) { if ( r > MAX_FILLCIRCLE_POINTS ) cnt = MAX_FILLCIRCLE_POINTS; @@ -1035,7 +1232,8 @@ void wDrawFilledCircle( circlePts[inx][0] = x + (int)(r * mswcos( inx*dang ) + 0.5 ); circlePts[inx][1] = y + (int)(r * mswsin( inx*dang ) + 0.5 ); } - wDrawFilledPolygon( d, circlePts, cnt, color, opts ); + //wDrawFilledPolygon( d, circlePts, NULL, cnt, color, opts ); + wDrawPolygon(d, circlePts, NULL, cnt, color, 1, wDrawLineSolid,opts, TRUE, FALSE ); } else { Ellipse( d->hDc, p0.x, p0.y, p1.x, p1.y ); if (d->hWnd) { @@ -1084,21 +1282,30 @@ void wDrawRestoreImage( } -void wDrawClear( wDraw_p d ) +void wDrawClearTemp( wDraw_p d ) { RECT rect; - SetROP2( d->hDc, R2_WHITE ); - Rectangle( d->hDc, 0, 0, d->w, d->h ); + SelectObject( d->hDc, d->hBmTemp ); + BitBlt(d->hDc, 0, 0, d->w, d->h, d->hDc, 0, 0, WHITENESS); if (d->hWnd) { - rect.top = 0; - rect.bottom = d->h; - rect.left = 0; - rect.right = d->w; - InvalidateRect( d->hWnd, &rect, FALSE ); + rect.top = 0; + rect.bottom = d->h; + rect.left = 0; + rect.right = d->w; + InvalidateRect( d->hWnd, &rect, FALSE ); } } +void wDrawClear( wDraw_p d ) +{ + SelectObject( d->hDc, d->hBmMain ); + // BitBlt is faster than Rectangle + BitBlt(d->hDc, 0, 0, d->w, d->h, d->hDc, 0, 0, WHITENESS); + wDrawClearTemp(d); +} + + void wDrawSetSize( wDraw_p d, wPos_t width, @@ -1196,7 +1403,7 @@ void wDrawBitMap( wDrawColor dc, wDrawOpts dopt ) { - HDC bmDc, hDc; + HDC bmDc; HBITMAP oldBm; DWORD mode; int x0, y0; @@ -1208,9 +1415,7 @@ void wDrawBitMap( if ( noNegDrawArgs > 0 && ( x0 < 0 || y0 < 0 ) ) return; #endif - if (dopt & wDrawOptTemp) { - mode = tmpOp; - } else if (dc == wDrawColorWhite) { + if (dc == wDrawColorWhite) { mode = clrOp; dc = wDrawColorBlack; } else { @@ -1224,22 +1429,9 @@ void wDrawBitMap( RGB( 255, 255, 255 ), bm->w, bm->h, bm->bmx ); bm->color = dc; } - if ( (dopt & wDrawOptNoClip) != 0 && - ( px < 0 || px >= d->w || py < 0 || py >= d->h ) ) { - x0 += d->x; - y0 += d->y; - hDc = GetDC( ((wControl_p)(d->parent))->hWnd ); - bmDc = CreateCompatibleDC( hDc ); - oldBm = SelectObject( bmDc, bm->bm ); - BitBlt( hDc, x0, y0, bm->w, bm->h, bmDc, 0, 0, tmpOp ); - SelectObject( bmDc, oldBm ); - DeleteDC( bmDc ); - ReleaseDC( ((wControl_p)(d->parent))->hWnd, hDc ); - return; - } bmDc = CreateCompatibleDC( d->hDc ); - setDrawMode( d->hDc, d, 0, wDrawLineSolid, dc, dopt ); + setDrawMode( d, 0, wDrawLineSolid, dc, dopt ); oldBm = SelectObject( bmDc, bm->bm ); BitBlt( d->hDc, x0, y0, bm->w, bm->h, bmDc, 0, 0, mode ); SelectObject( bmDc, oldBm ); @@ -1260,7 +1452,7 @@ wDrawBitMap_p wDrawBitMapCreate( int h, int x, int y, - const char * bits ) + const unsigned char * bits ) { wDrawBitMap_p bm; int bmSize = ((w+7)/8) * h; @@ -1322,12 +1514,14 @@ long FAR PASCAL XEXPORT mswDrawPush( hDc = GetDC(hWnd); if ( b->option & BD_DIRECT ) { b->hDc = hDc; - b->hBm = 0; + b->hBmMain = 0; + b->hBmTemp = 0; b->hBmOld = 0; } else { b->hDc = CreateCompatibleDC( hDc ); - b->hBm = CreateCompatibleBitmap( hDc, b->w, b->h ); - b->hBmOld = SelectObject( b->hDc, b->hBm ); + b->hBmMain = CreateCompatibleBitmap( hDc, b->w, b->h ); + b->hBmTemp = CreateCompatibleBitmap( hDc, b->w, b->h ); + b->hBmOld = SelectObject( b->hDc, b->hBmMain ); } if (mswPalette) { SelectPalette( b->hDc, mswPalette, 0 ); @@ -1355,8 +1549,12 @@ long FAR PASCAL XEXPORT mswDrawPush( if ( b->option & BD_DIRECT ) { } else { hDc = GetDC( b->hWnd ); - b->hBm = CreateCompatibleBitmap( hDc, b->w, b->h ); - DeleteObject(SelectObject( b->hDc, b->hBm )); +//- DeleteObject( b->hBmOld ); + DeleteObject( b->hBmMain ); + DeleteObject( b->hBmTemp ); + b->hBmMain = CreateCompatibleBitmap( hDc, b->w, b->h ); + b->hBmTemp = CreateCompatibleBitmap( hDc, b->w, b->h ); +//- b->hBmOld = SelectObject( b->hDc, b->hBmMain ); ReleaseDC( b->hWnd, hDc ); SetROP2( b->hDc, R2_WHITE ); Rectangle( b->hDc, 0, 0, b->w, b->h ); @@ -1383,6 +1581,7 @@ long FAR PASCAL XEXPORT mswDrawPush( case WM_LBUTTONUP: case WM_RBUTTONDOWN: case WM_RBUTTONUP: + case WM_LBUTTONDBLCLK: if (message == WM_LBUTTONDOWN) action = wActionLDown; else if (message == WM_RBUTTONDOWN) @@ -1391,6 +1590,8 @@ long FAR PASCAL XEXPORT mswDrawPush( action = wActionLUp; else if (message == WM_RBUTTONUP) action = wActionRUp; + else if (message == WM_LBUTTONDBLCLK) + action = wActionLDownDouble; else { if ( (wParam & MK_LBUTTON) != 0) action = wActionLDrag; @@ -1414,6 +1615,8 @@ long FAR PASCAL XEXPORT mswDrawPush( iy = HIWORD( lParam ); x = XPIX2INCH( b, ix ); y = YPIX2INCH( b, iy ); + b->lastX = x; + b->lastY = y; if (b->action) b->action( b, b->data, action, x, y ); if (b->hWnd) @@ -1435,7 +1638,7 @@ long FAR PASCAL XEXPORT mswDrawPush( case VK_RIGHT: extChar = wAccelKey_Right; break; case VK_LEFT: extChar = wAccelKey_Left; break; case VK_BACK: extChar = wAccelKey_Back; break; - /*case VK_F1: extChar = wAccelKey_F1; break;*/ + case VK_F1: extChar = wAccelKey_F1; break; case VK_F2: extChar = wAccelKey_F2; break; case VK_F3: extChar = wAccelKey_F3; break; case VK_F4: extChar = wAccelKey_F4; break; @@ -1450,9 +1653,9 @@ long FAR PASCAL XEXPORT mswDrawPush( } if (b && b->action) { if (extChar != wAccelKey_None) - b->action( b, b->data, wActionExtKey + ( (int)extChar << 8 ), 0, 0 ); + b->action( b, b->data, wActionExtKey + ( (int)extChar << 8 ), b->lastX, b->lastY ); else - b->action( b, b->data, wActionText + ( wParam << 8 ), 0, 0 ); + b->action( b, b->data, wActionText + ( wParam << 8 ), b->lastX, b->lastY ); } return 0; @@ -1468,11 +1671,22 @@ long FAR PASCAL XEXPORT mswDrawPush( b->paletteClock = winPaletteClock; } } + HBITMAP hBmOld = SelectObject( b->hDc, b->hBmMain ); + + if (bDrawMainBM) { + BitBlt(hDc, rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top, + b->hDc, rect.left, rect.top, + SRCCOPY); + } + SelectObject( b->hDc, b->bCopiedMain?b->hBmTemp:b->hBmMain ); BitBlt( hDc, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, b->hDc, rect.left, rect.top, - SRCCOPY ); + bDrawMainBM?SRCAND:SRCCOPY); + SelectObject( b->hDc, hBmOld ); EndPaint( hWnd, &ps ); + b->bCopiedMain = FALSE; } } break; @@ -1499,18 +1713,44 @@ long FAR PASCAL XEXPORT mswDrawPush( static LRESULT drawMsgProc( wDraw_p b, HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) { wAction_t action; - + switch( message ) { case WM_MOUSEWHEEL: /* handle mouse wheel events */ - /* fwKeys = GET_KEYSTATE_WPARAM(wParam); modifier keys are currently ignored */ - if ( GET_WHEEL_DELTA_WPARAM(wParam) > 0 ) { - action = wActionWheelUp; + if (GET_KEYSTATE_WPARAM(wParam) & (MK_SHIFT|MK_MBUTTON) ) { + if (GET_KEYSTATE_WPARAM(wParam) & MK_CONTROL ) { + if (GET_WHEEL_DELTA_WPARAM(wParam) > 0) { + action = wActionScrollLeft; + } else { + action = wActionScrollRight; + } + } else { + if (GET_WHEEL_DELTA_WPARAM(wParam) > 0) { + action = wActionScrollUp; + } else { + action = wActionScrollDown; + } + } } else { - action = wActionWheelDown; + if (GET_WHEEL_DELTA_WPARAM(wParam) > 0) { + action = wActionWheelUp; + } else { + action = wActionWheelDown; + } + } + if (b->action) + b->action( b, b->data, action, b->lastX, b->lastY ); + return 0; + case WM_MOUSEHWHEEL: + if ( GET_KEYSTATE_WPARAM(wParam) & (MK_SHIFT|MK_MBUTTON)) { + if ( GET_WHEEL_DELTA_WPARAM(wParam) > 0 ) { + action = wActionScrollRight; + } else { + action = wActionScrollLeft; + } } if (b->action) - b->action( b, b->data, action, 0, 0 ); + b->action( b, b->data, action, b->lastX, b->lastY ); return 0; } @@ -1521,10 +1761,12 @@ static LRESULT drawMsgProc( wDraw_p b, HWND hWnd, UINT message, WPARAM wParam, L static void drawDoneProc( wControl_p b ) { wDraw_p d = (wDraw_p)b; - if (d->hBm) { + if (d->hBmMain) { SelectObject( d->hDc, d->hBmOld ); - DeleteObject( d->hBm ); - d->hBm = (HBITMAP)0; + DeleteObject( d->hBmMain ); + d->hBmMain = (HBITMAP)0; + DeleteObject( d->hBmTemp ); + d->hBmTemp = (HBITMAP)0; } if (d->hPen) { SelectObject( d->hDc, GetStockObject( BLACK_PEN ) ); @@ -1580,10 +1822,17 @@ void mswRepaintAll( void ) for ( b=drawList; b; b=b->drawNext ) { if (GetUpdateRect( b->hWnd, &rect, FALSE )) { hDc = BeginPaint( b->hWnd, &ps ); + HBITMAP hBmOld = SelectObject( b->hDc, b->hBmMain ); BitBlt( hDc, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, b->hDc, rect.left, rect.top, SRCCOPY ); + SelectObject( b->hDc, b->hBmTemp ); + BitBlt( hDc, rect.left, rect.top, + rect.right-rect.left, rect.bottom-rect.top, + b->hDc, rect.left, rect.top, + SRCAND ); + SelectObject( b->hDc, hBmOld ); EndPaint( b->hWnd, &ps ); } } @@ -1648,6 +1897,7 @@ wDraw_p wDrawCreate( SelectPalette( hDc, mswPalette, 0 ); ReleaseDC( d->hWnd, hDc ); } + d->bCopiedMain = FALSE; return d; } @@ -1681,14 +1931,19 @@ wDraw_p wBitMapCreate( wPos_t w, wPos_t h, int planes ) wNoticeEx( NT_ERROR, "CreateBitMap: CreateDC fails", "Ok", NULL ); return FALSE; } - d->hBm = CreateCompatibleBitmap( hDc, d->w, d->h ); - if ( d->hBm == (HBITMAP)0 ) { - wNoticeEx( NT_ERROR, "CreateBitMap: CreateBM fails", "Ok", NULL ); + d->hBmMain = CreateCompatibleBitmap( hDc, d->w, d->h ); + if ( d->hBmMain == (HBITMAP)0 ) { + wNoticeEx( NT_ERROR, "CreateBitMap: CreateBM Main fails", "Ok", NULL ); + return FALSE; + } + d->hBmTemp = CreateCompatibleBitmap( hDc, d->w, d->h ); + if ( d->hBmTemp == (HBITMAP)0 ) { + wNoticeEx( NT_ERROR, "CreateBitMap: CreateBM Temp fails", "Ok", NULL ); return FALSE; } d->hasPalette = (GetDeviceCaps(hDc,RASTERCAPS ) & RC_PALETTE) != 0; ReleaseDC( mswHWnd, hDc ); - d->hBmOld = SelectObject( d->hDc, d->hBm ); + d->hBmOld = SelectObject( d->hDc, d->hBmMain ); if (mswPalette) { SelectPalette( d->hDc, mswPalette, 0 ); RealizePalette( d->hDc ); @@ -1697,8 +1952,9 @@ wDraw_p wBitMapCreate( wPos_t w, wPos_t h, int planes ) d->hFactor = (double)GetDeviceCaps( d->hDc, LOGPIXELSY ); d->DPI = 96.0; /*min( d->wFactor, d->hFactor );*/ d->hWnd = 0; - SetROP2( d->hDc, R2_WHITE ); - Rectangle( d->hDc, 0, 0, d->w, d->h ); + wDrawClear(d); +//- SetROP2( d->hDc, R2_WHITE ); +//- Rectangle( d->hDc, 0, 0, d->w, d->h ); return d; } @@ -1709,10 +1965,12 @@ wBool_t wBitMapDelete( wDraw_p d ) DeleteObject( d->hPen ); d->hPen = (HPEN)0; } - if (d->hBm) { + if (d->hBmMain) { SelectObject( d->hDc, d->hBmOld ); - DeleteObject( d->hBm ); - d->hBm = (HBITMAP)0; + DeleteObject( d->hBmMain ); + d->hBmMain = (HBITMAP)0; + DeleteObject( d->hBmTemp ); + d->hBmTemp = (HBITMAP)0; } if (d->hDc) { DeleteDC( d->hDc ); @@ -1722,74 +1980,75 @@ wBool_t wBitMapDelete( wDraw_p d ) return TRUE; } -wBool_t wBitMapWriteFile( wDraw_p d, const char * fileName ) +/** + * write bitmap file. The bitmap in d must contain a valid HBITMAP + * + * \param d A wDraw_p to process. + * \param fileName Filename of the file. + * + * \returns A wBool_t. TRUE on success + */ + +wBool_t +wBitMapWriteFile(wDraw_p d, const char * fileName) { - char *pixels; - int j, ww, chunk; - FILE * f; - BITMAPFILEHEADER bmfh; - struct { - BITMAPINFOHEADER bmih; - RGBQUAD colors[256]; - } bmi; - int rc; - - if ( d->hBm == 0) - return FALSE; - f = wFileOpen( fileName, "wb" ); - if (!f) { - wNoticeEx( NT_ERROR, fileName, "Ok", NULL ); - return FALSE; - } - ww = ((d->w +3) / 4) * 4; - bmfh.bfType = 'B'+('M'<<8); - bmfh.bfSize = (long)(sizeof bmfh) + (long)(sizeof bmi.bmih) + (long)(sizeof bmi.colors) + (long)ww * (long)(d->h); - bmfh.bfReserved1 = 0; - bmfh.bfReserved2 = 0; - bmfh.bfOffBits = sizeof bmfh + sizeof bmi.bmih + sizeof bmi.colors; - fwrite( &bmfh, 1, sizeof bmfh, f ); - bmi.bmih.biSize = sizeof bmi.bmih; - bmi.bmih.biWidth = d->w; - bmi.bmih.biHeight = d->h; - bmi.bmih.biPlanes = 1; - bmi.bmih.biBitCount = 8; - bmi.bmih.biCompression = BI_RGB; - bmi.bmih.biSizeImage = 0; - bmi.bmih.biXPelsPerMeter = 75*(10000/254); - bmi.bmih.biYPelsPerMeter = 75*(10000/254); - bmi.bmih.biClrUsed = bmi.bmih.biClrImportant = mswGetColorList( bmi.colors ); - SelectObject( d->hDc, d->hBmOld ); - rc = GetDIBits( d->hDc, d->hBm, 0, 1, NULL, (BITMAPINFO*)&bmi, DIB_RGB_COLORS ); - if ( rc == 0 ) { - wNoticeEx( NT_ERROR, "WriteBitMap: Can't get bitmapinfo from Bitmap", "Ok", NULL ); - return FALSE; - } - bmi.bmih.biClrUsed = 256; - fwrite( &bmi.bmih, 1, sizeof bmi.bmih, f ); - fwrite( bmi.colors, 1, sizeof bmi.colors, f ); - chunk = 32000/ww; - pixels = (char*)malloc( ww*chunk ); - if ( pixels == NULL ) { - wNoticeEx( NT_ERROR, "WriteBitMap: no memory", "OK", NULL ); - return FALSE; - } - for (j=0;jh;j+=chunk) { - if (j+chunk>d->h) - chunk = d->h-j; - rc = GetDIBits( d->hDc, d->hBm, j, chunk, pixels, (BITMAPINFO*)&bmi, DIB_RGB_COLORS ); - if ( rc == 0 ) - if ( rc == 0 ) { - wNoticeEx( NT_ERROR, "WriteBitMap: Can't get bits from Bitmap", "Ok", NULL ); - return FALSE; - } - rc = fwrite( pixels, 1, ww*chunk, f ); - if (rc != ww*chunk) { - wNoticeEx( NT_ERROR, "WriteBitMap: Bad fwrite", "Ok", NULL); - } - } - free( pixels ); - SelectObject( d->hDc, d->hBm ); - fclose( f ); - return TRUE; + FIBITMAP *dib = NULL; + FIBITMAP *dib2 = NULL; + FREE_IMAGE_FORMAT fif = FIF_UNKNOWN; + BOOL bSuccess = FALSE; + + if (d->hBmMain) { + + BITMAP bm; + GetObject(d->hBmMain, sizeof(BITMAP), (LPSTR)&bm); + dib = FreeImage_Allocate(bm.bmWidth, bm.bmHeight, bm.bmBitsPixel, 0, 0, 0); + // The GetDIBits function clears the biClrUsed and biClrImportant BITMAPINFO members (dont't know why) + // So we save these infos below. This is needed for palettized images only. + int nColors = FreeImage_GetColorsUsed(dib); + HDC dc = GetDC(NULL); + GetDIBits(dc, + d->hBmMain, + 0, + FreeImage_GetHeight(dib), + FreeImage_GetBits(dib), + FreeImage_GetInfo(dib), + DIB_RGB_COLORS); + ReleaseDC(NULL, dc); + + // restore BITMAPINFO members + FreeImage_GetInfoHeader(dib)->biClrUsed = nColors; + FreeImage_GetInfoHeader(dib)->biClrImportant = nColors; + // we will get a 32 bit bitmap on Windows systems with invalid alpha + // so it needs to be converted to 24 bits. + // (see: https://sourceforge.net/p/freeimage/discussion/36110/thread/0699ce8e/ ) + dib2 = FreeImage_ConvertTo24Bits(dib); + FreeImage_Unload(dib); + } + + // Try to guess the file format from the file extension + fif = FreeImage_GetFIFFromFilename(fileName); + if (fif != FIF_UNKNOWN) { + // Check that the dib can be saved in this format + BOOL bCanSave; + + FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib2); + if (image_type == FIT_BITMAP) { + // standard bitmap type + WORD bpp = FreeImage_GetBPP(dib2); + bCanSave = (FreeImage_FIFSupportsWriting(fif) && + FreeImage_FIFSupportsExportBPP(fif, bpp)); + } else { + // special bitmap type + bCanSave = FreeImage_FIFSupportsExportType(fif, image_type); + } + + if (bCanSave) { + bSuccess = FreeImage_Save(fif, dib2, fileName, PNG_DEFAULT); + return bSuccess; + } + } + FreeImage_Unload(dib2); + + return bSuccess; } diff --git a/app/wlib/mswlib/mswedit.c b/app/wlib/mswlib/mswedit.c index fbae89f..dc70ac3 100644 --- a/app/wlib/mswlib/mswedit.c +++ b/app/wlib/mswlib/mswedit.c @@ -1,3 +1,25 @@ +/** \file mswedit.c + * Text entry widgets + */ + +/* XTrackCAD - Model Railroad CAD + * Copyright (C) 2005 Dave Bullis + * + * 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. + */ + #include #include #include @@ -15,6 +37,7 @@ struct wString_t { wStringCallBack_p action; }; +#ifdef LATER struct wInteger_t { WOBJ_COMMON long low, high; @@ -30,6 +53,7 @@ struct wFloat_t { double oldValue; wFloatCallBack_p action; }; +#endif // LATER static XWNDPROC oldEditProc = NULL; @@ -47,53 +71,35 @@ long FAR PASCAL _export pushEdit( UINT wParam, LONG lParam ) { - /* Catch and cause focus to leave control */ + #ifdef WIN32 long inx = GetWindowLong( hWnd, GWL_ID ); #else short inx = GetWindowWord( hWnd, GWW_ID ); #endif - wControl_p b = mswMapIndex( inx ); + wControl_p b = mswMapIndex(inx); - switch (message) { + switch (message) + { case WM_CHAR: - if ( b != NULL) { - switch( wParam ) { - case 0x0D: - case 0x1B: - case 0x09: - SetFocus( ((wControl_p)(b->parent))->hWnd ); - SendMessage( ((wControl_p)(b->parent))->hWnd, WM_CHAR, - wParam, lParam ); - /*SendMessage( ((wControl_p)(b->parent))->hWnd, WM_COMMAND, - inx, MAKELONG( hWnd, EN_KILLFOCUS ) );*/ - return 0L; - } - } - break; - - case WM_KEYUP: - if ( b != NULL) - switch (b->type) { - case B_STRING: - if (((wString_p)b)->action) - mswSetTrigger( (wControl_p)b, triggerString ); - break; -#ifdef LATER - case B_INTEGER: - if (((wInteger_p)b)->action) - mswSetTrigger( (wControl_p)b, triggerInteger ); - break; - case B_FLOAT: - if (((wFloat_p)b)->action) - mswSetTrigger( (wControl_p)b, triggerFloat ); - break; -#endif - } - break; + if (b != NULL) { + switch (wParam) { + case VK_RETURN: + triggerString(b); + return (0L); + break; + case 0x1B: + case 0x09: + SetFocus(((wControl_p)(b->parent))->hWnd); + SendMessage(((wControl_p)(b->parent))->hWnd, WM_CHAR, + wParam, lParam); + return 0L; + } + } + break; } - return CallWindowProc( oldEditProc, hWnd, message, wParam, lParam ); + return CallWindowProc(oldEditProc, hWnd, message, wParam, lParam); } /* @@ -112,7 +118,7 @@ void wStringSetValue( WORD len = (WORD)strlen( arg ); SendMessage( b->hWnd, WM_SETTEXT, 0, (DWORD)arg ); #ifdef WIN32 - SendMessage( b->hWnd, EM_SETSEL, len, len ); + SendMessage( b->hWnd, EM_SETSEL, 0, -1 ); SendMessage( b->hWnd, EM_SCROLLCARET, 0, 0L ); #else SendMessage( b->hWnd, EM_SETSEL, 0, MAKELPARAM(len,len) ); @@ -140,60 +146,92 @@ const char * wStringGetValue( return buff; } +/** + * Get the string from a entry field. The returned pointer has to be free() after processing is complete. + * + * \param bs IN string entry field + * + * \return pointer to entered string or NULL if entry field is empty. + */ -static void triggerString( - wControl_p b ) +static char *getString(wString_p bs) { - wString_p bs = (wString_p)b; - int cnt; + char *tmpBuffer = NULL; + UINT chars = SendMessage(bs->hWnd, EM_LINELENGTH, (WPARAM)0, 0L); - if (bs->action) { - *(WPARAM*)&mswTmpBuff[0] = 78; - cnt = (int)SendMessage( bs->hWnd, (UINT)EM_GETLINE, 0, (DWORD)(LPSTR)mswTmpBuff ); - mswTmpBuff[cnt] = '\0'; - if (bs->valueP) - strcpy( bs->valueP, mswTmpBuff ); - bs->action( mswTmpBuff, bs->data ); - mswSetTrigger( NULL, NULL ); - } + if (chars) { + tmpBuffer = malloc(chars > sizeof(WORD)? chars + 1 : sizeof(WORD) + 1); + *(WORD *)tmpBuffer = chars; + SendMessage(bs->hWnd, (UINT)EM_GETLINE, 0, (LPARAM)tmpBuffer); + tmpBuffer[chars] = '\0'; + } + + return (tmpBuffer); } +/** + * Retrieve and process string entry. If a string has been entered, the callback for + * the specific entry field is called. + * + * \param b IN string entry field + */ -LRESULT stringProc( - wControl_p b, - HWND hWnd, - UINT message, - WPARAM wParam, - LPARAM lParam ) +static void triggerString( + wControl_p b) { - wString_p bs = (wString_p)b; - int cnt; - int modified; - - switch( message ) { - - case WM_COMMAND: - switch (WCMD_PARAM_NOTF) { - case EN_KILLFOCUS: - modified = (int)SendMessage( bs->hWnd, (UINT)EM_GETMODIFY, 0, 0L ); - if (!modified) - break; - *(WPARAM*)&mswTmpBuff[0] = 78; - cnt = (int)SendMessage( bs->hWnd, (UINT)EM_GETLINE, 0, (DWORD)(LPSTR)mswTmpBuff ); - mswTmpBuff[cnt] = '\0'; - if (bs->valueP) - strncpy( bs->valueP, mswTmpBuff, bs->valueL ); - if (bs->action) { - bs->action( mswTmpBuff, bs->data ); - mswSetTrigger( NULL, NULL ); - } - break; - SendMessage( bs->hWnd, (UINT)EM_SETMODIFY, FALSE, 0L ); - } - break; + wString_p bs = (wString_p)b; + + char *enteredString = getString(bs); + if (enteredString) + { + if (bs->valueP) { + strcpy(bs->valueP, enteredString); + } + if (bs->action) { + bs->action(enteredString, bs->data); + } + free(enteredString); } +} - return DefWindowProc( hWnd, message, wParam, lParam ); + +LRESULT stringProc( + wControl_p b, + HWND hWnd, + UINT message, + WPARAM wParam, + LPARAM lParam) +{ + wString_p bs = (wString_p)b; + int modified; + + switch (message) { + + case WM_COMMAND: + switch (WCMD_PARAM_NOTF) { + case EN_KILLFOCUS: + modified = (int)SendMessage(bs->hWnd, (UINT)EM_GETMODIFY, 0, 0L); + if (!modified) { + break; + } + + char *enteredString = getString(bs); + if (enteredString) { + if (bs->valueP) { + strcpy(bs->valueP, enteredString); + } + if (bs->action) { + bs->action(enteredString, bs->data); + mswSetTrigger(NULL, NULL); + } + free(enteredString); + } + SendMessage(bs->hWnd, (UINT)EM_SETMODIFY, FALSE, 0L); + } + break; + } + + return DefWindowProc(hWnd, message, wParam, lParam); } @@ -249,10 +287,6 @@ wString_p wStringCreate( return b; } -#ifdef CONTROL3D - Ctl3dSubclassCtl( b->hWnd); -#endif - newEditProc = MakeProcInstance( (XWNDPROC)pushEdit, mswHInst ); oldEditProc = (XWNDPROC)GetWindowLong(b->hWnd, GWL_WNDPROC ); SetWindowLong( b->hWnd, GWL_WNDPROC, (LONG)newEditProc ); diff --git a/app/wlib/mswlib/mswint.h b/app/wlib/mswlib/mswint.h index 2311415..e560053 100644 --- a/app/wlib/mswlib/mswint.h +++ b/app/wlib/mswlib/mswint.h @@ -1,6 +1,7 @@ #include "wlib.h" #include "mswlib.h" -#include "dynarr.h" +//#include "dynarr.h" +#include "common.h" #ifndef WIN32 /*#define CONTROL3D*/ #endif @@ -38,7 +39,9 @@ #define WSCROLL_PARAM_HWND HIWORD(lParam) #endif -#define CAST_AWAY_CONST (char *) +#ifndef CAST_AWAY_CONST + #define CAST_AWAY_CONST (char *) +#endif #define BOOL_T wBool_t #define POS_T wPos_t @@ -122,16 +125,24 @@ struct wDraw_t { double DPI; wDrawRedrawCallBack_p drawRepaint; wDrawActionCallBack_p action; - HBITMAP hBm; + HBITMAP hBmMain; + HBITMAP hBmTemp; + HBITMAP hBmOld; HPEN hPen; HBRUSH hBrush; wDraw_p drawNext; - HBITMAP hBmOld; wBool_t hasPalette; int paletteClock; HBITMAP hBmBackup; HDC hDcBackup; HBITMAP hBmBackupOld; + void *background; + wBool_t bTempMode; + wBool_t bCopiedMain; + + wPos_t lastX; + wPos_t lastY; + }; extern HINSTANCE mswHInst; @@ -147,6 +158,7 @@ extern wDrawColor wDrawColorWhite; extern wDrawColor wDrawColorBlack; extern long mswThickFont; extern double mswScale; +extern double scaleIcon; DWORD mswGetBaseStyle( wWin_p ); char * mswStrdup( const char * ); @@ -190,4 +202,5 @@ void deleteBitmaps( void ); void mswDrawIcon( HDC, int, int, wIcon_p, int, COLORREF, COLORREF ); /* gwin32.c*/ -char *g_win32_getlocale (void); \ No newline at end of file +char *g_win32_getlocale (void); + diff --git a/app/wlib/mswlib/mswlist.c b/app/wlib/mswlib/mswlist.c index 2453a5e..95ecec3 100644 --- a/app/wlib/mswlib/mswlist.c +++ b/app/wlib/mswlib/mswlist.c @@ -243,7 +243,7 @@ wBool_t wListSetValues( void * itemData ) { listData * ldp; - WORD curSel; + WORD curSel = -1; ldp = (listData*)malloc( sizeof *ldp ); ldp->itemContext = itemData; ldp->bm = bm; diff --git a/app/wlib/mswlib/mswmenu.c b/app/wlib/mswlib/mswmenu.c index 815752a..d56e24d 100644 --- a/app/wlib/mswlib/mswmenu.c +++ b/app/wlib/mswlib/mswmenu.c @@ -31,6 +31,7 @@ #include #include #include +#include "misc.h" #include "mswint.h" #include "i18n.h" @@ -579,7 +580,7 @@ wMenuPush_p wMenuPushCreate( { wMenuPush_p mi; int rc; - char label[80]; + char *label = malloc(strlen(labelStr) + 30 ); /**< The label and sufficient space for the keyboard shortcut */ char *cp; char ac; UINT vk; @@ -591,9 +592,9 @@ wMenuPush_p wMenuPushCreate( mi->mparent = m; mi->acclKey = acclKey; mi->enabled = TRUE; - strcpy( label, mi->labelStr ); + strcpy(label, labelStr); modifier = 0; - if ( acclKey != 0 ) { + if ( acclKey != 0 && strlen(label ) < 60 ) { DYNARR_APPEND( acclTable_t, acclTable_da, 10 ); cp = label + strlen( label ); *cp++ = '\t'; @@ -625,6 +626,7 @@ wMenuPush_p wMenuPushCreate( acclTable(acclTable_da.cnt-1).mp = mi; } rc = AppendMenu( m->menu, MF_STRING, mi->index, label ); + free(label); return mi; } diff --git a/app/wlib/mswlib/mswmisc.c b/app/wlib/mswlib/mswmisc.c index e045cc8..6b5f1c9 100644 --- a/app/wlib/mswlib/mswmisc.c +++ b/app/wlib/mswlib/mswmisc.c @@ -22,6 +22,7 @@ #define _WIN32_WINNT 0x0500 #include +#include #include #include #include @@ -30,8 +31,10 @@ #include #include #include +#include "misc.h" #include "mswint.h" #include "i18n.h" +#include "FreeImage.h" #if _MSC_VER > 1300 #define stricmp _stricmp @@ -47,6 +50,8 @@ char * mswStrdup(const char *); #define ALARM_TIMER (902) #define BALLOONHELP_TIMER (903) #define TRIGGER_TIMER (904) +#define CONTROLHILITEWIDTH (2) +#define CONTROLHILITECOLOR (RGB(0x3a,0x5f,0xcd)) #define WANT_LITTLE_LABEL_FONT @@ -78,6 +83,8 @@ HFONT mswLabelFont; long mswThickFont = 1; double mswScale = 1.0; +double scaleIcon = 1.0; /**< Scaling factor for toolbar icons */ + callBacks_t *mswCallBacks[CALLBACK_CNT]; void closeBalloonHelp(void); @@ -87,7 +94,12 @@ static wControl_p getControlFromCursor(HWND, wWin_p *); */ struct wWin_t { - WOBJ_COMMON + WOBJ_COMMON + int validGeometry; + int min_width; + int max_width; + int min_height; + int max_height; wPos_t lastX, lastY; wPos_t padX, padY; wControl_p first, last; @@ -174,7 +186,21 @@ static int dumpControls; extern char *userLocale; - +// list of supported fileformats for image files +char * filterImageFiles[] = { N_("All image files"), + "*.gif;*.jpg;*.jpeg;*.png;*.tif;*.tiff", + N_("GIF files (*.gif)"), + "*.gif", + N_("JPEG files (*.jpeg,*.jpg)"), + "*.jpg;*.jpeg", + N_("PNG files (*.png)"), + "*.png", + N_("TIFF files (*.tiff, *.tif)"), + "*.tif;*.tiff", + N_("All files (*)"), + "*", + }; + /* ***************************************************************************** * @@ -609,6 +635,35 @@ static void getSavedSizeAndPos( } } +/** + * Set min and max dimensions for a window. + * + * \param min_width IN minimum width of window + * \param max_width IN maximum width of window + * \param min_height IN minimum height of window + * \param max_height IN maximum height of window + * \param base_width IN unused on Windows + * \param base_height IN unused on Windows + * \param aspect_ration IN unused on Windows + */ +void wSetGeometry(wWin_p win, + int min_width, + int max_width, + int min_height, + int max_height, + int base_width, + int base_height, + double aspect_ratio) +{ + win->validGeometry = TRUE; //remember that geometry was set + win->min_width = min_width; + win->max_width = max_width; + win->min_height = min_height; + win->max_height = max_height; + + return; +} + /** * Create a window. Retrieves the saved size and position and restores the created window accordingly. * @@ -812,6 +867,10 @@ wWin_p wWinMainCreate( wPrefGetInteger("draw", "maximized", &maximize, 0L); option |= (maximize ? F_MAXIMIZE : 0); + wPrefGetFloat(PREFSECTION, LARGEICON, &scaleIcon, 1.0); + if (scaleIcon < 1.0) scaleIcon = 1.0; + if (scaleIcon > 2.0) scaleIcon = 2.0; + showCmd = SW_SHOW; w = winCommonCreate(NULL, W_MAIN, option|F_RESIZE, "MswMainWindow", WS_OVERLAPPEDWINDOW, labelStr, winProc, x, y, data, @@ -819,13 +878,10 @@ wWin_p wWinMainCreate( mswHWnd = w->hWnd; if (!mswThickFont) { - DWORD dw; SendMessage(w->hWnd, WM_SETFONT, (WPARAM)mswLabelFont, 0L); hDc = GetDC(w->hWnd); GetTextMetrics(hDc, &tm); mswEditHeight = tm.tmHeight+2; - dw = GetTextExtent(hDc, "AXqypj", 6); - mswEditHeight = HIWORD(dw)+2; ReleaseDC(w->hWnd, hDc); } @@ -1380,12 +1436,11 @@ void wWinClear( { } -void wSetCursor( +void wSetCursor(wDraw_p win, wCursor_t cursor) { switch (cursor) { case wCursorNormal: - case wCursorQuestion: default: SetCursor(LoadCursor(NULL, IDC_ARROW)); break; @@ -1401,6 +1456,42 @@ void wSetCursor( case wCursorIBeam: SetCursor(LoadCursor(NULL, IDC_IBEAM)); break; + + case wCursorQuestion: + SetCursor(LoadCursor(NULL, IDC_HELP)); + break; + + case wCursorHand: + SetCursor(LoadCursor(NULL, IDC_HAND)); + break; + + case wCursorNo: + SetCursor(LoadCursor(NULL, IDC_NO)); + break; + + case wCursorSizeAll: + SetCursor(LoadCursor(NULL, IDC_SIZEALL)); + break; + + case wCursorSizeNESW: + SetCursor(LoadCursor(NULL, IDC_SIZENESW)); + break; + + case wCursorSizeNWSE: + SetCursor(LoadCursor(NULL, IDC_SIZENWSE)); + break; + + case wCursorSizeNS: + SetCursor(LoadCursor(NULL, IDC_SIZENS)); + break; + + case wCursorSizeWE: + SetCursor(LoadCursor(NULL, IDC_SIZEWE)); + break; + + case wCursorAppStart: + SetCursor(LoadCursor(NULL, IDC_APPSTARTING)); + break; } curCursor = cursor; @@ -1663,7 +1754,7 @@ void wControlSetLabel( wControl_p b, const char * labelStr) { - if (b->type == B_RADIO || b->type == B_TOGGLE) { + if (b->type == B_RADIO ) { ; } else { int lab_l; @@ -1693,8 +1784,6 @@ void wControlSetContext( b->data = context; } -static int controlHiliteWidth = 5; -static int controlHiliteWidth2 = 3; void wControlHilite( wControl_p b, wBool_t hilite) @@ -1702,12 +1791,13 @@ void wControlHilite( HDC hDc; HPEN oldPen, newPen; int oldMode; + LOGBRUSH logBrush = { BS_SOLID, CONTROLHILITECOLOR, (ULONG_PTR)NULL }; if (b == NULL) { return; } - if (!IsWindowVisible(b->parent->hWnd)) { + if (!IsWindowVisible(b->parent->hWnd)) { return; } @@ -1716,14 +1806,18 @@ void wControlHilite( } hDc = GetDC(b->parent->hWnd); - newPen = CreatePen(PS_SOLID, controlHiliteWidth, RGB(0,0,0)); + newPen = ExtCreatePen(PS_GEOMETRIC | PS_SOLID | PS_ENDCAP_ROUND | PS_JOIN_BEVEL, + CONTROLHILITEWIDTH, + &logBrush, + 0, + NULL); oldPen = SelectObject(hDc, newPen); oldMode = SetROP2(hDc, R2_NOTXORPEN); - MoveTo(hDc, b->x-controlHiliteWidth2, b->y-controlHiliteWidth2); - LineTo(hDc, b->x+b->w+controlHiliteWidth2, b->y-controlHiliteWidth2); - LineTo(hDc, b->x+b->w+controlHiliteWidth2, b->y+b->h+controlHiliteWidth2); - LineTo(hDc, b->x-controlHiliteWidth2, b->y+b->h+controlHiliteWidth2); - LineTo(hDc, b->x-controlHiliteWidth2, b->y-controlHiliteWidth2); + Rectangle(hDc, + b->x - CONTROLHILITEWIDTH - 1, + b->y - CONTROLHILITEWIDTH - 1, + b->x + b->w + CONTROLHILITEWIDTH + 1, + b->y + b->h + CONTROLHILITEWIDTH + 1); SetROP2(hDc, oldMode); SelectObject(hDc, oldPen); DeleteObject(newPen); @@ -1766,6 +1860,26 @@ void wMessage( ReleaseDC(w->hWnd, hDc); } +/** + * Open a document using an external application + * + * \param file + * \return TRUE on success, FALSE on error + * + */ +unsigned wOpenFileExternal(char *file) +{ + HINSTANCE res; + + res = ShellExecute(mswHWnd, "open", file, NULL, NULL, SW_SHOW); + + if ((int)res <= 32) { + wNoticeEx(NT_ERROR, "Error when opening file!", "Cancel", NULL); + return(FALSE); + } + + return(TRUE); +} void wExit(int rc) { @@ -2040,12 +2154,22 @@ int wNotice3( } } +/** + * Show help text for the given topic. + * + * \param topic The topic. if NULL the index page is shown. + */ void wHelp( const char * topic) { char *pszHelpTopic; HWND hwndHelp; + char *theTopic = "index"; + + if (topic) { + theTopic = topic; + } if (!helpInitted) { HtmlHelp(NULL, NULL, HH_INITIALIZE, (DWORD)&dwCookie) ; @@ -2054,9 +2178,9 @@ void wHelp( /* "c:\\help.chm::/intro.htm>mainwin", */ /* attention: always adapt constant value (10) to needed number of formatting characters */ - pszHelpTopic = malloc(strlen(helpFile) + strlen(topic) + 10); + pszHelpTopic = malloc(strlen(helpFile) + strlen(theTopic) + 10); assert(pszHelpTopic != NULL); - sprintf(pszHelpTopic, "/%s.html", topic); + sprintf(pszHelpTopic, "/%s.html", theTopic); hwndHelp = HtmlHelp(mswHWnd, helpFile, HH_DISPLAY_TOPIC, (DWORD_PTR)pszHelpTopic); @@ -2068,6 +2192,8 @@ void wHelp( } + + void doHelpMenu(void * context) { HH_FTS_QUERY ftsQuery; @@ -2092,6 +2218,13 @@ void doHelpMenu(void * context) HtmlHelp(mswHWnd, helpFile, HH_DISPLAY_SEARCH,(DWORD)&ftsQuery); break; + + case 3: /*Context*/ + const char * topic; + topic = GetCurCommandName(); + wHelp(topic); + break; + default: return; } @@ -2099,11 +2232,16 @@ void doHelpMenu(void * context) helpInitted = TRUE; } +void wDoAccelHelp(wAccelKey_e key, void * context) { + doHelpMenu(context); +} + void wMenuAddHelp( wMenu_p m) { - wMenuPushCreate(m, NULL, "&Contents", 0, doHelpMenu, (void*)1); - wMenuPushCreate(m, NULL, "&Search for Help on...", 0, doHelpMenu, (void*)2); + wMenuPushCreate(m, NULL, _("&Contents"), 0, doHelpMenu, (void*)1); + wMenuPushCreate(m, NULL, _("&Search for Help on..."), 0, doHelpMenu, (void*)2); + wMenuPushCreate(m, NULL, _("Co&mmand Context Help"), 0, doHelpMenu, (void*)3); } @@ -2326,6 +2464,24 @@ struct wFilSel_t { #define SELECTEDFILENAME_BUFFERSIZE (8*1024) /**extList; + if (fs->option == FS_PICTURES) { + ofn.lpstrFilter = GetImageFileFormats(); + } + else { + ofn.lpstrFilter = fs->extList; + } + ofn.nFilterIndex = 0; selFileName = malloc(SELECTEDFILENAME_BUFFERSIZE); memset(selFileName, '\0', SELECTEDFILENAME_BUFFERSIZE); @@ -2599,6 +2760,23 @@ MainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) wAccelKey_e extChar; switch (message) { + case WM_GETMINMAXINFO: + LPMINMAXINFO pMMI = (LPMINMAXINFO)lParam; + inx = GetWindowWord(hWnd, 0); + + if (inx >= CONTROL_BASE && inx <= controlMap_da.cnt) { + w = (wWin_p)controlMap(inx - CONTROL_BASE).b; + if (w != NULL) { + if (w->validGeometry) { + pMMI->ptMaxTrackSize.x = w->max_width; + pMMI->ptMaxTrackSize.y = w->max_height; + pMMI->ptMinTrackSize.x = w->min_width; + pMMI->ptMinTrackSize.y = w->min_height; + } + } + } + return(0); + case WM_MOUSEWHEEL: inx = GetWindowWord(hWnd, 0); b = getControlFromCursor(hWnd, NULL); @@ -2614,22 +2792,6 @@ MainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) case WM_DRAWITEM: case WM_COMMAND: case WM_MEASUREITEM: - case WM_NOTVALID: - if (WCMD_PARAM_ID == IDM_DOHELP) { - b = getControlFromCursor(hWnd, NULL); - closeBalloonHelp(); - - if (!b) { - return 0L; - } - - if (b->helpStr) { - wHelp(b->helpStr); - } - - return 0L; - } - closeBalloonHelp(); if (WCMD_PARAM_ID < CONTROL_BASE || WCMD_PARAM_ID > (WPARAM)controlMap_da.cnt) { @@ -2913,26 +3075,26 @@ MainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) case WM_SETCURSOR: /*if (any buttons down) break;*/ - wSetCursor(curCursor); + wSetCursor(NULL, curCursor); if (!mswAllowBalloonHelp) { - break; + return TRUE; } if (IsIconic(mswHWnd)) { - break; + return TRUE; } b = getControlFromCursor(hWnd, NULL); if (b == balloonControlButton) { - break; + return TRUE; } if (/*(!IsWindowEnabled(hWnd))*/ GetActiveWindow() != hWnd || (!b) || b->type == B_DRAW || b->helpStr == NULL) { closeBalloonHelp(); - break; + return TRUE; } if (b != balloonHelpButton) { @@ -2940,19 +3102,19 @@ MainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) } if (balloonHelpState != balloonHelpIdle) { - break; + return TRUE; } balloonHelpTimer = SetTimer(mswHWnd, BALLOONHELP_TIMER, balloonHelpTimeOut, NULL); if (balloonHelpTimer == (UINT)0) { - break; + return TRUE; } balloonHelpState = balloonHelpWait; balloonHelpButton = b; - break; + return TRUE; case WM_SYSCOMMAND: inx = GetWindowWord(hWnd, 0); @@ -3211,13 +3373,13 @@ static BOOL InitApplication(HINSTANCE hinstCurrent) return FALSE; } - wc.style = CS_VREDRAW | CS_HREDRAW | CS_OWNDC; + wc.style = CS_VREDRAW | CS_HREDRAW | CS_OWNDC | CS_DBLCLKS; wc.lpfnWndProc = mswDrawPush; wc.lpszClassName = mswDrawWindowClassName; wc.cbWndExtra = 4; if (!RegisterClass(&wc)) { - mswFail("RegisterClass(drawClass)"); + mswFail("RegisterClass(drawClass)"); return FALSE; } @@ -3238,8 +3400,6 @@ int PASCAL WinMain(HINSTANCE hinstCurrent, HINSTANCE hinstPrevious, HDC hDc; char **argv; int argc; - TEXTMETRIC tm; - DWORD dw; if (!hinstPrevious) { if (!InitApplication(hinstCurrent)) { @@ -3264,10 +3424,6 @@ int PASCAL WinMain(HINSTANCE hinstCurrent, HINSTANCE hinstPrevious, mswScale = 1.0; } - GetTextMetrics(hDc, &tm); - mswEditHeight = tm.tmHeight + 8; - dw = GetTextExtent(hDc, "AXqypj", 6); - mswEditHeight = HIWORD(dw)+2; ReleaseDC(0, hDc); mswCreateCheckBitmaps(); /* diff --git a/app/wlib/mswlib/mswmsg.c b/app/wlib/mswlib/mswmsg.c index 4a21921..6445299 100644 --- a/app/wlib/mswlib/mswmsg.c +++ b/app/wlib/mswlib/mswmsg.c @@ -42,6 +42,7 @@ static void repaintMessage( HFONT hFont; LOGFONT msgFont; double scale = 1.0; + TEXTMETRIC textMetrics; hDc = GetDC( hWnd ); @@ -74,13 +75,15 @@ static void repaintMessage( hFont = SelectObject( hDc, mswLabelFont ); } + GetTextMetrics(hDc, &textMetrics); + rect.bottom = (long)(bm->y+( bm->h )); rect.right = (long)(bm->x+( scale * bm->w )); - rect.top = bm->y; + rect.top = bm->y+1; rect.left = bm->x; SetBkColor( hDc, GetSysColor( COLOR_BTNFACE ) ); - ExtTextOut( hDc, bm->x, bm->y, ETO_CLIPPED|ETO_OPAQUE, &rect, bm->message, strlen( bm->message ), NULL ); + ExtTextOut( hDc, bm->x, bm->y + ((bm->h + 2 - textMetrics.tmHeight) / 2), ETO_CLIPPED|ETO_OPAQUE, &rect, bm->message, strlen( bm->message ), NULL ); if( scale != 1.0 ) /* in case we did create a new font earlier, delete it now */ @@ -138,7 +141,7 @@ wPos_t wMessageGetHeight( long flags ) if( flags & BM_SMALL ) scale = SCALE_SMALL; - return((wPos_t)((mswEditHeight - 4) * scale )); + return((wPos_t)((mswEditHeight) * scale )); #endif } diff --git a/app/wlib/mswlib/mswpref.c b/app/wlib/mswlib/mswpref.c index eaa39fe..201171a 100644 --- a/app/wlib/mswlib/mswpref.c +++ b/app/wlib/mswlib/mswpref.c @@ -5,6 +5,7 @@ #include #include #include +#include "misc.h" #include "mswint.h" #include #include diff --git a/app/wlib/mswlib/mswprint.c b/app/wlib/mswlib/mswprint.c index 91f05ea..13756c7 100644 --- a/app/wlib/mswlib/mswprint.c +++ b/app/wlib/mswlib/mswprint.c @@ -27,7 +27,7 @@ struct tagPD printDlg; #endif static int printStatus = FALSE; static DOCINFO docInfo; -static double pageSizeW = 8.5, pageSizeH = 11.0; +static double tBorder = 0.0, rBorder = 0.0, bBorder = 0.0, lBorder = 0.0; static double physSizeW = 8.5, physSizeH = 11.0; static int pageCount = -1; @@ -66,10 +66,16 @@ void getPageDim( HDC hDc ) size_h = GetDeviceCaps( hDc, VERTSIZE ); print_d.w = res_w = GetDeviceCaps( hDc, HORZRES ); print_d.h = res_h = GetDeviceCaps( hDc, VERTRES ); + double pageSizeW, pageSizeH; pageSizeW = ((double)res_w)/print_d.wFactor; pageSizeH = ((double)res_h)/print_d.hFactor; physSizeW = ((double)dims.x)/print_d.wFactor; physSizeH = ((double)dims.y)/print_d.hFactor; + // Get Borders/Margins - offs are the top, left borders + lBorder = ((double)offs.x)/print_d.hFactor; + tBorder = ((double)offs.y)/print_d.hFactor; + rBorder = physSizeW-pageSizeW-lBorder; + bBorder = physSizeH-pageSizeH-tBorder; } static wBool_t printInit( void ) @@ -83,6 +89,7 @@ static wBool_t printInit( void ) return printerOk; } initted = TRUE; + memset(&printDlg, 0, sizeof printDlg); printDlg.lStructSize = sizeof printDlg; printDlg.hwndOwner = NULL; printDlg.Flags = PD_RETURNDC|PD_RETURNDEFAULT; @@ -194,16 +201,36 @@ void wPrintSetup( wPrintSetupCallBack_p callback ) } } +const char* wPrintGetName() +{ + static char sPrinterName[100]; + HANDLE hDevNames = printDlg.hDevNames; + DEVNAMES* pDevNames = GlobalLock(hDevNames); + if (pDevNames == NULL) { + strcpy(sPrinterName, "Printer"); + } + else { + strncpy(sPrinterName, (char*)pDevNames + pDevNames->wDeviceOffset, sizeof sPrinterName - 1); + sPrinterName[sizeof sPrinterName - 1] = '\0'; + } + GlobalUnlock( hDevNames ); + return sPrinterName; +} -void wPrintGetPageSize( double *w, double *h ) +void wPrintGetMargins( + double * tMargin, + double * rMargin, + double * bMargin, + double * lMargin ) { - printInit(); - *w = pageSizeW; - *h = pageSizeH; + if ( tMargin ) *tMargin = tBorder; + if ( rMargin ) *rMargin = rBorder; + if ( bMargin ) *bMargin = bBorder; + if ( lMargin ) *lMargin = lBorder; } -void wPrintGetPhysSize( double *w, double *h ) +void wPrintGetPageSize( double *w, double *h ) { printInit(); *w = physSizeW; @@ -378,10 +405,3 @@ wBool_t wPrintNewMargin( const char * name, double t, double b, double l, double { return TRUE; } - -void wPrintSetCallBacks( - wAddPrinterCallBack_p newPrinter, - wAddMarginCallBack_p newMargin, - wAddFontAliasCallBack_p newFontAlias ) -{ -} diff --git a/app/wlib/mswlib/mswsplash.c b/app/wlib/mswlib/mswsplash.c index 47df6b7..172b563 100644 --- a/app/wlib/mswlib/mswsplash.c +++ b/app/wlib/mswlib/mswsplash.c @@ -204,8 +204,11 @@ wCreateSplash( char *appname, char *appver ) /* create the title string */ pszBuf = malloc( strlen( appname ) + strlen( appver ) + 2 ); - if( !pszBuf ) - return( 0 ); + if (!pszBuf) { + GlobalUnlock(hgbl); + GlobalFree(hgbl); + return(0); + } sprintf( pszBuf, "%s %s", appname, appver ); lpw += 1+MultiByteToWideChar (CP_ACP, 0, pszBuf, -1, (LPWSTR)lpw, 50); @@ -226,7 +229,6 @@ wCreateSplash( char *appname, char *appver ) GlobalUnlock(hgbl); hSplash = CreateDialogIndirectParam( mswHInst, (LPDLGTEMPLATE) hgbl, mswHWnd, (DLGPROC)SplashDlgProc, (LPARAM)hBmp ); - GetLastError(); /* free allocated memory */ GlobalFree(hgbl); diff --git a/app/wlib/mswlib/mswtext.c b/app/wlib/mswlib/mswtext.c index 293e2b4..0a0ce88 100644 --- a/app/wlib/mswlib/mswtext.c +++ b/app/wlib/mswlib/mswtext.c @@ -137,6 +137,9 @@ void wTextAppend( if (b->option&BO_READONLY) { SendMessage(b->hWnd, EM_SETREADONLY, 1, 0L); } + + // scroll to bottom of text box + SendMessage(b->hWnd, EM_LINESCROLL, 0, 10000L); } @@ -247,42 +250,54 @@ wBool_t wTextGetModified( return (wBool_t)rc; } +/** + * Get the size of the text in the text control including terminating '\0'. Note that + * the text actually might be shorter if the text includes CRs. + * + * \param b IN text control + * \return required buffer size + */ int wTextGetSize( wText_p b) { - int lc, l, len=0; - lc = (int)SendMessage(b->hWnd, EM_GETLINECOUNT, 0, 0L); + int len; - for (l=0; lhWnd, EM_LINEINDEX, l, 0L); - len += (int)SendMessage(b->hWnd, EM_LINELENGTH, charIndex, 0L) + 1; - } - - if (len == 1) { - len = 0; - } + len = GetWindowTextLength(b->hWnd); - return len; + return len + 1; } +/** + * Get the text from a textentry. The buffer must be large enough for the text and + * the terminating \0. + * In case the string contains carriage returns these are removed. The returned string + * will be shortened accordingly. + * To get the complete contents the buffer size must be equal or greater then the return + * value of wTextGetSize() + * + * \param b IN text entry + * \param t IN/OUT buffer for text + * \param s IN size of buffer + */ + void wTextGetText( wText_p b, char * t, int s) { - int lc, l, len; - s--; - lc = (int)SendMessage(b->hWnd, EM_GETLINECOUNT, 0, 0L); - - for (l=0; l=0; l++) { - *(WORD*)t = s; - len = (int)SendMessage(b->hWnd, EM_GETLINE, l, (LPARAM)t); - t += len; - *t++ = '\n'; - s -= len+1; - } - - *(t - 1) = '\0'; // overwrite the last \n added + char *buffer = malloc(s); + char *ptr = buffer; + GetWindowText(b->hWnd, buffer, s); + + // remove carriage returns + while (*ptr) { + if (*ptr != '\r') { + *t = *ptr; + t++; + } + ptr++; + } + free(buffer); } diff --git a/app/wlib/mswlib/simple-gettext.c b/app/wlib/mswlib/simple-gettext.c index d213fc3..412eece 100644 --- a/app/wlib/mswlib/simple-gettext.c +++ b/app/wlib/mswlib/simple-gettext.c @@ -148,8 +148,9 @@ utf8_to_native( char *str, unsigned int len, int dummy ) /* 2. convert from UTF-8 to system codepage */ WideCharToMultiByte(CP_ACP, 0, (LPWSTR)buf, wcharLen, resBuffer, len + 1, NULL, NULL ); - free( buf ); + } + free(buf); return( resBuffer ); } diff --git a/app/wlib/mswlib/unittest/CMakeLists.txt b/app/wlib/mswlib/unittest/CMakeLists.txt new file mode 100644 index 0000000..b91c1ff --- /dev/null +++ b/app/wlib/mswlib/unittest/CMakeLists.txt @@ -0,0 +1,11 @@ +# build unit tests for the xtrkcad Windows library + +add_executable(utf8test + utf8test.c + ../utf8conv.c + ) + +target_link_libraries(utf8test + ${LIBS}) + +add_test(UTF8ConversionTest utf8test) diff --git a/app/wlib/mswlib/unittest/utf8test.c b/app/wlib/mswlib/unittest/utf8test.c new file mode 100644 index 0000000..5b00371 --- /dev/null +++ b/app/wlib/mswlib/unittest/utf8test.c @@ -0,0 +1,65 @@ +/** \file utf8test.c +* Unit tests for utf 8 conversion routines on Windows +*/ + +#include +#include +#include + +#include + +#include + +#define SIMPLEASCIITEXT "The quick brown fox jumps over the lazy dog." +#define UMLAUTTEXT "äöüÄÖÜß" + +static void +ASCIIText(void **state) +{ + char output[100]; + char result[100]; + bool success; + (void)state; + + success = wSystemToUTF8(SIMPLEASCIITEXT, output, 100); + assert_true((void *)success); + + success = wUTF8ToSystem(output, result, 100); + assert_true((void *)success); + + assert_false(strcmp(SIMPLEASCIITEXT, result)); +} + +static void +Umlauts(void **state) +{ + char output[100]; + char result[100]; + bool success; + (void)state; + + success = wIsUTF8(UMLAUTTEXT); + assert_false((void *)success); + + success = wSystemToUTF8(UMLAUTTEXT, output, 100); + assert_true((void *)success); + + success = wIsUTF8(output); + assert_true((void *)success); + + success = wUTF8ToSystem(output, result, 100); + assert_true((void *)success); + + assert_false(strcmp(UMLAUTTEXT, result)); +} + + +int main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(ASCIIText), + cmocka_unit_test(Umlauts), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} \ No newline at end of file diff --git a/app/wlib/mswlib/utf8conv.c b/app/wlib/mswlib/utf8conv.c new file mode 100644 index 0000000..62ada76 --- /dev/null +++ b/app/wlib/mswlib/utf8conv.c @@ -0,0 +1,210 @@ +/** + * \file utf8conv.c. + * + * UTF-8 conversion functions + */ + +/* XTrkCad - Model Railroad CAD + * Copyright (C) 2020 Martin Fischer + * + * 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. + */ + +#include +#include +#include + +#include + +#include + +/** + * Convert system codepage to UTF 8 + * + * \param inString The input string. + * \param [in,out] outString The output string buffer. + * \param outStringLength Length of the output buffer + * + * \returns FALSE if it fails. + */ + +bool +wSystemToUTF8(const char *inString, char *outString, unsigned outStringLength) +{ + unsigned int cnt = 2 * (strlen(inString) + 1); + char *tempBuffer = malloc(cnt); + + // convert to wide character (UTF16) + MultiByteToWideChar(CP_ACP, + 0, + inString, + -1, + (LPWSTR)tempBuffer, + cnt); + + // convert from wide char to UTF-8 + WideCharToMultiByte(CP_UTF8, + 0, + (LPCWCH)tempBuffer, + -1, + (LPSTR)outString, + outStringLength, + NULL, + NULL); + + free(tempBuffer); + return true; +} + +/** + * Convert from UTF-8 to system codepage + * + * \param inString The input string. + * \param [in,out] outString the output string. + * \param outStringLength Length of the output buffer. + * + * \returns True if it succeeds, false if it fails. + */ + +bool +wUTF8ToSystem(const char *inString, char *outString, unsigned outStringLength) +{ + unsigned int cnt = 2 * (strlen(inString) + 1); + char *tempBuffer = malloc(cnt); + + // convert to wide character (UTF16) + MultiByteToWideChar(CP_UTF8, + 0, + inString, + -1, + (LPWSTR)tempBuffer, + cnt); + + + cnt = WideCharToMultiByte(CP_ACP, + 0, + (LPCWCH)tempBuffer, + -1, + (LPSTR)outString, + 0L, + NULL, + NULL); + + if (outStringLength <= cnt) { + return (false); + } + + // convert from wide char to system codepage + WideCharToMultiByte(CP_ACP, + 0, + (LPCWCH)tempBuffer, + -1, + (LPSTR)outString, + outStringLength, + NULL, + NULL); + + free(tempBuffer); + return true; +} + +/** + * Is passed string in correct UTF-8 format? + * Taken from https://stackoverflow.com/questions/1031645/how-to-detect-utf-8-in-plain-c + * + * \param string The string to check. + * + * \returns True if UTF 8, false if not. + */ + +bool wIsUTF8(const char * string) +{ + if (!string) { + return 0; + } + + const unsigned char * bytes = (const unsigned char *)string; + while (*bytes) { + if ((// ASCII + // use bytes[0] <= 0x7F to allow ASCII control characters + bytes[0] == 0x09 || + bytes[0] == 0x0A || + bytes[0] == 0x0D || + (0x20 <= bytes[0] && bytes[0] <= 0x7E) + ) + ) { + bytes += 1; + continue; + } + + if ((// non-overlong 2-byte + (0xC2 <= bytes[0] && bytes[0] <= 0xDF) && + (0x80 <= bytes[1] && bytes[1] <= 0xBF) + ) + ) { + bytes += 2; + continue; + } + + if ((// excluding overlongs + bytes[0] == 0xE0 && + (0xA0 <= bytes[1] && bytes[1] <= 0xBF) && + (0x80 <= bytes[2] && bytes[2] <= 0xBF) + ) || + (// straight 3-byte + ((0xE1 <= bytes[0] && bytes[0] <= 0xEC) || + bytes[0] == 0xEE || + bytes[0] == 0xEF) && + (0x80 <= bytes[1] && bytes[1] <= 0xBF) && + (0x80 <= bytes[2] && bytes[2] <= 0xBF) + ) || + (// excluding surrogates + bytes[0] == 0xED && + (0x80 <= bytes[1] && bytes[1] <= 0x9F) && + (0x80 <= bytes[2] && bytes[2] <= 0xBF) + ) + ) { + bytes += 3; + continue; + } + + if ((// planes 1-3 + bytes[0] == 0xF0 && + (0x90 <= bytes[1] && bytes[1] <= 0xBF) && + (0x80 <= bytes[2] && bytes[2] <= 0xBF) && + (0x80 <= bytes[3] && bytes[3] <= 0xBF) + ) || + (// planes 4-15 + (0xF1 <= bytes[0] && bytes[0] <= 0xF3) && + (0x80 <= bytes[1] && bytes[1] <= 0xBF) && + (0x80 <= bytes[2] && bytes[2] <= 0xBF) && + (0x80 <= bytes[3] && bytes[3] <= 0xBF) + ) || + (// plane 16 + bytes[0] == 0xF4 && + (0x80 <= bytes[1] && bytes[1] <= 0x8F) && + (0x80 <= bytes[2] && bytes[2] <= 0xBF) && + (0x80 <= bytes[3] && bytes[3] <= 0xBF) + ) + ) { + bytes += 4; + continue; + } + + return false; + } + + return true; +} \ No newline at end of file -- cgit v1.2.3