diff --git a/programs/notepad/dialog.c b/programs/notepad/dialog.c index 8d237e48661..a7827b45a3d 100644 --- a/programs/notepad/dialog.c +++ b/programs/notepad/dialog.c @@ -55,7 +55,7 @@ void AlertFileNotFound(LPSTR szFileName) { /* Load and format szMessage */ LoadString(Globals.hInstance, IDS_NOTFOUND, szRessource, sizeof(szRessource)); - wvsprintf(szMessage, szRessource, szFileName); + wsprintf(szMessage, szRessource, szFileName); /* Load szCaption */ LoadString(Globals.hInstance, IDS_ERROR, szRessource, sizeof(szRessource)); @@ -74,7 +74,7 @@ int AlertFileNotSaved(LPSTR szFileName) { /* Load and format Message */ LoadString(Globals.hInstance, IDS_NOTSAVED, szRessource, sizeof(szRessource)); - wvsprintf(szMessage, szRessource, szFileName); + wsprintf(szMessage, szRessource, szFileName); /* Load Caption */ @@ -146,19 +146,12 @@ BOOL DoCloseFile(void) { void DoOpenFile(LPCSTR szFileName) { - int hFile; - WORD nResult; - /* Close any files and prompt to save changes */ if (DoCloseFile()) { GetFileTitle(szFileName, Globals.szFileName, sizeof(Globals.szFileName)); LANGUAGE_UpdateWindowCaption(); - hFile = _lopen(szFileName, OF_READ); - nResult = _lread(hFile, Globals.Buffer, sizeof(Globals.Buffer)); - _lclose(hFile); - /* FIXME: Append time/date if first line contains LOGPREFIX */ - /* (Globals.Buffer, ) */ + LoadBufferFromFile(szFileName); } } @@ -167,7 +160,7 @@ VOID DIALOG_FileNew(VOID) { /* Close any files and promt to save changes */ if (DoCloseFile()) { - /* do nothing yet */ + TrashBuffer(); } } diff --git a/programs/notepad/main.c b/programs/notepad/main.c index ca628c21d7f..cfc73c34cee 100644 --- a/programs/notepad/main.c +++ b/programs/notepad/main.c @@ -1,8 +1,25 @@ /* * Notepad * + * Copyright 2000 Mike McCormack * Copyright 1997,98 Marcel Baur * To be distributed under the Wine License + * + * 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 + * - seperate view code and document code + * + * This program is intended as a testbed for winelib as much as + * a useful application. */ #include @@ -22,6 +39,664 @@ 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(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) + { + 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(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)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 = dwLines - 1; + break; + case SB_PAGEDOWN: + if( dy > dwVOffset) + dwVOffset=0; + break; + } + /* position scroll */ + SetScrollPos(hWnd, SB_VERT, dwVOffset, TRUE); +} + /*********************************************************************** * * NOTEPAD_MenuCommand @@ -87,16 +762,57 @@ LRESULT NOTEPAD_WndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) switch (msg) { case WM_CREATE: - break; + 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); - TextOut(hContext, 1, 1, Globals.Buffer, strlen(Globals.Buffer)); + 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: @@ -148,6 +864,8 @@ int PASCAL WinMain (HANDLE hInstance, HANDLE prev, LPSTR cmdline, int show) /* Select Language */ LANGUAGE_Init(); + /* setup buffer */ + InitBuffer(); /* Setup Globals */ @@ -244,9 +962,6 @@ int PASCAL WinMain (HANDLE hInstance, HANDLE prev, LPSTR cmdline, int show) DragAcceptFiles(Globals.hMainWnd, TRUE); - MessageBox(Globals.hMainWnd, "BEWARE!\nThis is ALPHA software that may destroy your file system.\nPlease take care.", - "A note from the developer...", MB_ICONEXCLAMATION); - /* now enter mesage loop */ while (GetMessage (&msg, 0, 0, 0)) { diff --git a/programs/notepad/main.h b/programs/notepad/main.h index b42880ef9ac..7172939d87b 100644 --- a/programs/notepad/main.h +++ b/programs/notepad/main.h @@ -55,6 +55,9 @@ extern NOTEPAD_GLOBALS Globals; /* function prototypes */ +void TrashBuffer(void); +void LoadBufferFromFile(LPCSTR lpFileName); + /* class names */ /* Resource names */