Don't free the stack selector, it's freed in ExitThread anyway (thanks
[wine.git] / programs / notepad / main.c
blob770ab3629585b4bf87bb43c45f5565e23c021c40
1 /*
2 * Notepad
4 * Copyright 2000 Mike McCormack <Mike_McCormack@looksmart.com.au>
5 * Copyright 1997,98 Marcel Baur <mbaur@g26.ethz.ch>
6 * Copyright 2002 Sylvain Petreolle <spetreolle@yahoo.fr>
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * FIXME,TODO list:
23 * - Use wine Heap instead of malloc/free (done)
24 * - use scroll bars (vertical done)
25 * - cut 'n paste (clipboard)
26 * - save file
27 * - print file
28 * - find dialog
29 * - encapsulate data structures (?) - half done
30 * - free unused memory
31 * - solve Open problems
32 * - smoother scrolling
33 * - separate view code and document code
35 * This program is intended as a testbed for winelib as much as
36 * a useful application.
39 #include <stdio.h>
40 #include "windows.h"
42 #include "main.h"
43 #include "license.h"
44 #include "dialog.h"
45 #include "language.h"
47 extern BOOL FileExists(LPCSTR szFilename);
48 extern BOOL DoCloseFile(void);
49 extern void DoOpenFile(LPCSTR szFileName);
51 NOTEPAD_GLOBALS Globals;
54 /* Using a pointer to pointer data structure to
55 achieve a little more efficiency. Hopefully
56 it will be worth it, because it complicates the
57 code - mjm 26 Jun 2000 */
59 #define BUFFERCHUNKSIZE 0xe0
60 typedef struct TAGLine {
61 LPSTR lpLine;
62 DWORD dwWidth;
63 DWORD dwMaxWidth;
64 } LINE, *LPLINE;
66 /* FIXME: make this info into a structure */
67 /* typedef struct tagBUFFER { */
68 DWORD dwVOffset=0;
69 DWORD dwLines=0;
70 DWORD dwMaxLines=0;
71 LPLINE lpBuffer=NULL;
72 DWORD dwXpos=0,dwYpos=0; /* position of caret in char coords */
73 DWORD dwCaretXpos=0,dwCaretYpos=0; /* position of caret in pixel coords */
74 TEXTMETRIC tm; /* textmetric for current font */
75 RECT rectClient; /* client rectangle of the window we're drawing in */
76 /* } BUFFER, *LPBUFFER */
78 VOID InitFontInfo(HWND hWnd)
80 HDC hDC = GetDC(hWnd);
82 if(hDC)
84 GetTextMetrics(hDC, &tm);
85 ReleaseDC(hWnd,hDC);
89 void InitBuffer(void)
91 lpBuffer = NULL;
92 dwLines = 0;
93 dwMaxLines = 0;
94 dwXpos=0;
95 dwYpos=0;
98 /* convert x,y character co-ords into x pixel co-ord */
99 DWORD CalcStringWidth(HDC hDC, DWORD x, DWORD y)
101 DWORD len;
102 SIZE size;
104 size.cx = 0;
105 size.cy = 0;
107 if(y>dwLines)
108 return size.cx;
109 if(lpBuffer == NULL)
110 return size.cx;
111 if(lpBuffer[y].lpLine == NULL)
112 return size.cx;
113 len = (x<lpBuffer[y].dwWidth) ?
114 x : lpBuffer[y].dwWidth;
115 GetTextExtentPoint(hDC, lpBuffer[y].lpLine, len, &size);
117 return size.cx;
120 void CalcCaretPos(HDC hDC, DWORD dwXpos, DWORD dwYpos)
122 dwCaretXpos = CalcStringWidth(hDC, dwXpos, dwYpos);
123 dwCaretYpos = tm.tmHeight*(dwYpos-dwVOffset);
124 SetCaretPos(dwCaretXpos,dwCaretYpos);
127 DWORD GetLinesPerPage(HWND hWnd)
129 return (rectClient.bottom/tm.tmHeight); /* round down */
132 /* render one line of text and blank space */
133 void RenderLine(HDC hDC, DWORD lineno)
135 RECT rect;
136 HBRUSH hPrev;
138 if(!hDC)
139 return;
141 /* erase the space at the end of a line using a white rectangle */
142 rect.top = tm.tmHeight*(lineno-dwVOffset);
143 rect.bottom = tm.tmHeight*(lineno-dwVOffset+1);
145 if(lpBuffer && (lineno<dwLines))
146 rect.left = CalcStringWidth(hDC, lpBuffer[lineno].dwWidth,lineno);
147 else
148 rect.left = 0;
149 rect.right = rectClient.right;
151 /* use the white pen so there's not outline */
152 hPrev = SelectObject(hDC, GetStockObject(WHITE_PEN));
153 Rectangle(hDC, rect.left, rect.top, rect.right, rect.bottom);
154 SelectObject(hDC, hPrev);
156 if(lpBuffer && lpBuffer[lineno].lpLine)
158 TextOut(hDC, 0, rect.top, lpBuffer[lineno].lpLine,
159 lpBuffer[lineno].dwWidth);
164 * Paint the buffer onto the window.
166 void RenderWindow(HDC hDC) {
167 DWORD i;
169 if(!hDC)
170 return;
172 /* FIXME: render only necessary lines */
173 for(i = dwVOffset; i < (dwVOffset+GetLinesPerPage(0)); i++)
175 RenderLine(hDC,i);
180 * Check that correct buffers exist to access line y pos x
181 * Only manages memory.
183 * Returns TRUE if the line is accessable
184 * FALSE if there is a problem
186 BOOL ValidateLine(DWORD y,DWORD x)
188 DWORD max;
190 /* check to see that the BUFFER has enough lines */
191 max = dwMaxLines;
192 if( (max<=y) || (lpBuffer == NULL))
194 while(max<=y)
195 max += BUFFERCHUNKSIZE;
196 /* use GlobalAlloc first time round */
197 if(lpBuffer)
198 lpBuffer = (LPLINE) GlobalReAlloc((HGLOBAL)lpBuffer,GMEM_FIXED,
199 max*sizeof(LINE)) ;
200 else
201 lpBuffer = (LPLINE) GlobalAlloc(GMEM_FIXED, max*sizeof(LINE));
202 if(lpBuffer == NULL)
203 return FALSE;
204 ZeroMemory(&lpBuffer[dwLines], sizeof(LINE)*(max-dwLines) );
205 dwMaxLines = max;
208 /* check to see that the LINE is wide enough */
209 max = lpBuffer[y].dwMaxWidth;
210 if( (max <= x) || (lpBuffer[y].lpLine == NULL) )
212 while(max <= x)
213 max += BUFFERCHUNKSIZE;
214 /* use GlobalAlloc first */
215 if(lpBuffer[y].lpLine)
216 lpBuffer[y].lpLine = (LPSTR)GlobalReAlloc((HGLOBAL)lpBuffer[y].lpLine,
217 GMEM_FIXED, max) ;
218 else
219 lpBuffer[y].lpLine = (LPSTR)GlobalAlloc( GMEM_FIXED, max);
220 if(lpBuffer[y].lpLine == NULL)
221 return FALSE;
222 lpBuffer[y].dwWidth = 0;
223 lpBuffer[y].dwMaxWidth = max;
225 return TRUE;
228 /* inserts a new line into the buffer */
229 BOOL DoNewLine(HDC hDC)
231 DWORD i,cnt;
232 LPSTR src,dst;
234 /* check to see if we need more memory for the buffer pointers */
235 if(!ValidateLine(dwLines,0))
236 return FALSE;
238 /* shuffle up all the lines */
239 for(i=dwLines; i>(dwYpos+1); i--)
241 lpBuffer[i] = lpBuffer[i-1];
242 RenderLine(hDC,i);
244 ZeroMemory(&lpBuffer[dwYpos+1],sizeof(LINE));
246 /* copy the characters after the carat (if any) to the next line */
247 src = &lpBuffer[dwYpos].lpLine[dwXpos];
248 cnt = lpBuffer[dwYpos].dwWidth-dwXpos;
249 if(!ValidateLine(dwYpos+1,cnt)) /* allocates the buffer */
250 return FALSE; /* FIXME */
251 dst = &lpBuffer[dwYpos+1].lpLine[0];
252 memcpy(dst, src, cnt);
253 lpBuffer[dwYpos+1].dwWidth = cnt;
254 lpBuffer[dwYpos].dwWidth -= cnt;
256 /* move the cursor */
257 dwLines++;
258 dwXpos = 0;
259 dwYpos++;
261 /* update the window */
262 RenderLine(hDC, dwYpos-1);
263 RenderLine(hDC, dwYpos);
264 CalcCaretPos(hDC, dwXpos, dwYpos);
265 /* FIXME: don't use globals */
266 SetScrollRange(Globals.hMainWnd, SB_VERT, 0, dwLines, TRUE);
268 return TRUE;
272 * Attempt a basic edit buffer
274 BOOL AddCharToBuffer(HDC hDC, char ch)
276 /* we can use lpBuffer[dwYpos] */
277 if(!ValidateLine(dwYpos,0))
278 return FALSE;
280 /* shuffle the rest of the line*/
281 if(!ValidateLine(dwYpos, lpBuffer[dwYpos].dwWidth))
282 return FALSE;
283 lpBuffer[dwYpos].dwWidth++;
284 memmove(&lpBuffer[dwYpos].lpLine[dwXpos+1],
285 &lpBuffer[dwYpos].lpLine[dwXpos],
286 lpBuffer[dwYpos].dwWidth-dwXpos);
288 /* add the character */
289 lpBuffer[dwYpos].lpLine[dwXpos] = ch;
290 if(dwLines == 0)
291 dwLines++;
292 dwXpos++;
294 /* update the window and cursor position */
295 RenderLine(hDC,dwYpos);
296 CalcCaretPos(hDC,dwXpos,dwYpos);
298 return TRUE;
302 /* erase a character */
303 BOOL DoBackSpace(HDC hDC)
305 DWORD i;
307 if(lpBuffer == NULL)
308 return FALSE;
309 if(lpBuffer[dwYpos].lpLine && (dwXpos>0))
311 dwXpos --;
312 /* FIXME: use memmove */
313 for(i=dwXpos; i<(lpBuffer[dwYpos].dwWidth-1); i++)
314 lpBuffer[dwYpos].lpLine[i]=lpBuffer[dwYpos].lpLine[i+1];
316 lpBuffer[dwYpos].dwWidth--;
317 RenderLine(hDC, dwYpos);
318 CalcCaretPos(hDC,dwXpos,dwYpos);
320 else
322 /* Erase a newline. To do this we join two lines */
323 LPSTR src, dest;
324 DWORD len, oldlen;
326 if(dwYpos==0)
327 return FALSE;
329 oldlen = lpBuffer[dwYpos-1].dwWidth;
330 if(lpBuffer[dwYpos-1].lpLine&&lpBuffer[dwYpos].lpLine)
332 /* concatonate to the end of the line above line */
333 src = &lpBuffer[dwYpos].lpLine[0];
334 dest = &lpBuffer[dwYpos-1].lpLine[lpBuffer[dwYpos-1].dwWidth];
335 len = lpBuffer[dwYpos].dwWidth;
337 /* check the length of the new line */
338 if(!ValidateLine(dwYpos-1,lpBuffer[dwYpos-1].dwWidth + len))
339 return FALSE;
341 memcpy(dest,src,len);
342 lpBuffer[dwYpos-1].dwWidth+=len;
343 GlobalFree( (HGLOBAL)lpBuffer[dwYpos].lpLine);
345 else if (!lpBuffer[dwYpos-1].lpLine)
347 lpBuffer[dwYpos-1]=lpBuffer[dwYpos];
348 } /* else both are NULL */
349 RenderLine(hDC,dwYpos-1);
351 /* don't zero - it's going to get trashed anyhow */
353 /* shuffle up all the lines below this one */
354 for(i=dwYpos; i<(dwLines-1); i++)
356 lpBuffer[i] = lpBuffer[i+1];
357 RenderLine(hDC,i);
360 /* clear the last line */
361 ZeroMemory(&lpBuffer[dwLines-1],sizeof (LINE));
362 RenderLine(hDC,dwLines-1);
363 dwLines--;
365 /* adjust the cursor position to joining point */
366 dwYpos--;
367 dwXpos = oldlen;
369 CalcCaretPos(hDC,dwXpos,dwYpos);
370 SetScrollRange(Globals.hMainWnd, SB_VERT, 0, dwLines, TRUE);
372 return TRUE;
375 /* as used by File->New */
376 void TrashBuffer(void)
378 DWORD i;
380 /* variables belonging to the buffer */
381 if(lpBuffer)
383 for(i=0; i<dwLines; i++)
385 if(lpBuffer[i].lpLine)
386 GlobalFree ((HGLOBAL)lpBuffer[i].lpLine);
387 ZeroMemory(&lpBuffer[i],sizeof (LINE));
389 GlobalFree((HGLOBAL)lpBuffer);
390 lpBuffer=NULL;
392 dwLines = 0;
393 dwMaxLines = 0;
395 /* variables belonging to the view */
396 dwXpos = 0;
397 dwYpos = 0;
398 dwVOffset = 0 ;
399 /* FIXME: don't use globals */
400 SetScrollPos(Globals.hMainWnd, SB_VERT, dwVOffset, FALSE);
401 SetScrollRange(Globals.hMainWnd, SB_VERT, 0, dwLines, TRUE);
406 * Add a line to the buffer
408 /* FIXME: this breaks lines longer than BUFFERCHUNKSIZE */
409 DWORD CreateLine(
410 LPSTR buffer, /* pointer to buffer with file data */
411 DWORD size, /* number of bytes available in buffer */
412 BOOL nomore)
414 DWORD i;
416 if(size == 0)
417 return 0;
419 for(i=0; i<size; i++)
421 if(buffer[i]==0x0a)
423 if(ValidateLine(dwLines,i))
425 memcpy(&lpBuffer[dwLines].lpLine[0],&buffer[0],i);
426 lpBuffer[dwLines].dwWidth = i;
427 dwLines++;
429 return i+1;
433 /* make a line of the rest */
434 if( (i == BUFFERCHUNKSIZE) || nomore )
436 if(ValidateLine(dwLines,i))
438 memcpy(&lpBuffer[dwLines].lpLine[0],&buffer[0],i);
439 lpBuffer[dwLines].dwWidth = i;
440 dwLines++;
442 return i;
445 return 0;
450 * This is probably overcomplicated by lpBuffer data structure...
451 * Read blocks from the file, then add each line from the
452 * block to the buffer until there is none left. If all
453 * a slab isn't used, try load some more data from the file.
455 void LoadBufferFromFile(LPCSTR szFileName)
457 HANDLE hFile;
458 CHAR *pTemp;
459 DWORD size,i,len,bytes_left,bytes_read;
461 hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ, NULL,
462 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
463 if(hFile == INVALID_HANDLE_VALUE)
464 return;
465 size = BUFFERCHUNKSIZE;
466 pTemp = (LPSTR) GlobalAlloc(GMEM_FIXED, size);
467 if(!pTemp)
468 return;
469 bytes_read = 1; /* anything non-zero */
470 bytes_left = 0;
471 while(bytes_read)
473 if(!ReadFile(hFile,
474 &pTemp[bytes_left],
475 size-bytes_left,
476 &bytes_read, NULL))
477 break;
478 bytes_left+=bytes_read;
480 /* add strings to buffer */
481 for(i = 0;
482 (i<size) &&
483 (len = CreateLine(&pTemp[i], bytes_left, !bytes_read));
484 i+= len,bytes_left-=len );
486 /* move leftover to front of buffer */
487 if(bytes_left)
488 memmove(&pTemp[0],&pTemp[i], bytes_left);
490 CloseHandle(hFile);
493 BOOL DoInput(HDC hDC, WPARAM wParam, LPARAM lParam)
495 switch(wParam)
497 case 0x08:
498 return DoBackSpace(hDC);
499 case 0x0d:
500 return DoNewLine(hDC);
501 default:
502 return AddCharToBuffer(hDC,wParam);
506 BOOL GotoHome(HWND hWnd)
508 dwXpos = 0;
509 dwYpos = 0;
510 dwVOffset = 0;
511 return TRUE;
514 BOOL GotoEndOfLine(HWND hWnd)
516 dwXpos = lpBuffer[dwYpos].dwWidth;
517 return TRUE;
520 BOOL GotoDown(HWND hWnd)
522 if((dwYpos+1) >= dwLines)
524 return FALSE;
526 dwYpos++;
527 if (dwXpos>lpBuffer[dwYpos].dwWidth)
528 GotoEndOfLine(hWnd);
529 return TRUE;
532 BOOL GotoUp(HWND hWnd)
534 if(dwYpos==0)
535 return FALSE;
536 dwYpos--;
537 if (dwXpos>lpBuffer[dwYpos].dwWidth)
538 GotoEndOfLine(hWnd);
539 return TRUE;
542 BOOL GotoLeft(HWND hWnd)
544 if(dwXpos > 0)
546 dwXpos--;
547 return TRUE;
549 if(GotoUp(hWnd))
550 return GotoEndOfLine(hWnd);
551 return FALSE;
554 BOOL GotoRight(HWND hWnd)
556 if(dwXpos<lpBuffer[dwYpos].dwWidth)
558 dwXpos++;
559 return TRUE;
561 if(!GotoDown(hWnd))
562 return FALSE;
563 dwXpos=0;
564 return TRUE;
567 /* check the caret is still on the screen */
568 BOOL ScrollABit(HWND hWnd)
570 if(dwYpos<dwVOffset)
572 dwVOffset = dwYpos;
573 return TRUE;
575 if(dwYpos>(dwVOffset+GetLinesPerPage(hWnd)))
577 dwVOffset = dwYpos - GetLinesPerPage(hWnd) + 1;
578 return TRUE;
580 return FALSE;
583 /* FIXME: move the window around so we can still see the caret */
584 VOID DoEdit(HWND hWnd, WPARAM wParam, LPARAM lParam)
586 HDC hDC;
588 if(lpBuffer==NULL)
589 return;
590 switch(wParam)
592 case VK_HOME: GotoHome(hWnd);
593 break;
595 case VK_END: GotoEndOfLine(hWnd);
596 break;
598 case VK_LEFT: GotoLeft(hWnd);
599 break;
601 case VK_RIGHT: GotoRight(hWnd);
602 break;
604 case VK_DOWN: GotoDown(hWnd);
605 break;
607 case VK_UP: GotoUp(hWnd);
608 break;
610 default:
611 return;
614 hDC = GetDC(hWnd);
615 if(hDC)
617 CalcCaretPos(hDC, dwXpos, dwYpos);
618 ReleaseDC(hWnd,hDC);
620 if(ScrollABit(hWnd))
621 InvalidateRect(hWnd, NULL, FALSE);
624 void ButtonDownToCaretPos(HWND hWnd, WPARAM wParam, LPARAM lParam)
626 DWORD x, y, caretx, carety;
627 BOOL refine_guess = TRUE;
628 HDC hDC;
630 x = LOWORD(lParam);
631 y = HIWORD(lParam);
633 caretx = x/tm.tmAveCharWidth; /* guess */
634 carety = dwVOffset + y/tm.tmHeight;
636 hDC = GetDC(hWnd);
638 if(lpBuffer == NULL)
640 caretx = 0;
641 carety = 0;
642 refine_guess = FALSE;
645 /* if the cursor is past the bottom, put it after the last char */
646 if(refine_guess && (carety>=dwLines) )
648 carety=dwLines-1;
649 caretx=lpBuffer[carety].dwWidth;
650 refine_guess = FALSE;
653 /* cursor past end of line? */
654 if(refine_guess && (x>CalcStringWidth(hDC,lpBuffer[carety].dwWidth,carety)))
656 caretx = lpBuffer[carety].dwWidth;
657 refine_guess = FALSE;
660 /* FIXME: doesn't round properly */
661 if(refine_guess)
663 if(CalcStringWidth(hDC,caretx,carety)<x)
665 while( (caretx<lpBuffer[carety].dwWidth) &&
666 (CalcStringWidth(hDC,caretx+1,carety)<x))
667 caretx++;
669 else
671 while((caretx>0)&&(CalcStringWidth(hDC,caretx-1,carety)>x))
672 caretx--;
676 /* set the caret's position */
677 dwXpos = caretx;
678 dwYpos = carety;
679 CalcCaretPos(hDC, caretx, carety);
680 ReleaseDC(hWnd,hDC);
683 void DoScroll(HWND hWnd, WPARAM wParam, LPARAM lParam)
685 DWORD dy = GetLinesPerPage(hWnd);
687 switch(wParam) /* vscroll code */
689 case SB_LINEUP:
690 if(dwVOffset)
691 dwVOffset--;
692 break;
693 case SB_LINEDOWN:
694 if(dwVOffset<dwLines)
695 dwVOffset++;
696 break;
697 case SB_PAGEUP:
698 if( (dy+dwVOffset) > dwLines)
699 dwVOffset = dwLines - 1;
700 break;
701 case SB_PAGEDOWN:
702 if( dy > dwVOffset)
703 dwVOffset=0;
704 break;
706 /* position scroll */
707 SetScrollPos(hWnd, SB_VERT, dwVOffset, TRUE);
710 /***********************************************************************
712 * NOTEPAD_MenuCommand
714 * All handling of main menu events
717 int NOTEPAD_MenuCommand (WPARAM wParam)
719 switch (wParam) {
720 case 0x100: DIALOG_FileNew(); break;
721 case 0x101: DIALOG_FileOpen(); break;
722 case 0x102: DIALOG_FileSave(); break;
723 case 0x103: DIALOG_FileSaveAs(); break;
724 case 0x104: DIALOG_FilePrint(); break;
725 case 0x105: DIALOG_FilePageSetup(); break;
726 case 0x106: DIALOG_FilePrinterSetup();break;
727 case 0x108: DIALOG_FileExit(); break;
729 case 0x110: DIALOG_EditUndo(); break;
730 case 0x111: DIALOG_EditCut(); break;
731 case 0x112: DIALOG_EditCopy(); break;
732 case 0x113: DIALOG_EditPaste(); break;
733 case 0x114: DIALOG_EditDelete(); break;
734 case 0x116: DIALOG_EditSelectAll(); break;
735 case 0x117: DIALOG_EditTimeDate();break;
736 case 0x119: DIALOG_EditWrap(); break;
738 case 0x120: DIALOG_Search(); break;
739 case 0x121: DIALOG_SearchNext(); break;
741 case 0x130: DIALOG_HelpContents(); break;
742 case 0x131: DIALOG_HelpSearch(); break;
743 case 0x132: DIALOG_HelpHelp(); break;
744 case 0x135: DIALOG_HelpLicense(); break;
745 case 0x136: DIALOG_HelpNoWarranty(); break;
746 case 0x137: DIALOG_HelpAboutWine(); break;
748 // default:
749 // LANGUAGE_DefaultHandle(wParam);
751 return 0;
756 /***********************************************************************
758 * NOTEPAD_WndProc
761 LRESULT WINAPI NOTEPAD_WndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
763 PAINTSTRUCT ps;
764 HDC hContext;
765 HANDLE hDrop; /* drag & drop */
766 CHAR szFileName[MAX_STRING_LEN];
767 RECT Windowsize;
769 lstrcpy(szFileName, "");
771 switch (msg) {
773 case WM_CREATE:
774 GetClientRect(hWnd, &rectClient);
775 InitFontInfo(hWnd);
776 break;
778 case WM_SETFOCUS:
779 CreateCaret(Globals.hMainWnd, 0, 1, tm.tmHeight);
780 SetCaretPos(dwCaretXpos, dwCaretYpos);
781 ShowCaret(Globals.hMainWnd);
782 break;
784 case WM_KILLFOCUS:
785 DestroyCaret();
786 break;
788 case WM_PAINT:
789 GetClientRect(hWnd, &rectClient);
790 hContext = BeginPaint(hWnd, &ps);
791 RenderWindow(hContext);
792 EndPaint(hWnd, &ps);
793 break;
795 case WM_KEYDOWN:
796 DoEdit(hWnd, wParam, lParam);
797 break;
799 case WM_CHAR:
800 GetClientRect(hWnd, &rectClient);
801 HideCaret(hWnd);
802 hContext = GetDC(hWnd);
803 DoInput(hContext,wParam,lParam);
804 ReleaseDC(hWnd,hContext);
805 ShowCaret(hWnd);
806 break;
808 case WM_LBUTTONDOWN:
809 /* figure out where the mouse was clicked */
810 ButtonDownToCaretPos(hWnd, wParam, lParam);
811 break;
813 case WM_VSCROLL:
814 DoScroll(hWnd, wParam, lParam);
815 InvalidateRect(hWnd, NULL, FALSE); /* force a redraw */
816 break;
818 case WM_COMMAND:
819 /* FIXME: this is a bit messy */
820 NOTEPAD_MenuCommand(wParam);
821 InvalidateRect(hWnd, NULL, FALSE); /* force a redraw */
822 hContext = GetDC(hWnd);
823 CalcCaretPos(hContext,dwXpos,dwYpos);
824 ReleaseDC(hWnd,hContext);
825 break;
827 case WM_DESTROYCLIPBOARD:
828 MessageBox(Globals.hMainWnd, "Empty clipboard", "Debug", MB_ICONEXCLAMATION);
829 break;
831 case WM_CLOSE:
832 if (DoCloseFile()) {
833 PostQuitMessage(0);
835 break;
837 case WM_DESTROY:
838 PostQuitMessage (0);
839 break;
841 case WM_SIZE:
842 GetClientRect(Globals.hMainWnd, &Windowsize);
843 break;
845 case WM_DROPFILES:
846 /* User has dropped a file into main window */
847 hDrop = (HANDLE) wParam;
848 DragQueryFile(hDrop, 0, (CHAR *) &szFileName, sizeof(szFileName));
849 DragFinish(hDrop);
850 DoOpenFile(szFileName);
851 break;
853 default:
854 return DefWindowProc (hWnd, msg, wParam, lParam);
856 return 0l;
859 int AlertFileDoesNotExist(LPSTR szFileName) {
861 int nResult;
862 CHAR szMessage[MAX_STRING_LEN];
863 CHAR szRessource[MAX_STRING_LEN];
865 LoadString(Globals.hInstance, STRING_DOESNOTEXIST, szRessource,
866 sizeof(szRessource));
867 wsprintf(szMessage, szRessource, szFileName);
869 LoadString(Globals.hInstance, STRING_ERROR, szRessource, sizeof(szRessource));
871 nResult = MessageBox(Globals.hMainWnd, szMessage, szRessource,
872 MB_ICONEXCLAMATION | MB_YESNO);
874 return(nResult);
877 void HandleCommandLine(LPSTR cmdline)
880 while (*cmdline && (*cmdline == ' ' || *cmdline == '-'))
883 CHAR option;
885 if (*cmdline++ == ' ') continue;
887 option = *cmdline;
888 if (option) cmdline++;
889 while (*cmdline && *cmdline == ' ') cmdline++;
891 switch(option)
893 case 'p':
894 case 'P': printf("Print file: ");
895 /* Not yet able to print a file */
896 break;
900 if (*cmdline)
902 /* file name is passed in the command line */
903 char *file_name;
904 BOOL file_exists;
905 char buf[MAX_PATH];
907 if (FileExists(cmdline))
909 file_exists = TRUE;
910 file_name = cmdline;
912 else
914 /* try to find file with ".txt" extention */
915 if (!strcmp(".txt", cmdline + strlen(cmdline) - strlen(".txt")))
917 file_exists = FALSE;
918 file_name = cmdline;
920 else
922 strncpy(buf, cmdline, MAX_PATH - strlen(".txt") - 1);
923 strcat(buf, ".txt");
924 file_name = buf;
925 file_exists = FileExists(buf);
929 if (file_exists)
931 DoOpenFile(file_name);
932 InvalidateRect(Globals.hMainWnd, NULL, FALSE);
934 else
936 switch (AlertFileDoesNotExist(file_name)) {
937 case IDYES:
938 DoOpenFile(file_name);
939 break;
941 case IDNO:
942 break;
949 /***********************************************************************
951 * WinMain
954 int PASCAL WinMain (HINSTANCE hInstance, HINSTANCE prev, LPSTR cmdline, int show)
956 MSG msg;
957 WNDCLASS class;
958 char className[] = "NPClass"; /* To make sure className >= 0x10000 */
959 char winName[] = "Notepad";
961 /* setup buffer */
962 InitBuffer();
964 /* Setup Globals */
966 Globals.lpszIniFile = "notepad.ini";
967 Globals.lpszIcoFile = "notepad.ico";
969 Globals.hInstance = hInstance;
971 #ifndef LCC
972 Globals.hMainIcon = ExtractIcon(Globals.hInstance,
973 Globals.lpszIcoFile, 0);
974 #endif
975 if (!Globals.hMainIcon) {
976 Globals.hMainIcon = LoadIcon(0, MAKEINTRESOURCE(DEFAULTICON));
979 lstrcpy(Globals.szFindText, "");
980 lstrcpy(Globals.szFileName, "");
981 lstrcpy(Globals.szMarginTop, "25 mm");
982 lstrcpy(Globals.szMarginBottom, "25 mm");
983 lstrcpy(Globals.szMarginLeft, "20 mm");
984 lstrcpy(Globals.szMarginRight, "20 mm");
985 lstrcpy(Globals.szHeader, "&n");
986 lstrcpy(Globals.szFooter, "Page &s");
987 lstrcpy(Globals.Buffer, "Hello World");
989 if (!prev){
990 class.style = CS_HREDRAW | CS_VREDRAW;
991 class.lpfnWndProc = NOTEPAD_WndProc;
992 class.cbClsExtra = 0;
993 class.cbWndExtra = 0;
994 class.hInstance = Globals.hInstance;
995 class.hIcon = LoadIcon (0, IDI_APPLICATION);
996 class.hCursor = LoadCursor (0, IDC_ARROW);
997 class.hbrBackground = GetStockObject (WHITE_BRUSH);
998 class.lpszMenuName = 0;
999 class.lpszClassName = className;
1002 if (!RegisterClass (&class)) return FALSE;
1004 /* Setup windows */
1007 Globals.hMainWnd = CreateWindow (className, winName,
1008 WS_OVERLAPPEDWINDOW + WS_HSCROLL + WS_VSCROLL,
1009 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 0,
1010 LoadMenu(Globals.hInstance, STRING_MENU_Xx),
1011 Globals.hInstance, 0);
1013 Globals.hFindReplaceDlg = 0;
1015 LANGUAGE_LoadMenus();
1017 SetMenu(Globals.hMainWnd, Globals.hMainMenu);
1019 ShowWindow (Globals.hMainWnd, show);
1020 UpdateWindow (Globals.hMainWnd);
1022 /* Set up dialogs */
1024 /* Identify Messages originating from FindReplace */
1026 Globals.nCommdlgFindReplaceMsg = RegisterWindowMessage("commdlg_FindReplace");
1027 if (Globals.nCommdlgFindReplaceMsg==0) {
1028 MessageBox(Globals.hMainWnd, "Could not register commdlg_FindReplace window message",
1029 "Error", MB_ICONEXCLAMATION);
1032 HandleCommandLine(cmdline);
1034 /* Set up Drag&Drop */
1036 DragAcceptFiles(Globals.hMainWnd, TRUE);
1038 /* now enter mesage loop */
1040 while (GetMessage (&msg, 0, 0, 0)) {
1041 if (IsDialogMessage(Globals.hFindReplaceDlg, &msg)!=0) {
1042 /* Message belongs to FindReplace dialog */
1043 /* We just let IsDialogMessage handle it */
1045 else
1047 /* Message belongs to the Notepad Main Window */
1048 TranslateMessage (&msg);
1049 DispatchMessage (&msg);
1052 return 0;