Added regedit unit test, a couple minor changes to regedit.
[wine] / programs / notepad / main.c
1 /*
2  *  Notepad
3  *
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>
7  *
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.
12  *
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.
17  *
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
21  *
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
34  *
35  * This program is intended as a testbed for winelib as much as
36  * a useful application.
37  */
38
39 #include <stdio.h>
40 #include "windows.h"
41
42 #include "main.h"
43 #include "license.h"
44 #include "dialog.h"
45 #include "language.h"
46
47 extern BOOL FileExists(LPCSTR szFilename);
48 extern BOOL DoCloseFile(void);
49 extern void DoOpenFile(LPCSTR szFileName);
50
51 NOTEPAD_GLOBALS Globals;
52
53
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 */
58
59 #define BUFFERCHUNKSIZE 0xe0
60 typedef struct TAGLine {
61     LPSTR lpLine;
62     DWORD dwWidth;
63     DWORD dwMaxWidth;
64 } LINE, *LPLINE;
65
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 */
77
78 VOID InitFontInfo(HWND hWnd)
79 {
80     HDC hDC = GetDC(hWnd);
81
82     if(hDC)
83     {
84         GetTextMetrics(hDC, &tm);
85         ReleaseDC(hWnd,hDC);
86     }
87 }
88
89 void InitBuffer(void)
90 {
91     lpBuffer = NULL;
92     dwLines = 0;
93     dwMaxLines = 0;
94     dwXpos=0;
95     dwYpos=0;
96 }
97
98 /* convert x,y character co-ords into x pixel co-ord */
99 DWORD CalcStringWidth(HDC hDC, DWORD x, DWORD y)
100 {
101     DWORD len;
102     SIZE size;
103
104     size.cx = 0;
105     size.cy = 0;
106
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);
116
117     return size.cx;
118 }
119
120 void CalcCaretPos(HDC hDC, DWORD dwXpos, DWORD dwYpos)
121 {
122     dwCaretXpos = CalcStringWidth(hDC, dwXpos, dwYpos);
123     dwCaretYpos = tm.tmHeight*(dwYpos-dwVOffset);
124     SetCaretPos(dwCaretXpos,dwCaretYpos);
125 }
126
127 DWORD GetLinesPerPage(HWND hWnd)
128 {
129     return (rectClient.bottom/tm.tmHeight); /* round down */
130 }
131
132 /* render one line of text and blank space */
133 void RenderLine(HDC hDC, DWORD lineno)
134 {
135     RECT rect;
136     HBRUSH hPrev;
137
138     if(!hDC)
139         return;
140
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);
144
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;
150
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);
155
156     if(lpBuffer && lpBuffer[lineno].lpLine)
157     {
158         TextOut(hDC, 0, rect.top, lpBuffer[lineno].lpLine,
159                        lpBuffer[lineno].dwWidth);
160     }
161 }
162
163 /*
164  * Paint the buffer onto the window.
165  */
166 void RenderWindow(HDC hDC) {
167     DWORD i;
168
169     if(!hDC)
170         return;
171
172     /* FIXME: render only necessary lines */
173     for(i = dwVOffset; i < (dwVOffset+GetLinesPerPage(0)); i++)
174     {
175         RenderLine(hDC,i);
176     }
177 }
178
179 /*
180  * Check that correct buffers exist to access line y pos x
181  * Only manages memory.
182  *
183  * Returns TRUE if the line is accessable
184  *         FALSE if there is a problem
185  */
186 BOOL ValidateLine(DWORD y,DWORD x)
187 {
188     DWORD max;
189
190     /* check to see that the BUFFER has enough lines */
191     max = dwMaxLines;
192     if( (max<=y) || (lpBuffer == NULL))
193     {
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;
206     }
207
208     /* check to see that the LINE is wide enough */
209     max = lpBuffer[y].dwMaxWidth;
210     if( (max <= x) || (lpBuffer[y].lpLine == NULL) )
211     {
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;
224     }
225     return TRUE;
226 }
227
228 /* inserts a new line into the buffer */
229 BOOL DoNewLine(HDC hDC)
230 {
231     DWORD i,cnt;
232     LPSTR src,dst;
233
234     /* check to see if we need more memory for the buffer pointers */
235     if(!ValidateLine(dwLines,0))
236         return FALSE;
237
238     /* shuffle up all the lines */
239     for(i=dwLines; i>(dwYpos+1); i--)
240     {
241         lpBuffer[i] = lpBuffer[i-1];
242         RenderLine(hDC,i);
243     }
244     ZeroMemory(&lpBuffer[dwYpos+1],sizeof(LINE));
245
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;
255
256     /* move the cursor */
257     dwLines++;
258     dwXpos = 0;
259     dwYpos++;
260
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);
267
268     return TRUE;
269 }
270
271 /*
272  * Attempt a basic edit buffer
273  */
274 BOOL AddCharToBuffer(HDC hDC, char ch)
275 {
276     /* we can use lpBuffer[dwYpos] */
277     if(!ValidateLine(dwYpos,0))
278         return FALSE;
279
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);
287
288     /* add the character */
289     lpBuffer[dwYpos].lpLine[dwXpos] = ch;
290     if(dwLines == 0)
291         dwLines++;
292     dwXpos++;
293
294     /* update the window and cursor position */
295     RenderLine(hDC,dwYpos);
296     CalcCaretPos(hDC,dwXpos,dwYpos);
297
298     return TRUE;
299 }
300
301
302 /* erase a character */
303 BOOL DoBackSpace(HDC hDC)
304 {
305     DWORD i;
306
307     if(lpBuffer == NULL)
308         return FALSE;
309     if(lpBuffer[dwYpos].lpLine && (dwXpos>0))
310     {
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];
315
316         lpBuffer[dwYpos].dwWidth--;
317         RenderLine(hDC, dwYpos);
318         CalcCaretPos(hDC,dwXpos,dwYpos);
319     }
320     else
321     {
322         /* Erase a newline. To do this we join two lines */
323         LPSTR src, dest;
324         DWORD len, oldlen;
325
326         if(dwYpos==0)
327             return FALSE;
328
329         oldlen = lpBuffer[dwYpos-1].dwWidth;
330         if(lpBuffer[dwYpos-1].lpLine&&lpBuffer[dwYpos].lpLine)
331         {
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;
336
337             /* check the length of the new line */
338             if(!ValidateLine(dwYpos-1,lpBuffer[dwYpos-1].dwWidth + len))
339                 return FALSE;
340
341             memcpy(dest,src,len);
342             lpBuffer[dwYpos-1].dwWidth+=len;
343             GlobalFree( (HGLOBAL)lpBuffer[dwYpos].lpLine);
344         }
345         else if (!lpBuffer[dwYpos-1].lpLine)
346         {
347             lpBuffer[dwYpos-1]=lpBuffer[dwYpos];
348         } /* else both are NULL */
349         RenderLine(hDC,dwYpos-1);
350
351         /* don't zero - it's going to get trashed anyhow */
352
353         /* shuffle up all the lines below this one */
354         for(i=dwYpos; i<(dwLines-1); i++)
355         {
356             lpBuffer[i] = lpBuffer[i+1];
357             RenderLine(hDC,i);
358         }
359
360         /* clear the last line */
361         ZeroMemory(&lpBuffer[dwLines-1],sizeof (LINE));
362         RenderLine(hDC,dwLines-1);
363         dwLines--;
364
365         /* adjust the cursor position to joining point */
366         dwYpos--;
367         dwXpos = oldlen;
368
369         CalcCaretPos(hDC,dwXpos,dwYpos);
370         SetScrollRange(Globals.hMainWnd, SB_VERT, 0, dwLines, TRUE);
371     }
372     return TRUE;
373 }
374
375 /* as used by File->New */
376 void TrashBuffer(void)
377 {
378     DWORD i;
379
380     /* variables belonging to the buffer */
381     if(lpBuffer)
382     {
383         for(i=0; i<dwLines; i++)
384         {
385             if(lpBuffer[i].lpLine)
386                 GlobalFree ((HGLOBAL)lpBuffer[i].lpLine);
387             ZeroMemory(&lpBuffer[i],sizeof (LINE));
388         }
389         GlobalFree((HGLOBAL)lpBuffer);
390         lpBuffer=NULL;
391     }
392     dwLines = 0;
393     dwMaxLines = 0;
394
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);
402 }
403
404
405 /*
406  * Add a line to the buffer
407  */
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)
413 {
414     DWORD i;
415
416     if(size == 0)
417         return 0;
418
419     for(i=0; i<size; i++)
420     {
421         if(buffer[i]==0x0a)
422         {
423             if(ValidateLine(dwLines,i))
424             {
425                 memcpy(&lpBuffer[dwLines].lpLine[0],&buffer[0],i);
426                 lpBuffer[dwLines].dwWidth = i;
427                 dwLines++;
428             }
429             return i+1;
430         }
431     }
432
433     /* make a line of the rest */
434     if( (i == BUFFERCHUNKSIZE) || nomore )
435     {
436         if(ValidateLine(dwLines,i))
437         {
438             memcpy(&lpBuffer[dwLines].lpLine[0],&buffer[0],i);
439             lpBuffer[dwLines].dwWidth = i;
440             dwLines++;
441         }
442         return i;
443     }
444
445     return 0;
446 }
447
448
449 /*
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.
454  */
455 void LoadBufferFromFile(LPCSTR szFileName)
456 {
457     HANDLE hFile;
458     CHAR *pTemp;
459     DWORD size,i,len,bytes_left,bytes_read;
460
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)
472     {
473         if(!ReadFile(hFile,
474                      &pTemp[bytes_left],
475                      size-bytes_left,
476                      &bytes_read, NULL))
477             break;
478         bytes_left+=bytes_read;
479
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 );
485
486         /* move leftover to front of buffer */
487         if(bytes_left)
488             memmove(&pTemp[0],&pTemp[i], bytes_left);
489     }
490     CloseHandle(hFile);
491 }
492
493 BOOL DoInput(HDC hDC, WPARAM wParam, LPARAM lParam)
494 {
495     switch(wParam)
496     {
497     case 0x08:
498         return DoBackSpace(hDC);
499     case 0x0d:
500         return DoNewLine(hDC);
501     default:
502         return AddCharToBuffer(hDC,wParam);
503     }
504 }
505
506 BOOL GotoHome(HWND hWnd)
507 {
508     dwXpos = 0;
509     dwYpos = 0;
510     dwVOffset = 0;
511     return TRUE;
512 }
513
514 BOOL GotoEndOfLine(HWND hWnd)
515 {
516     dwXpos = lpBuffer[dwYpos].dwWidth;
517     return TRUE;
518 }
519
520 BOOL GotoDown(HWND hWnd)
521 {
522     if((dwYpos+1) >= dwLines)
523     {
524         return FALSE;
525     }
526     dwYpos++;
527     if (dwXpos>lpBuffer[dwYpos].dwWidth)
528         GotoEndOfLine(hWnd);
529     return TRUE;
530 }
531
532 BOOL GotoUp(HWND hWnd)
533 {
534     if(dwYpos==0)
535         return FALSE;
536     dwYpos--;
537     if (dwXpos>lpBuffer[dwYpos].dwWidth)
538         GotoEndOfLine(hWnd);
539     return TRUE;
540 }
541
542 BOOL GotoLeft(HWND hWnd)
543 {
544     if(dwXpos > 0)
545     {
546         dwXpos--;
547         return TRUE;
548     }
549     if(GotoUp(hWnd))
550         return GotoEndOfLine(hWnd);
551     return FALSE;
552 }
553
554 BOOL GotoRight(HWND hWnd)
555 {
556     if(dwXpos<lpBuffer[dwYpos].dwWidth)
557     {
558         dwXpos++;
559         return TRUE;
560     }
561     if(!GotoDown(hWnd))
562         return FALSE;
563     dwXpos=0;
564     return TRUE;
565 }
566
567 /* check the caret is still on the screen */
568 BOOL ScrollABit(HWND hWnd)
569 {
570     if(dwYpos<dwVOffset)
571     {
572         dwVOffset = dwYpos;
573         return TRUE;
574     }
575     if(dwYpos>(dwVOffset+GetLinesPerPage(hWnd)))
576     {
577         dwVOffset = dwYpos - GetLinesPerPage(hWnd) + 1;
578         return TRUE;
579     }
580     return FALSE;
581 }
582
583 /* FIXME: move the window around so we can still see the caret */
584 VOID DoEdit(HWND hWnd, WPARAM wParam, LPARAM lParam)
585 {
586     HDC hDC;
587
588     if(lpBuffer==NULL)
589         return;
590     switch(wParam)
591     {
592     case VK_HOME: GotoHome(hWnd);
593         break;
594
595     case VK_END: GotoEndOfLine(hWnd);
596         break;
597
598     case VK_LEFT: GotoLeft(hWnd);
599         break;
600
601     case VK_RIGHT: GotoRight(hWnd);
602         break;
603
604     case VK_DOWN: GotoDown(hWnd);
605         break;
606
607     case VK_UP: GotoUp(hWnd);
608         break;
609
610     default:
611         return;
612     }
613
614     hDC = GetDC(hWnd);
615     if(hDC)
616     {
617         CalcCaretPos(hDC, dwXpos, dwYpos);
618         ReleaseDC(hWnd,hDC);
619     }
620     if(ScrollABit(hWnd))
621         InvalidateRect(hWnd, NULL, FALSE);
622 }
623
624 void ButtonDownToCaretPos(HWND hWnd, WPARAM wParam, LPARAM lParam)
625 {
626     DWORD x, y, caretx, carety;
627     BOOL refine_guess = TRUE;
628     HDC hDC;
629
630     x = LOWORD(lParam);
631     y = HIWORD(lParam);
632
633     caretx = x/tm.tmAveCharWidth; /* guess */
634     carety = dwVOffset + y/tm.tmHeight;
635
636     hDC = GetDC(hWnd);
637
638     if(lpBuffer == NULL)
639     {
640         caretx = 0;
641         carety = 0;
642         refine_guess = FALSE;
643     }
644
645     /* if the cursor is past the bottom, put it after the last char */
646     if(refine_guess && (carety>=dwLines) )
647     {
648         carety=dwLines-1;
649         caretx=lpBuffer[carety].dwWidth;
650         refine_guess = FALSE;
651     }
652
653     /* cursor past end of line? */
654     if(refine_guess && (x>CalcStringWidth(hDC,lpBuffer[carety].dwWidth,carety)))
655     {
656         caretx = lpBuffer[carety].dwWidth;
657         refine_guess = FALSE;
658     }
659
660     /* FIXME: doesn't round properly */
661     if(refine_guess)
662     {
663         if(CalcStringWidth(hDC,caretx,carety)<x)
664         {
665             while( (caretx<lpBuffer[carety].dwWidth) &&
666                    (CalcStringWidth(hDC,caretx+1,carety)<x))
667                 caretx++;
668         }
669         else
670         {
671             while((caretx>0)&&(CalcStringWidth(hDC,caretx-1,carety)>x))
672                 caretx--;
673         }
674     }
675
676     /* set the caret's position */
677     dwXpos = caretx;
678     dwYpos = carety;
679     CalcCaretPos(hDC, caretx, carety);
680     ReleaseDC(hWnd,hDC);
681 }
682
683 void DoScroll(HWND hWnd, WPARAM wParam, LPARAM lParam)
684 {
685     DWORD dy = GetLinesPerPage(hWnd);
686
687     switch(wParam) /* vscroll code */
688     {
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;
705     }
706     /* position scroll */
707     SetScrollPos(hWnd, SB_VERT, dwVOffset, TRUE);
708 }
709
710 /***********************************************************************
711  *
712  *           NOTEPAD_MenuCommand
713  *
714  *  All handling of main menu events
715  */
716
717 int NOTEPAD_MenuCommand (WPARAM wParam)
718 {
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;
728
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;
737
738      case 0x120:     DIALOG_Search(); break;
739      case 0x121:       DIALOG_SearchNext(); break;
740
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;
747
748 //     default:
749 //      LANGUAGE_DefaultHandle(wParam);
750    }
751    return 0;
752 }
753
754
755
756 /***********************************************************************
757  *
758  *           NOTEPAD_WndProc
759  */
760
761 LRESULT WINAPI NOTEPAD_WndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
762 {
763     PAINTSTRUCT ps;
764     HDC hContext;
765     HANDLE hDrop;                      /* drag & drop */
766     CHAR szFileName[MAX_STRING_LEN];
767     RECT Windowsize;
768
769     lstrcpy(szFileName, "");
770
771     switch (msg) {
772
773        case WM_CREATE:
774           GetClientRect(hWnd, &rectClient);
775           InitFontInfo(hWnd);
776           break;
777
778        case WM_SETFOCUS:
779           CreateCaret(Globals.hMainWnd, 0, 1, tm.tmHeight);
780           SetCaretPos(dwCaretXpos, dwCaretYpos);
781           ShowCaret(Globals.hMainWnd);
782           break;
783
784        case WM_KILLFOCUS:
785           DestroyCaret();
786           break;
787
788        case WM_PAINT:
789           GetClientRect(hWnd, &rectClient);
790           hContext = BeginPaint(hWnd, &ps);
791           RenderWindow(hContext);
792           EndPaint(hWnd, &ps);
793         break;
794
795        case WM_KEYDOWN:
796           DoEdit(hWnd, wParam, lParam);
797           break;
798
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;
807
808        case WM_LBUTTONDOWN:
809           /* figure out where the mouse was clicked */
810           ButtonDownToCaretPos(hWnd, wParam, lParam);
811           break;
812
813        case WM_VSCROLL:
814           DoScroll(hWnd, wParam, lParam);
815           InvalidateRect(hWnd, NULL, FALSE); /* force a redraw */
816           break;
817
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;
826
827        case WM_DESTROYCLIPBOARD:
828           MessageBox(Globals.hMainWnd, "Empty clipboard", "Debug", MB_ICONEXCLAMATION);
829           break;
830
831        case WM_CLOSE:
832           if (DoCloseFile()) {
833              PostQuitMessage(0);
834           }
835           break;
836
837        case WM_DESTROY:
838              PostQuitMessage (0);
839           break;
840
841        case WM_SIZE:
842           GetClientRect(Globals.hMainWnd, &Windowsize);
843           break;
844
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;
852
853        default:
854           return DefWindowProc (hWnd, msg, wParam, lParam);
855     }
856     return 0l;
857 }
858
859 int AlertFileDoesNotExist(LPSTR szFileName) {
860
861    int nResult;
862    CHAR szMessage[MAX_STRING_LEN];
863    CHAR szRessource[MAX_STRING_LEN];
864
865    LoadString(Globals.hInstance, STRING_DOESNOTEXIST, szRessource,
866               sizeof(szRessource));
867    wsprintf(szMessage, szRessource, szFileName);
868
869    LoadString(Globals.hInstance, STRING_ERROR,  szRessource, sizeof(szRessource));
870
871    nResult = MessageBox(Globals.hMainWnd, szMessage, szRessource,
872                         MB_ICONEXCLAMATION | MB_YESNO);
873
874    return(nResult);
875 }
876
877 void HandleCommandLine(LPSTR cmdline)
878 {
879
880     while (*cmdline && (*cmdline == ' ' || *cmdline == '-'))
881
882     {
883         CHAR   option;
884
885         if (*cmdline++ == ' ') continue;
886
887         option = *cmdline;
888         if (option) cmdline++;
889         while (*cmdline && *cmdline == ' ') cmdline++;
890
891         switch(option)
892         {
893             case 'p':
894             case 'P': printf("Print file: ");
895                       /* Not yet able to print a file */
896                       break;
897         }
898     }
899
900     if (*cmdline)
901     {
902         /* file name is passed in the command line */
903         char *file_name;
904         BOOL file_exists;
905         char buf[MAX_PATH];
906
907         if (FileExists(cmdline))
908         {
909             file_exists = TRUE;
910             file_name = cmdline;
911         }
912         else
913         {
914             /* try to find file with ".txt" extention */
915             if (!strcmp(".txt", cmdline + strlen(cmdline) - strlen(".txt")))
916             {
917                 file_exists = FALSE;
918                 file_name = cmdline;
919             }
920             else
921             {
922                 strncpy(buf, cmdline, MAX_PATH - strlen(".txt") - 1);
923                 strcat(buf, ".txt");
924                 file_name = buf;
925                 file_exists = FileExists(buf);
926             }
927         }
928
929         if (file_exists)
930         {
931             DoOpenFile(file_name);
932             InvalidateRect(Globals.hMainWnd, NULL, FALSE);
933         }
934         else
935         {
936             switch (AlertFileDoesNotExist(file_name)) {
937             case IDYES:
938                 DoOpenFile(file_name);
939                 break;
940
941             case IDNO:
942                 break;
943             }
944         }
945      }
946 }
947
948
949 /***********************************************************************
950  *
951  *           WinMain
952  */
953
954 int PASCAL WinMain (HINSTANCE hInstance, HINSTANCE prev, LPSTR cmdline, int show)
955 {
956     MSG      msg;
957     WNDCLASS class;
958     char className[] = "NPClass";  /* To make sure className >= 0x10000 */
959     char winName[]   = "Notepad";
960
961     /* setup buffer */
962     InitBuffer();
963
964     /* Setup Globals */
965
966     Globals.lpszIniFile = "notepad.ini";
967     Globals.lpszIcoFile = "notepad.ico";
968
969     Globals.hInstance       = hInstance;
970
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));
977     }
978
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");
988
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;
1000     }
1001
1002     if (!RegisterClass (&class)) return FALSE;
1003
1004     /* Setup windows */
1005
1006
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);
1012
1013     Globals.hFindReplaceDlg = 0;
1014
1015    LANGUAGE_LoadMenus();
1016
1017     SetMenu(Globals.hMainWnd, Globals.hMainMenu);
1018
1019     ShowWindow (Globals.hMainWnd, show);
1020     UpdateWindow (Globals.hMainWnd);
1021
1022     /* Set up dialogs */
1023
1024     /* Identify Messages originating from FindReplace */
1025
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);
1030     }
1031
1032     HandleCommandLine(cmdline);
1033
1034     /* Set up Drag&Drop */
1035
1036     DragAcceptFiles(Globals.hMainWnd, TRUE);
1037
1038     /* now enter mesage loop     */
1039
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 */
1044         }
1045           else
1046         {
1047           /* Message belongs to the Notepad Main Window */
1048           TranslateMessage (&msg);
1049           DispatchMessage (&msg);
1050         }
1051     }
1052     return 0;
1053 }