1054 lines
26 KiB
C
1054 lines
26 KiB
C
/*
|
|
* Notepad
|
|
*
|
|
* Copyright 2000 Mike McCormack <Mike_McCormack@looksmart.com.au>
|
|
* Copyright 1997,98 Marcel Baur <mbaur@g26.ethz.ch>
|
|
* Copyright 2002 Sylvain Petreolle <spetreolle@yahoo.fr>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
* FIXME,TODO list:
|
|
* - Use wine Heap instead of malloc/free (done)
|
|
* - use scroll bars (vertical done)
|
|
* - cut 'n paste (clipboard)
|
|
* - save file
|
|
* - print file
|
|
* - find dialog
|
|
* - encapsulate data structures (?) - half done
|
|
* - free unused memory
|
|
* - solve Open problems
|
|
* - smoother scrolling
|
|
* - separate view code and document code
|
|
*
|
|
* This program is intended as a testbed for winelib as much as
|
|
* a useful application.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include "windows.h"
|
|
|
|
#include "main.h"
|
|
#include "license.h"
|
|
#include "dialog.h"
|
|
#include "language.h"
|
|
|
|
extern BOOL FileExists(LPCSTR szFilename);
|
|
extern BOOL DoCloseFile(void);
|
|
extern void DoOpenFile(LPCSTR szFileName);
|
|
|
|
NOTEPAD_GLOBALS Globals;
|
|
|
|
|
|
/* Using a pointer to pointer data structure to
|
|
achieve a little more efficiency. Hopefully
|
|
it will be worth it, because it complicates the
|
|
code - mjm 26 Jun 2000 */
|
|
|
|
#define BUFFERCHUNKSIZE 0xe0
|
|
typedef struct TAGLine {
|
|
LPSTR lpLine;
|
|
DWORD dwWidth;
|
|
DWORD dwMaxWidth;
|
|
} LINE, *LPLINE;
|
|
|
|
/* FIXME: make this info into a structure */
|
|
/* typedef struct tagBUFFER { */
|
|
DWORD dwVOffset=0;
|
|
DWORD dwLines=0;
|
|
DWORD dwMaxLines=0;
|
|
LPLINE lpBuffer=NULL;
|
|
DWORD dwXpos=0,dwYpos=0; /* position of caret in char coords */
|
|
DWORD dwCaretXpos=0,dwCaretYpos=0; /* position of caret in pixel coords */
|
|
TEXTMETRIC tm; /* textmetric for current font */
|
|
RECT rectClient; /* client rectangle of the window we're drawing in */
|
|
/* } BUFFER, *LPBUFFER */
|
|
|
|
VOID InitFontInfo(HWND hWnd)
|
|
{
|
|
HDC hDC = GetDC(hWnd);
|
|
|
|
if(hDC)
|
|
{
|
|
GetTextMetrics(hDC, &tm);
|
|
ReleaseDC(hWnd,hDC);
|
|
}
|
|
}
|
|
|
|
void InitBuffer(void)
|
|
{
|
|
lpBuffer = NULL;
|
|
dwLines = 0;
|
|
dwMaxLines = 0;
|
|
dwXpos=0;
|
|
dwYpos=0;
|
|
}
|
|
|
|
/* convert x,y character co-ords into x pixel co-ord */
|
|
DWORD CalcStringWidth(HDC hDC, DWORD x, DWORD y)
|
|
{
|
|
DWORD len;
|
|
SIZE size;
|
|
|
|
size.cx = 0;
|
|
size.cy = 0;
|
|
|
|
if(y>dwLines)
|
|
return size.cx;
|
|
if(lpBuffer == NULL)
|
|
return size.cx;
|
|
if(lpBuffer[y].lpLine == NULL)
|
|
return size.cx;
|
|
len = (x<lpBuffer[y].dwWidth) ?
|
|
x : lpBuffer[y].dwWidth;
|
|
GetTextExtentPoint(hDC, lpBuffer[y].lpLine, len, &size);
|
|
|
|
return size.cx;
|
|
}
|
|
|
|
void CalcCaretPos(HDC hDC, DWORD dwXpos, DWORD dwYpos)
|
|
{
|
|
dwCaretXpos = CalcStringWidth(hDC, dwXpos, dwYpos);
|
|
dwCaretYpos = tm.tmHeight*(dwYpos-dwVOffset);
|
|
SetCaretPos(dwCaretXpos,dwCaretYpos);
|
|
}
|
|
|
|
DWORD GetLinesPerPage(HWND hWnd)
|
|
{
|
|
return (rectClient.bottom/tm.tmHeight); /* round down */
|
|
}
|
|
|
|
/* render one line of text and blank space */
|
|
void RenderLine(HDC hDC, DWORD lineno)
|
|
{
|
|
RECT rect;
|
|
HBRUSH hPrev;
|
|
|
|
if(!hDC)
|
|
return;
|
|
|
|
/* erase the space at the end of a line using a white rectangle */
|
|
rect.top = tm.tmHeight*(lineno-dwVOffset);
|
|
rect.bottom = tm.tmHeight*(lineno-dwVOffset+1);
|
|
|
|
if(lpBuffer && (lineno<dwLines))
|
|
rect.left = CalcStringWidth(hDC, lpBuffer[lineno].dwWidth,lineno);
|
|
else
|
|
rect.left = 0;
|
|
rect.right = rectClient.right;
|
|
|
|
/* use the white pen so there's not outline */
|
|
hPrev = SelectObject(hDC, GetStockObject(WHITE_PEN));
|
|
Rectangle(hDC, rect.left, rect.top, rect.right, rect.bottom);
|
|
SelectObject(hDC, hPrev);
|
|
|
|
if(lpBuffer && lpBuffer[lineno].lpLine)
|
|
{
|
|
TextOut(hDC, 0, rect.top, lpBuffer[lineno].lpLine,
|
|
lpBuffer[lineno].dwWidth);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Paint the buffer onto the window.
|
|
*/
|
|
void RenderWindow(HDC hDC) {
|
|
DWORD i;
|
|
|
|
if(!hDC)
|
|
return;
|
|
|
|
/* FIXME: render only necessary lines */
|
|
for(i = dwVOffset; i < (dwVOffset+GetLinesPerPage(0)); i++)
|
|
{
|
|
RenderLine(hDC,i);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check that correct buffers exist to access line y pos x
|
|
* Only manages memory.
|
|
*
|
|
* Returns TRUE if the line is accessable
|
|
* FALSE if there is a problem
|
|
*/
|
|
BOOL ValidateLine(DWORD y,DWORD x)
|
|
{
|
|
DWORD max;
|
|
|
|
/* check to see that the BUFFER has enough lines */
|
|
max = dwMaxLines;
|
|
if( (max<=y) || (lpBuffer == NULL))
|
|
{
|
|
while(max<=y)
|
|
max += BUFFERCHUNKSIZE;
|
|
/* use GlobalAlloc first time round */
|
|
if(lpBuffer)
|
|
lpBuffer = (LPLINE) GlobalReAlloc((HGLOBAL)lpBuffer,GMEM_FIXED,
|
|
max*sizeof(LINE)) ;
|
|
else
|
|
lpBuffer = (LPLINE) GlobalAlloc(GMEM_FIXED, max*sizeof(LINE));
|
|
if(lpBuffer == NULL)
|
|
return FALSE;
|
|
ZeroMemory(&lpBuffer[dwLines], sizeof(LINE)*(max-dwLines) );
|
|
dwMaxLines = max;
|
|
}
|
|
|
|
/* check to see that the LINE is wide enough */
|
|
max = lpBuffer[y].dwMaxWidth;
|
|
if( (max <= x) || (lpBuffer[y].lpLine == NULL) )
|
|
{
|
|
while(max <= x)
|
|
max += BUFFERCHUNKSIZE;
|
|
/* use GlobalAlloc first */
|
|
if(lpBuffer[y].lpLine)
|
|
lpBuffer[y].lpLine = (LPSTR)GlobalReAlloc((HGLOBAL)lpBuffer[y].lpLine,
|
|
GMEM_FIXED, max) ;
|
|
else
|
|
lpBuffer[y].lpLine = (LPSTR)GlobalAlloc( GMEM_FIXED, max);
|
|
if(lpBuffer[y].lpLine == NULL)
|
|
return FALSE;
|
|
lpBuffer[y].dwWidth = 0;
|
|
lpBuffer[y].dwMaxWidth = max;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/* inserts a new line into the buffer */
|
|
BOOL DoNewLine(HDC hDC)
|
|
{
|
|
DWORD i,cnt;
|
|
LPSTR src,dst;
|
|
|
|
/* check to see if we need more memory for the buffer pointers */
|
|
if(!ValidateLine(dwLines,0))
|
|
return FALSE;
|
|
|
|
/* shuffle up all the lines */
|
|
for(i=dwLines; i>(dwYpos+1); i--)
|
|
{
|
|
lpBuffer[i] = lpBuffer[i-1];
|
|
RenderLine(hDC,i);
|
|
}
|
|
ZeroMemory(&lpBuffer[dwYpos+1],sizeof(LINE));
|
|
|
|
/* copy the characters after the carat (if any) to the next line */
|
|
src = &lpBuffer[dwYpos].lpLine[dwXpos];
|
|
cnt = lpBuffer[dwYpos].dwWidth-dwXpos;
|
|
if(!ValidateLine(dwYpos+1,cnt)) /* allocates the buffer */
|
|
return FALSE; /* FIXME */
|
|
dst = &lpBuffer[dwYpos+1].lpLine[0];
|
|
memcpy(dst, src, cnt);
|
|
lpBuffer[dwYpos+1].dwWidth = cnt;
|
|
lpBuffer[dwYpos].dwWidth -= cnt;
|
|
|
|
/* move the cursor */
|
|
dwLines++;
|
|
dwXpos = 0;
|
|
dwYpos++;
|
|
|
|
/* update the window */
|
|
RenderLine(hDC, dwYpos-1);
|
|
RenderLine(hDC, dwYpos);
|
|
CalcCaretPos(hDC, dwXpos, dwYpos);
|
|
/* FIXME: don't use globals */
|
|
SetScrollRange(Globals.hMainWnd, SB_VERT, 0, dwLines, TRUE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Attempt a basic edit buffer
|
|
*/
|
|
BOOL AddCharToBuffer(HDC hDC, char ch)
|
|
{
|
|
/* we can use lpBuffer[dwYpos] */
|
|
if(!ValidateLine(dwYpos,0))
|
|
return FALSE;
|
|
|
|
/* shuffle the rest of the line*/
|
|
if(!ValidateLine(dwYpos, lpBuffer[dwYpos].dwWidth))
|
|
return FALSE;
|
|
lpBuffer[dwYpos].dwWidth++;
|
|
memmove(&lpBuffer[dwYpos].lpLine[dwXpos+1],
|
|
&lpBuffer[dwYpos].lpLine[dwXpos],
|
|
lpBuffer[dwYpos].dwWidth-dwXpos);
|
|
|
|
/* add the character */
|
|
lpBuffer[dwYpos].lpLine[dwXpos] = ch;
|
|
if(dwLines == 0)
|
|
dwLines++;
|
|
dwXpos++;
|
|
|
|
/* update the window and cursor position */
|
|
RenderLine(hDC,dwYpos);
|
|
CalcCaretPos(hDC,dwXpos,dwYpos);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* erase a character */
|
|
BOOL DoBackSpace(HDC hDC)
|
|
{
|
|
DWORD i;
|
|
|
|
if(lpBuffer == NULL)
|
|
return FALSE;
|
|
if(lpBuffer[dwYpos].lpLine && (dwXpos>0))
|
|
{
|
|
dwXpos --;
|
|
/* FIXME: use memmove */
|
|
for(i=dwXpos; i<(lpBuffer[dwYpos].dwWidth-1); i++)
|
|
lpBuffer[dwYpos].lpLine[i]=lpBuffer[dwYpos].lpLine[i+1];
|
|
|
|
lpBuffer[dwYpos].dwWidth--;
|
|
RenderLine(hDC, dwYpos);
|
|
CalcCaretPos(hDC,dwXpos,dwYpos);
|
|
}
|
|
else
|
|
{
|
|
/* Erase a newline. To do this we join two lines */
|
|
LPSTR src, dest;
|
|
DWORD len, oldlen;
|
|
|
|
if(dwYpos==0)
|
|
return FALSE;
|
|
|
|
oldlen = lpBuffer[dwYpos-1].dwWidth;
|
|
if(lpBuffer[dwYpos-1].lpLine&&lpBuffer[dwYpos].lpLine)
|
|
{
|
|
/* concatonate to the end of the line above line */
|
|
src = &lpBuffer[dwYpos].lpLine[0];
|
|
dest = &lpBuffer[dwYpos-1].lpLine[lpBuffer[dwYpos-1].dwWidth];
|
|
len = lpBuffer[dwYpos].dwWidth;
|
|
|
|
/* check the length of the new line */
|
|
if(!ValidateLine(dwYpos-1,lpBuffer[dwYpos-1].dwWidth + len))
|
|
return FALSE;
|
|
|
|
memcpy(dest,src,len);
|
|
lpBuffer[dwYpos-1].dwWidth+=len;
|
|
GlobalFree( (HGLOBAL)lpBuffer[dwYpos].lpLine);
|
|
}
|
|
else if (!lpBuffer[dwYpos-1].lpLine)
|
|
{
|
|
lpBuffer[dwYpos-1]=lpBuffer[dwYpos];
|
|
} /* else both are NULL */
|
|
RenderLine(hDC,dwYpos-1);
|
|
|
|
/* don't zero - it's going to get trashed anyhow */
|
|
|
|
/* shuffle up all the lines below this one */
|
|
for(i=dwYpos; i<(dwLines-1); i++)
|
|
{
|
|
lpBuffer[i] = lpBuffer[i+1];
|
|
RenderLine(hDC,i);
|
|
}
|
|
|
|
/* clear the last line */
|
|
ZeroMemory(&lpBuffer[dwLines-1],sizeof (LINE));
|
|
RenderLine(hDC,dwLines-1);
|
|
dwLines--;
|
|
|
|
/* adjust the cursor position to joining point */
|
|
dwYpos--;
|
|
dwXpos = oldlen;
|
|
|
|
CalcCaretPos(hDC,dwXpos,dwYpos);
|
|
SetScrollRange(Globals.hMainWnd, SB_VERT, 0, dwLines, TRUE);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/* as used by File->New */
|
|
void TrashBuffer(void)
|
|
{
|
|
DWORD i;
|
|
|
|
/* variables belonging to the buffer */
|
|
if(lpBuffer)
|
|
{
|
|
for(i=0; i<dwLines; i++)
|
|
{
|
|
if(lpBuffer[i].lpLine)
|
|
GlobalFree ((HGLOBAL)lpBuffer[i].lpLine);
|
|
ZeroMemory(&lpBuffer[i],sizeof (LINE));
|
|
}
|
|
GlobalFree((HGLOBAL)lpBuffer);
|
|
lpBuffer=NULL;
|
|
}
|
|
dwLines = 0;
|
|
dwMaxLines = 0;
|
|
|
|
/* variables belonging to the view */
|
|
dwXpos = 0;
|
|
dwYpos = 0;
|
|
dwVOffset = 0 ;
|
|
/* FIXME: don't use globals */
|
|
SetScrollPos(Globals.hMainWnd, SB_VERT, dwVOffset, FALSE);
|
|
SetScrollRange(Globals.hMainWnd, SB_VERT, 0, dwLines, TRUE);
|
|
}
|
|
|
|
|
|
/*
|
|
* Add a line to the buffer
|
|
*/
|
|
/* FIXME: this breaks lines longer than BUFFERCHUNKSIZE */
|
|
DWORD CreateLine(
|
|
LPSTR buffer, /* pointer to buffer with file data */
|
|
DWORD size, /* number of bytes available in buffer */
|
|
BOOL nomore)
|
|
{
|
|
DWORD i;
|
|
|
|
if(size == 0)
|
|
return 0;
|
|
|
|
for(i=0; i<size; i++)
|
|
{
|
|
if(buffer[i]==0x0a)
|
|
{
|
|
if(ValidateLine(dwLines,i))
|
|
{
|
|
memcpy(&lpBuffer[dwLines].lpLine[0],&buffer[0],i);
|
|
lpBuffer[dwLines].dwWidth = i;
|
|
dwLines++;
|
|
}
|
|
return i+1;
|
|
}
|
|
}
|
|
|
|
/* make a line of the rest */
|
|
if( (i == BUFFERCHUNKSIZE) || nomore )
|
|
{
|
|
if(ValidateLine(dwLines,i))
|
|
{
|
|
memcpy(&lpBuffer[dwLines].lpLine[0],&buffer[0],i);
|
|
lpBuffer[dwLines].dwWidth = i;
|
|
dwLines++;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* This is probably overcomplicated by lpBuffer data structure...
|
|
* Read blocks from the file, then add each line from the
|
|
* block to the buffer until there is none left. If all
|
|
* a slab isn't used, try load some more data from the file.
|
|
*/
|
|
void LoadBufferFromFile(LPCSTR szFileName)
|
|
{
|
|
HANDLE hFile;
|
|
CHAR *pTemp;
|
|
DWORD size,i,len,bytes_left,bytes_read;
|
|
|
|
hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ, NULL,
|
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if(hFile == INVALID_HANDLE_VALUE)
|
|
return;
|
|
size = BUFFERCHUNKSIZE;
|
|
pTemp = (LPSTR) GlobalAlloc(GMEM_FIXED, size);
|
|
if(!pTemp)
|
|
return;
|
|
bytes_read = 1; /* anything non-zero */
|
|
bytes_left = 0;
|
|
while(bytes_read)
|
|
{
|
|
if(!ReadFile(hFile,
|
|
&pTemp[bytes_left],
|
|
size-bytes_left,
|
|
&bytes_read, NULL))
|
|
break;
|
|
bytes_left+=bytes_read;
|
|
|
|
/* add strings to buffer */
|
|
for(i = 0;
|
|
(i<size) &&
|
|
(len = CreateLine(&pTemp[i], bytes_left, !bytes_read));
|
|
i+= len,bytes_left-=len );
|
|
|
|
/* move leftover to front of buffer */
|
|
if(bytes_left)
|
|
memmove(&pTemp[0],&pTemp[i], bytes_left);
|
|
}
|
|
CloseHandle(hFile);
|
|
}
|
|
|
|
BOOL DoInput(HDC hDC, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch(wParam)
|
|
{
|
|
case 0x08:
|
|
return DoBackSpace(hDC);
|
|
case 0x0d:
|
|
return DoNewLine(hDC);
|
|
default:
|
|
return AddCharToBuffer(hDC,wParam);
|
|
}
|
|
}
|
|
|
|
BOOL GotoHome(HWND hWnd)
|
|
{
|
|
dwXpos = 0;
|
|
dwYpos = 0;
|
|
dwVOffset = 0;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL GotoEndOfLine(HWND hWnd)
|
|
{
|
|
dwXpos = lpBuffer[dwYpos].dwWidth;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL GotoDown(HWND hWnd)
|
|
{
|
|
if((dwYpos+1) >= dwLines)
|
|
{
|
|
return FALSE;
|
|
}
|
|
dwYpos++;
|
|
if (dwXpos>lpBuffer[dwYpos].dwWidth)
|
|
GotoEndOfLine(hWnd);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL GotoUp(HWND hWnd)
|
|
{
|
|
if(dwYpos==0)
|
|
return FALSE;
|
|
dwYpos--;
|
|
if (dwXpos>lpBuffer[dwYpos].dwWidth)
|
|
GotoEndOfLine(hWnd);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL GotoLeft(HWND hWnd)
|
|
{
|
|
if(dwXpos > 0)
|
|
{
|
|
dwXpos--;
|
|
return TRUE;
|
|
}
|
|
if(GotoUp(hWnd))
|
|
return GotoEndOfLine(hWnd);
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL GotoRight(HWND hWnd)
|
|
{
|
|
if(dwXpos<lpBuffer[dwYpos].dwWidth)
|
|
{
|
|
dwXpos++;
|
|
return TRUE;
|
|
}
|
|
if(!GotoDown(hWnd))
|
|
return FALSE;
|
|
dwXpos=0;
|
|
return TRUE;
|
|
}
|
|
|
|
/* check the caret is still on the screen */
|
|
BOOL ScrollABit(HWND hWnd)
|
|
{
|
|
if(dwYpos<dwVOffset)
|
|
{
|
|
dwVOffset = dwYpos;
|
|
return TRUE;
|
|
}
|
|
if(dwYpos>(dwVOffset+GetLinesPerPage(hWnd)))
|
|
{
|
|
dwVOffset = dwYpos - GetLinesPerPage(hWnd) + 1;
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/* FIXME: move the window around so we can still see the caret */
|
|
VOID DoEdit(HWND hWnd, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
HDC hDC;
|
|
|
|
if(lpBuffer==NULL)
|
|
return;
|
|
switch(wParam)
|
|
{
|
|
case VK_HOME: GotoHome(hWnd);
|
|
break;
|
|
|
|
case VK_END: GotoEndOfLine(hWnd);
|
|
break;
|
|
|
|
case VK_LEFT: GotoLeft(hWnd);
|
|
break;
|
|
|
|
case VK_RIGHT: GotoRight(hWnd);
|
|
break;
|
|
|
|
case VK_DOWN: GotoDown(hWnd);
|
|
break;
|
|
|
|
case VK_UP: GotoUp(hWnd);
|
|
break;
|
|
|
|
default:
|
|
return;
|
|
}
|
|
|
|
hDC = GetDC(hWnd);
|
|
if(hDC)
|
|
{
|
|
CalcCaretPos(hDC, dwXpos, dwYpos);
|
|
ReleaseDC(hWnd,hDC);
|
|
}
|
|
if(ScrollABit(hWnd))
|
|
InvalidateRect(hWnd, NULL, FALSE);
|
|
}
|
|
|
|
void ButtonDownToCaretPos(HWND hWnd, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
DWORD x, y, caretx, carety;
|
|
BOOL refine_guess = TRUE;
|
|
HDC hDC;
|
|
|
|
x = LOWORD(lParam);
|
|
y = HIWORD(lParam);
|
|
|
|
caretx = x/tm.tmAveCharWidth; /* guess */
|
|
carety = dwVOffset + y/tm.tmHeight;
|
|
|
|
hDC = GetDC(hWnd);
|
|
|
|
if(lpBuffer == NULL)
|
|
{
|
|
caretx = 0;
|
|
carety = 0;
|
|
refine_guess = FALSE;
|
|
}
|
|
|
|
/* if the cursor is past the bottom, put it after the last char */
|
|
if(refine_guess && (carety>=dwLines) )
|
|
{
|
|
carety=dwLines-1;
|
|
caretx=lpBuffer[carety].dwWidth;
|
|
refine_guess = FALSE;
|
|
}
|
|
|
|
/* cursor past end of line? */
|
|
if(refine_guess && (x>CalcStringWidth(hDC,lpBuffer[carety].dwWidth,carety)))
|
|
{
|
|
caretx = lpBuffer[carety].dwWidth;
|
|
refine_guess = FALSE;
|
|
}
|
|
|
|
/* FIXME: doesn't round properly */
|
|
if(refine_guess)
|
|
{
|
|
if(CalcStringWidth(hDC,caretx,carety)<x)
|
|
{
|
|
while( (caretx<lpBuffer[carety].dwWidth) &&
|
|
(CalcStringWidth(hDC,caretx+1,carety)<x))
|
|
caretx++;
|
|
}
|
|
else
|
|
{
|
|
while((caretx>0)&&(CalcStringWidth(hDC,caretx-1,carety)>x))
|
|
caretx--;
|
|
}
|
|
}
|
|
|
|
/* set the caret's position */
|
|
dwXpos = caretx;
|
|
dwYpos = carety;
|
|
CalcCaretPos(hDC, caretx, carety);
|
|
ReleaseDC(hWnd,hDC);
|
|
}
|
|
|
|
void DoScroll(HWND hWnd, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
DWORD dy = GetLinesPerPage(hWnd);
|
|
|
|
switch(wParam) /* vscroll code */
|
|
{
|
|
case SB_LINEUP:
|
|
if(dwVOffset)
|
|
dwVOffset--;
|
|
break;
|
|
case SB_LINEDOWN:
|
|
if(dwVOffset<dwLines)
|
|
dwVOffset++;
|
|
break;
|
|
case SB_PAGEUP:
|
|
if( (dy+dwVOffset) > dwLines)
|
|
dwVOffset = dwLines - 1;
|
|
break;
|
|
case SB_PAGEDOWN:
|
|
if( dy > dwVOffset)
|
|
dwVOffset=0;
|
|
break;
|
|
}
|
|
/* position scroll */
|
|
SetScrollPos(hWnd, SB_VERT, dwVOffset, TRUE);
|
|
}
|
|
|
|
/***********************************************************************
|
|
*
|
|
* NOTEPAD_MenuCommand
|
|
*
|
|
* All handling of main menu events
|
|
*/
|
|
|
|
int NOTEPAD_MenuCommand (WPARAM wParam)
|
|
{
|
|
switch (wParam) {
|
|
case 0x100: DIALOG_FileNew(); break;
|
|
case 0x101: DIALOG_FileOpen(); break;
|
|
case 0x102: DIALOG_FileSave(); break;
|
|
case 0x103: DIALOG_FileSaveAs(); break;
|
|
case 0x104: DIALOG_FilePrint(); break;
|
|
case 0x105: DIALOG_FilePageSetup(); break;
|
|
case 0x106: DIALOG_FilePrinterSetup();break;
|
|
case 0x108: DIALOG_FileExit(); break;
|
|
|
|
case 0x110: DIALOG_EditUndo(); break;
|
|
case 0x111: DIALOG_EditCut(); break;
|
|
case 0x112: DIALOG_EditCopy(); break;
|
|
case 0x113: DIALOG_EditPaste(); break;
|
|
case 0x114: DIALOG_EditDelete(); break;
|
|
case 0x116: DIALOG_EditSelectAll(); break;
|
|
case 0x117: DIALOG_EditTimeDate();break;
|
|
case 0x119: DIALOG_EditWrap(); break;
|
|
|
|
case 0x120: DIALOG_Search(); break;
|
|
case 0x121: DIALOG_SearchNext(); break;
|
|
|
|
case 0x130: DIALOG_HelpContents(); break;
|
|
case 0x131: DIALOG_HelpSearch(); break;
|
|
case 0x132: DIALOG_HelpHelp(); break;
|
|
case 0x135: DIALOG_HelpLicense(); break;
|
|
case 0x136: DIALOG_HelpNoWarranty(); break;
|
|
case 0x137: DIALOG_HelpAboutWine(); break;
|
|
|
|
/* default: */
|
|
/* LANGUAGE_DefaultHandle(wParam); */
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
*
|
|
* NOTEPAD_WndProc
|
|
*/
|
|
|
|
LRESULT WINAPI NOTEPAD_WndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
PAINTSTRUCT ps;
|
|
HDC hContext;
|
|
HANDLE hDrop; /* drag & drop */
|
|
CHAR szFileName[MAX_STRING_LEN];
|
|
RECT Windowsize;
|
|
|
|
lstrcpy(szFileName, "");
|
|
|
|
switch (msg) {
|
|
|
|
case WM_CREATE:
|
|
GetClientRect(hWnd, &rectClient);
|
|
InitFontInfo(hWnd);
|
|
break;
|
|
|
|
case WM_SETFOCUS:
|
|
CreateCaret(Globals.hMainWnd, 0, 1, tm.tmHeight);
|
|
SetCaretPos(dwCaretXpos, dwCaretYpos);
|
|
ShowCaret(Globals.hMainWnd);
|
|
break;
|
|
|
|
case WM_KILLFOCUS:
|
|
DestroyCaret();
|
|
break;
|
|
|
|
case WM_PAINT:
|
|
GetClientRect(hWnd, &rectClient);
|
|
hContext = BeginPaint(hWnd, &ps);
|
|
RenderWindow(hContext);
|
|
EndPaint(hWnd, &ps);
|
|
break;
|
|
|
|
case WM_KEYDOWN:
|
|
DoEdit(hWnd, wParam, lParam);
|
|
break;
|
|
|
|
case WM_CHAR:
|
|
GetClientRect(hWnd, &rectClient);
|
|
HideCaret(hWnd);
|
|
hContext = GetDC(hWnd);
|
|
DoInput(hContext,wParam,lParam);
|
|
ReleaseDC(hWnd,hContext);
|
|
ShowCaret(hWnd);
|
|
break;
|
|
|
|
case WM_LBUTTONDOWN:
|
|
/* figure out where the mouse was clicked */
|
|
ButtonDownToCaretPos(hWnd, wParam, lParam);
|
|
break;
|
|
|
|
case WM_VSCROLL:
|
|
DoScroll(hWnd, wParam, lParam);
|
|
InvalidateRect(hWnd, NULL, FALSE); /* force a redraw */
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
/* FIXME: this is a bit messy */
|
|
NOTEPAD_MenuCommand(wParam);
|
|
InvalidateRect(hWnd, NULL, FALSE); /* force a redraw */
|
|
hContext = GetDC(hWnd);
|
|
CalcCaretPos(hContext,dwXpos,dwYpos);
|
|
ReleaseDC(hWnd,hContext);
|
|
break;
|
|
|
|
case WM_DESTROYCLIPBOARD:
|
|
MessageBox(Globals.hMainWnd, "Empty clipboard", "Debug", MB_ICONEXCLAMATION);
|
|
break;
|
|
|
|
case WM_CLOSE:
|
|
if (DoCloseFile()) {
|
|
PostQuitMessage(0);
|
|
}
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
PostQuitMessage (0);
|
|
break;
|
|
|
|
case WM_SIZE:
|
|
GetClientRect(Globals.hMainWnd, &Windowsize);
|
|
break;
|
|
|
|
case WM_DROPFILES:
|
|
/* User has dropped a file into main window */
|
|
hDrop = (HANDLE) wParam;
|
|
DragQueryFile(hDrop, 0, (CHAR *) &szFileName, sizeof(szFileName));
|
|
DragFinish(hDrop);
|
|
DoOpenFile(szFileName);
|
|
break;
|
|
|
|
default:
|
|
return DefWindowProc (hWnd, msg, wParam, lParam);
|
|
}
|
|
return 0l;
|
|
}
|
|
|
|
int AlertFileDoesNotExist(LPSTR szFileName) {
|
|
|
|
int nResult;
|
|
CHAR szMessage[MAX_STRING_LEN];
|
|
CHAR szRessource[MAX_STRING_LEN];
|
|
|
|
LoadString(Globals.hInstance, STRING_DOESNOTEXIST, szRessource,
|
|
sizeof(szRessource));
|
|
wsprintf(szMessage, szRessource, szFileName);
|
|
|
|
LoadString(Globals.hInstance, STRING_ERROR, szRessource, sizeof(szRessource));
|
|
|
|
nResult = MessageBox(Globals.hMainWnd, szMessage, szRessource,
|
|
MB_ICONEXCLAMATION | MB_YESNO);
|
|
|
|
return(nResult);
|
|
}
|
|
|
|
void HandleCommandLine(LPSTR cmdline)
|
|
{
|
|
|
|
while (*cmdline && (*cmdline == ' ' || *cmdline == '-'))
|
|
|
|
{
|
|
CHAR option;
|
|
|
|
if (*cmdline++ == ' ') continue;
|
|
|
|
option = *cmdline;
|
|
if (option) cmdline++;
|
|
while (*cmdline && *cmdline == ' ') cmdline++;
|
|
|
|
switch(option)
|
|
{
|
|
case 'p':
|
|
case 'P': printf("Print file: ");
|
|
/* Not yet able to print a file */
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (*cmdline)
|
|
{
|
|
/* file name is passed in the command line */
|
|
char *file_name;
|
|
BOOL file_exists;
|
|
char buf[MAX_PATH];
|
|
|
|
if (FileExists(cmdline))
|
|
{
|
|
file_exists = TRUE;
|
|
file_name = cmdline;
|
|
}
|
|
else
|
|
{
|
|
/* try to find file with ".txt" extention */
|
|
if (!strcmp(".txt", cmdline + strlen(cmdline) - strlen(".txt")))
|
|
{
|
|
file_exists = FALSE;
|
|
file_name = cmdline;
|
|
}
|
|
else
|
|
{
|
|
strncpy(buf, cmdline, MAX_PATH - strlen(".txt") - 1);
|
|
strcat(buf, ".txt");
|
|
file_name = buf;
|
|
file_exists = FileExists(buf);
|
|
}
|
|
}
|
|
|
|
if (file_exists)
|
|
{
|
|
DoOpenFile(file_name);
|
|
InvalidateRect(Globals.hMainWnd, NULL, FALSE);
|
|
}
|
|
else
|
|
{
|
|
switch (AlertFileDoesNotExist(file_name)) {
|
|
case IDYES:
|
|
DoOpenFile(file_name);
|
|
break;
|
|
|
|
case IDNO:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
*
|
|
* WinMain
|
|
*/
|
|
|
|
int PASCAL WinMain (HINSTANCE hInstance, HINSTANCE prev, LPSTR cmdline, int show)
|
|
{
|
|
MSG msg;
|
|
WNDCLASS class;
|
|
char className[] = "NPClass"; /* To make sure className >= 0x10000 */
|
|
char winName[] = "Notepad";
|
|
|
|
/* setup buffer */
|
|
InitBuffer();
|
|
|
|
/* Setup Globals */
|
|
|
|
Globals.lpszIniFile = "notepad.ini";
|
|
Globals.lpszIcoFile = "notepad.ico";
|
|
|
|
Globals.hInstance = hInstance;
|
|
|
|
#ifndef LCC
|
|
Globals.hMainIcon = ExtractIcon(Globals.hInstance,
|
|
Globals.lpszIcoFile, 0);
|
|
#endif
|
|
if (!Globals.hMainIcon) {
|
|
Globals.hMainIcon = LoadIcon(0, MAKEINTRESOURCE(DEFAULTICON));
|
|
}
|
|
|
|
lstrcpy(Globals.szFindText, "");
|
|
lstrcpy(Globals.szFileName, "");
|
|
lstrcpy(Globals.szMarginTop, "25 mm");
|
|
lstrcpy(Globals.szMarginBottom, "25 mm");
|
|
lstrcpy(Globals.szMarginLeft, "20 mm");
|
|
lstrcpy(Globals.szMarginRight, "20 mm");
|
|
lstrcpy(Globals.szHeader, "&n");
|
|
lstrcpy(Globals.szFooter, "Page &s");
|
|
lstrcpy(Globals.Buffer, "Hello World");
|
|
|
|
if (!prev){
|
|
class.style = CS_HREDRAW | CS_VREDRAW;
|
|
class.lpfnWndProc = NOTEPAD_WndProc;
|
|
class.cbClsExtra = 0;
|
|
class.cbWndExtra = 0;
|
|
class.hInstance = Globals.hInstance;
|
|
class.hIcon = LoadIcon (0, IDI_APPLICATION);
|
|
class.hCursor = LoadCursor (0, IDC_ARROW);
|
|
class.hbrBackground = GetStockObject (WHITE_BRUSH);
|
|
class.lpszMenuName = 0;
|
|
class.lpszClassName = className;
|
|
}
|
|
|
|
if (!RegisterClass (&class)) return FALSE;
|
|
|
|
/* Setup windows */
|
|
|
|
|
|
Globals.hMainWnd = CreateWindow (className, winName,
|
|
WS_OVERLAPPEDWINDOW + WS_HSCROLL + WS_VSCROLL,
|
|
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 0,
|
|
LoadMenu(Globals.hInstance, STRING_MENU_Xx),
|
|
Globals.hInstance, 0);
|
|
|
|
Globals.hFindReplaceDlg = 0;
|
|
|
|
LANGUAGE_LoadMenus();
|
|
|
|
SetMenu(Globals.hMainWnd, Globals.hMainMenu);
|
|
|
|
ShowWindow (Globals.hMainWnd, show);
|
|
UpdateWindow (Globals.hMainWnd);
|
|
|
|
/* Set up dialogs */
|
|
|
|
/* Identify Messages originating from FindReplace */
|
|
|
|
Globals.nCommdlgFindReplaceMsg = RegisterWindowMessage("commdlg_FindReplace");
|
|
if (Globals.nCommdlgFindReplaceMsg==0) {
|
|
MessageBox(Globals.hMainWnd, "Could not register commdlg_FindReplace window message",
|
|
"Error", MB_ICONEXCLAMATION);
|
|
}
|
|
|
|
HandleCommandLine(cmdline);
|
|
|
|
/* Set up Drag&Drop */
|
|
|
|
DragAcceptFiles(Globals.hMainWnd, TRUE);
|
|
|
|
/* now enter mesage loop */
|
|
|
|
while (GetMessage (&msg, 0, 0, 0)) {
|
|
if (IsDialogMessage(Globals.hFindReplaceDlg, &msg)!=0) {
|
|
/* Message belongs to FindReplace dialog */
|
|
/* We just let IsDialogMessage handle it */
|
|
}
|
|
else
|
|
{
|
|
/* Message belongs to the Notepad Main Window */
|
|
TranslateMessage (&msg);
|
|
DispatchMessage (&msg);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|