Implemented parsing of file name, passed in command line, loading new
[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  *  To be distributed under the Wine License
7  *
8  *  FIXME,TODO list:
9  *  - Use wine Heap instead of malloc/free (done)
10  *  - use scroll bars (vertical done)
11  *  - cut 'n paste (clipboard)
12  *  - save file
13  *  - print file
14  *  - find dialog
15  *  - encapsulate data structures (?) - half done
16  *  - free unused memory
17  *  - solve Open problems
18  *  - smoother scrolling
19  *  - separate view code and document code
20  *
21  * This program is intended as a testbed for winelib as much as
22  * a useful application.
23  */
24
25 #include <stdio.h>
26 #include "windows.h"
27
28 #ifdef LCC
29 #include "lcc.h"
30 #endif
31
32 #include "main.h"
33 #include "license.h"
34 #include "dialog.h"
35 #include "language.h"
36
37 extern BOOL FileExists(LPCSTR szFilename);
38 extern BOOL DoCloseFile(void);
39 extern void DoOpenFile(LPCSTR szFileName);
40
41 NOTEPAD_GLOBALS Globals;
42
43
44 /* Using a pointer to pointer data structure to
45    achieve a little more efficiency. Hopefully
46    it will be worth it, because it complicates the
47    code - mjm 26 Jun 2000 */
48
49 #define BUFFERCHUNKSIZE 0xe0
50 typedef struct TAGLine {
51     LPSTR lpLine;
52     DWORD dwWidth;
53     DWORD dwMaxWidth;
54 } LINE, *LPLINE;
55
56 /* FIXME: make this info into a structure */
57 /* typedef struct tagBUFFER { */
58 DWORD dwVOffset=0;
59 DWORD dwLines=0;
60 DWORD dwMaxLines=0;
61 LPLINE lpBuffer=NULL;
62 DWORD dwXpos=0,dwYpos=0;        /* position of caret in char coords */
63 DWORD dwCaretXpos=0,dwCaretYpos=0; /* position of caret in pixel coords */
64 TEXTMETRIC tm;                  /* textmetric for current font */
65 RECT rectClient;        /* client rectangle of the window we're drawing in */
66 /* } BUFFER, *LPBUFFER */
67
68 VOID InitFontInfo(HWND hWnd)
69 {
70     HDC hDC = GetDC(hWnd);
71
72     if(hDC)
73     {
74         GetTextMetrics(hDC, &tm);
75         ReleaseDC(hWnd,hDC);
76     }
77 }
78
79 void InitBuffer(void)
80 {
81     lpBuffer = NULL;
82     dwLines = 0;
83     dwMaxLines = 0;
84     dwXpos=0;
85     dwYpos=0;
86 }
87
88 /* convert x,y character co-ords into x pixel co-ord */
89 DWORD CalcStringWidth(HDC hDC, DWORD x, DWORD y)
90 {
91     DWORD len;
92     SIZE size;
93
94     size.cx = 0;
95     size.cy = 0;
96
97     if(y>dwLines)
98         return size.cx;
99     if(lpBuffer == NULL) 
100         return size.cx;
101     if(lpBuffer[y].lpLine == NULL)
102         return size.cx;
103     len = (x<lpBuffer[y].dwWidth) ? 
104            x : lpBuffer[y].dwWidth;
105     GetTextExtentPoint(hDC, lpBuffer[y].lpLine, len, &size);
106
107     return size.cx;
108 }
109
110 void CalcCaretPos(HDC hDC, DWORD dwXpos, DWORD dwYpos)
111 {
112     dwCaretXpos = CalcStringWidth(hDC, dwXpos, dwYpos);
113     dwCaretYpos = tm.tmHeight*(dwYpos-dwVOffset);
114     SetCaretPos(dwCaretXpos,dwCaretYpos);
115 }
116
117 DWORD GetLinesPerPage(HWND hWnd)
118 {
119     return (rectClient.bottom/tm.tmHeight); /* round down */
120 }
121
122 /* render one line of text and blank space */
123 void RenderLine(HDC hDC, DWORD lineno)
124 {
125     RECT rect;
126     HBRUSH hPrev;
127
128     if(!hDC)
129         return;
130
131     /* erase the space at the end of a line using a white rectangle */
132     rect.top = tm.tmHeight*(lineno-dwVOffset);
133     rect.bottom = tm.tmHeight*(lineno-dwVOffset+1);
134
135     if(lpBuffer && (lineno<dwLines))
136         rect.left = CalcStringWidth(hDC, lpBuffer[lineno].dwWidth,lineno);
137     else
138         rect.left = 0;
139     rect.right = rectClient.right;
140
141     /* use the white pen so there's not outline */
142     hPrev = SelectObject(hDC, GetStockObject(WHITE_PEN));
143     Rectangle(hDC, rect.left, rect.top, rect.right, rect.bottom);
144     SelectObject(hDC, hPrev);
145     
146     if(lpBuffer && lpBuffer[lineno].lpLine)
147     {
148         TextOut(hDC, 0, rect.top, lpBuffer[lineno].lpLine, 
149                        lpBuffer[lineno].dwWidth);
150     }
151 }
152
153 /*
154  * Paint the buffer onto the window.
155  */
156 void RenderWindow(HDC hDC) {
157     DWORD i;
158
159     if(!hDC)
160         return;
161
162     /* FIXME: render only necessary lines */
163     for(i = dwVOffset; i < (dwVOffset+GetLinesPerPage(0)); i++)
164     {
165         RenderLine(hDC,i);
166     }
167 }
168
169 /* 
170  * Check that correct buffers exist to access line y pos x
171  * Only manages memory.
172  *
173  * Returns TRUE if the line is accessable
174  *         FALSE if there is a problem
175  */
176 BOOL ValidateLine(DWORD y,DWORD x)
177 {
178     DWORD max;
179
180     /* check to see that the BUFFER has enough lines */
181     max = dwMaxLines;
182     if( (max<=y) || (lpBuffer == NULL))
183     {
184         while(max<=y)
185             max += BUFFERCHUNKSIZE;
186         /* use GlobalAlloc first time round */
187         if(lpBuffer)
188             lpBuffer = (LPLINE) GlobalReAlloc((HGLOBAL)lpBuffer,GMEM_FIXED,
189                                           max*sizeof(LINE)) ;
190         else
191             lpBuffer = (LPLINE) GlobalAlloc(GMEM_FIXED, max*sizeof(LINE));
192         if(lpBuffer == NULL)
193             return FALSE;
194         ZeroMemory(&lpBuffer[dwLines], sizeof(LINE)*(max-dwLines) );
195         dwMaxLines = max;
196     }
197
198     /* check to see that the LINE is wide enough */
199     max = lpBuffer[y].dwMaxWidth;
200     if( (max <= x) || (lpBuffer[y].lpLine == NULL) )
201     {
202         while(max <= x)
203             max += BUFFERCHUNKSIZE;
204         /* use GlobalAlloc first */
205         if(lpBuffer[y].lpLine)
206             lpBuffer[y].lpLine = (LPSTR)GlobalReAlloc((HGLOBAL)lpBuffer[y].lpLine,
207                                                   GMEM_FIXED, max) ;
208         else
209             lpBuffer[y].lpLine = (LPSTR)GlobalAlloc( GMEM_FIXED, max);
210         if(lpBuffer[y].lpLine == NULL)
211             return FALSE;
212         lpBuffer[y].dwWidth = 0;
213         lpBuffer[y].dwMaxWidth = max;
214     }
215     return TRUE;
216 }
217
218 /* inserts a new line into the buffer */
219 BOOL DoNewLine(HDC hDC)
220 {
221     DWORD i,cnt;
222     LPSTR src,dst;
223
224     /* check to see if we need more memory for the buffer pointers */
225     if(!ValidateLine(dwLines,0))
226         return FALSE;
227
228     /* shuffle up all the lines */
229     for(i=dwLines; i>(dwYpos+1); i--)
230     {
231         lpBuffer[i] = lpBuffer[i-1];
232         RenderLine(hDC,i);
233     }
234     ZeroMemory(&lpBuffer[dwYpos+1],sizeof(LINE));
235
236     /* copy the characters after the carat (if any) to the next line */
237     src = &lpBuffer[dwYpos].lpLine[dwXpos];
238     cnt = lpBuffer[dwYpos].dwWidth-dwXpos;
239     if(!ValidateLine(dwYpos+1,cnt)) /* allocates the buffer */
240         return FALSE; /* FIXME */
241     dst = &lpBuffer[dwYpos+1].lpLine[0];
242     memcpy(dst, src, cnt);
243     lpBuffer[dwYpos+1].dwWidth = cnt;
244     lpBuffer[dwYpos].dwWidth  -= cnt;
245
246     /* move the cursor */
247     dwLines++;
248     dwXpos = 0;
249     dwYpos++;
250
251     /* update the window */
252     RenderLine(hDC, dwYpos-1);
253     RenderLine(hDC, dwYpos);
254     CalcCaretPos(hDC, dwXpos, dwYpos);
255     /* FIXME: don't use globals */
256     SetScrollRange(Globals.hMainWnd, SB_VERT, 0, dwLines, TRUE);
257
258     return TRUE;
259 }
260
261 /*
262  * Attempt a basic edit buffer
263  */
264 BOOL AddCharToBuffer(HDC hDC, char ch)
265 {
266     /* we can use lpBuffer[dwYpos] */
267     if(!ValidateLine(dwYpos,0))
268         return FALSE;
269
270     /* shuffle the rest of the line*/
271     if(!ValidateLine(dwYpos, lpBuffer[dwYpos].dwWidth))
272         return FALSE;
273     lpBuffer[dwYpos].dwWidth++;
274     memmove(&lpBuffer[dwYpos].lpLine[dwXpos+1],
275             &lpBuffer[dwYpos].lpLine[dwXpos],
276             lpBuffer[dwYpos].dwWidth-dwXpos);
277
278     /* add the character */
279     lpBuffer[dwYpos].lpLine[dwXpos] = ch;
280     if(dwLines == 0)
281         dwLines++;
282     dwXpos++;
283
284     /* update the window and cursor position */
285     RenderLine(hDC,dwYpos);
286     CalcCaretPos(hDC,dwXpos,dwYpos);
287
288     return TRUE;
289 }
290
291
292 /* erase a character */
293 BOOL DoBackSpace(HDC hDC)
294 {
295     DWORD i;
296
297     if(lpBuffer == NULL)
298         return FALSE;
299     if(lpBuffer[dwYpos].lpLine && (dwXpos>0))
300     {
301         dwXpos --;
302         /* FIXME: use memmove */
303         for(i=dwXpos; i<(lpBuffer[dwYpos].dwWidth-1); i++)
304             lpBuffer[dwYpos].lpLine[i]=lpBuffer[dwYpos].lpLine[i+1];
305
306         lpBuffer[dwYpos].dwWidth--;
307         RenderLine(hDC, dwYpos);
308         CalcCaretPos(hDC,dwXpos,dwYpos);
309     }
310     else 
311     {
312         /* Erase a newline. To do this we join two lines */
313         LPSTR src, dest;
314         DWORD len, oldlen;
315
316         if(dwYpos==0)
317             return FALSE;
318
319         oldlen = lpBuffer[dwYpos-1].dwWidth;
320         if(lpBuffer[dwYpos-1].lpLine&&lpBuffer[dwYpos].lpLine)
321         {
322             /* concatonate to the end of the line above line */
323             src  = &lpBuffer[dwYpos].lpLine[0];
324             dest = &lpBuffer[dwYpos-1].lpLine[lpBuffer[dwYpos-1].dwWidth];
325             len  = lpBuffer[dwYpos].dwWidth;
326
327             /* check the length of the new line */
328             if(!ValidateLine(dwYpos-1,lpBuffer[dwYpos-1].dwWidth + len))
329                 return FALSE;
330
331             memcpy(dest,src,len);
332             lpBuffer[dwYpos-1].dwWidth+=len;
333             GlobalFree( (HGLOBAL)lpBuffer[dwYpos].lpLine);
334         }
335         else if (!lpBuffer[dwYpos-1].lpLine)
336         {
337             lpBuffer[dwYpos-1]=lpBuffer[dwYpos];
338         } /* else both are NULL */
339         RenderLine(hDC,dwYpos-1);
340
341         /* don't zero - it's going to get trashed anyhow */
342
343         /* shuffle up all the lines below this one */
344         for(i=dwYpos; i<(dwLines-1); i++)
345         {
346             lpBuffer[i] = lpBuffer[i+1];
347             RenderLine(hDC,i);
348         }
349
350         /* clear the last line */
351         ZeroMemory(&lpBuffer[dwLines-1],sizeof (LINE));
352         RenderLine(hDC,dwLines-1);
353         dwLines--;
354
355         /* adjust the cursor position to joining point */
356         dwYpos--;
357         dwXpos = oldlen;
358
359         CalcCaretPos(hDC,dwXpos,dwYpos);
360         SetScrollRange(Globals.hMainWnd, SB_VERT, 0, dwLines, TRUE);
361     }
362     return TRUE;
363 }
364
365 /* as used by File->New */
366 void TrashBuffer(void)
367 {
368     DWORD i;
369
370     /* variables belonging to the buffer */
371     if(lpBuffer)
372     {
373         for(i=0; i<dwLines; i++)
374         {
375             if(lpBuffer[i].lpLine)
376                 GlobalFree ((HGLOBAL)lpBuffer[i].lpLine);
377             ZeroMemory(&lpBuffer[i],sizeof (LINE));
378         }
379         GlobalFree((HGLOBAL)lpBuffer);
380         lpBuffer=NULL;
381     }
382     dwLines = 0;
383     dwMaxLines = 0;
384
385     /* variables belonging to the view */
386     dwXpos = 0;
387     dwYpos = 0;
388     dwVOffset = 0 ;
389     /* FIXME: don't use globals */
390     SetScrollPos(Globals.hMainWnd, SB_VERT, dwVOffset, FALSE);
391     SetScrollRange(Globals.hMainWnd, SB_VERT, 0, dwLines, TRUE);
392 }
393
394
395 /*
396  * Add a line to the buffer
397  */
398 /* FIXME: this breaks lines longer than BUFFERCHUNKSIZE */
399 DWORD CreateLine(
400     LPSTR buffer, /* pointer to buffer with file data */
401     DWORD size, /* number of bytes available in buffer */
402     BOOL nomore)
403 {
404     DWORD i;
405     
406     if(size == 0)
407         return 0;
408
409     for(i=0; i<size; i++)
410     {
411         if(buffer[i]==0x0a)
412         {
413             if(ValidateLine(dwLines,i))
414             {
415                 memcpy(&lpBuffer[dwLines].lpLine[0],&buffer[0],i);
416                 lpBuffer[dwLines].dwWidth = i;
417                 dwLines++;
418             }
419             return i+1;
420         }
421     }
422
423     /* make a line of the rest */
424     if( (i == BUFFERCHUNKSIZE) || nomore )
425     {
426         if(ValidateLine(dwLines,i))
427         {
428             memcpy(&lpBuffer[dwLines].lpLine[0],&buffer[0],i);
429             lpBuffer[dwLines].dwWidth = i;
430             dwLines++;
431         }
432         return i;
433     }
434
435     return 0;
436 }
437
438
439 /* 
440  * This is probably overcomplicated by lpBuffer data structure... 
441  * Read blocks from the file, then add each line from the
442  *  block to the buffer until there is none left. If all
443  *  a slab isn't used, try load some more data from the file.
444  */
445 void LoadBufferFromFile(LPCSTR szFileName)
446 {
447     HANDLE hFile;
448     CHAR *pTemp;
449     DWORD size,i,len,bytes_left,bytes_read;
450
451     hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ, NULL,
452                            OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
453     if(hFile == INVALID_HANDLE_VALUE)
454         return;
455     size = BUFFERCHUNKSIZE;
456     pTemp = (LPSTR) GlobalAlloc(GMEM_FIXED, size);
457     if(!pTemp)
458         return;
459     bytes_read = 1; /* anything non-zero */
460     bytes_left = 0;
461     while(bytes_read)
462     {
463         if(!ReadFile(hFile, 
464                      &pTemp[bytes_left], 
465                      size-bytes_left, 
466                      &bytes_read, NULL))
467             break;
468         bytes_left+=bytes_read;
469
470         /* add strings to buffer */
471         for(i = 0; 
472             (i<size) &&
473             (len  = CreateLine(&pTemp[i], bytes_left, !bytes_read));
474             i+= len,bytes_left-=len );
475
476         /* move leftover to front of buffer */
477         if(bytes_left)
478             memmove(&pTemp[0],&pTemp[i], bytes_left);
479     }
480     CloseHandle(hFile);
481 }
482
483 BOOL DoInput(HDC hDC, WPARAM wParam, LPARAM lParam)
484 {
485     switch(wParam)
486     {
487     case 0x08:
488         return DoBackSpace(hDC);
489     case 0x0d:
490         return DoNewLine(hDC);
491     default:
492         return AddCharToBuffer(hDC,wParam);
493     }
494 }
495
496 BOOL GotoHome(HWND hWnd)
497 {
498     dwXpos = 0;
499     dwYpos = 0;
500     dwVOffset = 0;
501     return TRUE;
502 }
503
504 BOOL GotoEndOfLine(HWND hWnd)
505 {
506     dwXpos = lpBuffer[dwYpos].dwWidth;
507     return TRUE;
508 }
509
510 BOOL GotoDown(HWND hWnd)
511 {
512     if((dwYpos+1) >= dwLines)
513     {
514         return FALSE;
515     }
516     dwYpos++;
517     if (dwXpos>lpBuffer[dwYpos].dwWidth)
518         GotoEndOfLine(hWnd);
519     return TRUE;
520 }
521
522 BOOL GotoUp(HWND hWnd)
523 {
524     if(dwYpos==0)
525         return FALSE;
526     dwYpos--;
527     if (dwXpos>lpBuffer[dwYpos].dwWidth)
528         GotoEndOfLine(hWnd);
529     return TRUE;
530 }
531
532 BOOL GotoLeft(HWND hWnd)
533 {
534     if(dwXpos > 0)
535     {
536         dwXpos--;
537         return TRUE;
538     }
539     if(GotoUp(hWnd))   
540         return GotoEndOfLine(hWnd);
541     return FALSE;
542 }
543
544 BOOL GotoRight(HWND hWnd)
545 {
546     if(dwXpos<lpBuffer[dwYpos].dwWidth)
547     {
548         dwXpos++;
549         return TRUE;
550     }
551     if(!GotoDown(hWnd))
552         return FALSE;
553     dwXpos=0;
554     return TRUE;
555 }
556
557 /* check the caret is still on the screen */
558 BOOL ScrollABit(HWND hWnd)
559 {
560     if(dwYpos<dwVOffset)
561     {
562         dwVOffset = dwYpos;
563         return TRUE;
564     }
565     if(dwYpos>(dwVOffset+GetLinesPerPage(hWnd)))
566     {
567         dwVOffset = dwYpos - GetLinesPerPage(hWnd) + 1;
568         return TRUE;
569     }
570     return FALSE;
571 }
572
573 /* FIXME: move the window around so we can still see the caret */
574 VOID DoEdit(HWND hWnd, WPARAM wParam, LPARAM lParam)
575 {
576     HDC hDC;
577
578     if(lpBuffer==NULL)
579         return;
580     switch(wParam)
581     {
582     case VK_HOME: GotoHome(hWnd);
583         break;
584
585     case VK_END: GotoEndOfLine(hWnd);
586         break;
587
588     case VK_LEFT: GotoLeft(hWnd);
589         break;
590
591     case VK_RIGHT: GotoRight(hWnd);
592         break;
593
594     case VK_DOWN: GotoDown(hWnd);
595         break;
596
597     case VK_UP: GotoUp(hWnd);
598         break;
599
600     default:
601         return;
602     }
603
604     hDC = GetDC(hWnd);
605     if(hDC)
606     {
607         CalcCaretPos(hDC, dwXpos, dwYpos);
608         ReleaseDC(hWnd,hDC);
609     }
610     if(ScrollABit(hWnd))
611         InvalidateRect(hWnd, NULL, FALSE);
612 }
613
614 void ButtonDownToCaretPos(HWND hWnd, WPARAM wParam, LPARAM lParam)
615 {
616     DWORD x, y, caretx, carety;
617     BOOL refine_guess = TRUE;
618     HDC hDC;
619
620     x = LOWORD(lParam);
621     y = HIWORD(lParam);
622
623     caretx = x/tm.tmAveCharWidth; /* guess */
624     carety = dwVOffset + y/tm.tmHeight;
625
626     hDC = GetDC(hWnd);
627
628     if(lpBuffer == NULL)
629     {
630         caretx = 0;
631         carety = 0;
632         refine_guess = FALSE;
633     }
634
635     /* if the cursor is past the bottom, put it after the last char */
636     if(refine_guess && (carety>=dwLines) )
637     {
638         carety=dwLines-1;
639         caretx=lpBuffer[carety].dwWidth;
640         refine_guess = FALSE;
641     }
642
643     /* cursor past end of line? */
644     if(refine_guess && (x>CalcStringWidth(hDC,lpBuffer[carety].dwWidth,carety)))
645     {
646         caretx = lpBuffer[carety].dwWidth;
647         refine_guess = FALSE;
648     }
649
650     /* FIXME: doesn't round properly */
651     if(refine_guess)
652     {
653         if(CalcStringWidth(hDC,caretx,carety)<x)
654         {
655             while( (caretx<lpBuffer[carety].dwWidth) &&
656                    (CalcStringWidth(hDC,caretx+1,carety)<x))
657                 caretx++;
658         }
659         else
660         {
661             while((caretx>0)&&(CalcStringWidth(hDC,caretx-1,carety)>x))
662                 caretx--;
663         }
664     }
665     
666     /* set the caret's position */
667     dwXpos = caretx;
668     dwYpos = carety;
669     CalcCaretPos(hDC, caretx, carety);
670     ReleaseDC(hWnd,hDC);
671 }
672
673 void DoScroll(HWND hWnd, WPARAM wParam, LPARAM lParam)
674 {
675     DWORD dy = GetLinesPerPage(hWnd);
676
677     switch(wParam) /* vscroll code */
678     {
679     case SB_LINEUP:
680         if(dwVOffset)
681             dwVOffset--;
682         break;
683     case SB_LINEDOWN:
684         if(dwVOffset<dwLines)
685             dwVOffset++;
686         break;
687     case SB_PAGEUP:
688         if( (dy+dwVOffset) > dwLines)
689             dwVOffset = dwLines - 1;
690         break;
691     case SB_PAGEDOWN:
692         if( dy > dwVOffset)
693             dwVOffset=0;
694         break;
695     }
696     /* position scroll */
697     SetScrollPos(hWnd, SB_VERT, dwVOffset, TRUE);
698 }
699
700 /***********************************************************************
701  *
702  *           NOTEPAD_MenuCommand
703  *
704  *  All handling of main menu events
705  */
706
707 int NOTEPAD_MenuCommand (WPARAM wParam)
708 {  
709    switch (wParam) {
710      case NP_FILE_NEW:          DIALOG_FileNew(); break;
711      case NP_FILE_OPEN:         DIALOG_FileOpen(); break;
712      case NP_FILE_SAVE:         DIALOG_FileSave(); break;
713      case NP_FILE_SAVEAS:       DIALOG_FileSaveAs(); break;
714      case NP_FILE_PRINT:        DIALOG_FilePrint(); break;
715      case NP_FILE_PAGESETUP:    DIALOG_FilePageSetup(); break;
716      case NP_FILE_PRINTSETUP:   DIALOG_FilePrinterSetup();break;
717      case NP_FILE_EXIT:         DIALOG_FileExit(); break;
718
719      case NP_EDIT_UNDO:         DIALOG_EditUndo(); break;
720      case NP_EDIT_CUT:          DIALOG_EditCut(); break;
721      case NP_EDIT_COPY:         DIALOG_EditCopy(); break;
722      case NP_EDIT_PASTE:        DIALOG_EditPaste(); break;
723      case NP_EDIT_DELETE:       DIALOG_EditDelete(); break;
724      case NP_EDIT_SELECTALL:    DIALOG_EditSelectAll(); break;
725      case NP_EDIT_TIMEDATE:     DIALOG_EditTimeDate();break;
726      case NP_EDIT_WRAP:         DIALOG_EditWrap(); break;
727
728      case NP_SEARCH_SEARCH:     DIALOG_Search(); break;
729      case NP_SEARCH_NEXT:       DIALOG_SearchNext(); break;
730
731      case NP_HELP_CONTENTS:     DIALOG_HelpContents(); break;
732      case NP_HELP_SEARCH:       DIALOG_HelpSearch(); break;
733      case NP_HELP_ON_HELP:      DIALOG_HelpHelp(); break;
734      case NP_HELP_LICENSE:      DIALOG_HelpLicense(); break;
735      case NP_HELP_NO_WARRANTY:  DIALOG_HelpNoWarranty(); break;
736      case NP_HELP_ABOUT_WINE:   DIALOG_HelpAboutWine(); break;
737      
738      /* Handle languages */
739      default:
740       LANGUAGE_DefaultHandle(wParam);
741    }
742    return 0;
743 }
744
745
746
747 /***********************************************************************
748  *
749  *           NOTEPAD_WndProc
750  */
751
752 LRESULT WINAPI NOTEPAD_WndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
753 {
754     PAINTSTRUCT ps;
755     HDC hContext;
756     HANDLE hDrop;                      /* drag & drop */
757     CHAR szFileName[MAX_STRING_LEN];
758     RECT Windowsize;
759
760     lstrcpy(szFileName, "");
761
762     switch (msg) {
763
764        case WM_CREATE:
765           GetClientRect(hWnd, &rectClient);
766           InitFontInfo(hWnd);
767           break;
768
769        case WM_SETFOCUS:
770           CreateCaret(Globals.hMainWnd, 0, 1, tm.tmHeight);
771           SetCaretPos(dwCaretXpos, dwCaretYpos);
772           ShowCaret(Globals.hMainWnd);
773           break;
774
775        case WM_KILLFOCUS:
776           DestroyCaret();
777           break;
778
779        case WM_PAINT:
780           GetClientRect(hWnd, &rectClient);
781           hContext = BeginPaint(hWnd, &ps);
782           RenderWindow(hContext);
783           EndPaint(hWnd, &ps);
784         break;
785
786        case WM_KEYDOWN:
787           DoEdit(hWnd, wParam, lParam);
788           break;
789
790        case WM_CHAR:
791           GetClientRect(hWnd, &rectClient);
792           HideCaret(hWnd);
793           hContext = GetDC(hWnd);
794           DoInput(hContext,wParam,lParam);
795           ReleaseDC(hWnd,hContext);
796           ShowCaret(hWnd);
797           break;
798
799        case WM_LBUTTONDOWN:
800           /* figure out where the mouse was clicked */
801           ButtonDownToCaretPos(hWnd, wParam, lParam);
802           break;
803
804        case WM_VSCROLL:
805           DoScroll(hWnd, wParam, lParam);
806           InvalidateRect(hWnd, NULL, FALSE); /* force a redraw */
807           break;
808
809        case WM_COMMAND:
810           /* FIXME: this is a bit messy */
811           NOTEPAD_MenuCommand(wParam);
812           InvalidateRect(hWnd, NULL, FALSE); /* force a redraw */
813           hContext = GetDC(hWnd);
814           CalcCaretPos(hContext,dwXpos,dwYpos);
815           ReleaseDC(hWnd,hContext);
816           break;
817
818        case WM_DESTROYCLIPBOARD:
819           MessageBox(Globals.hMainWnd, "Empty clipboard", "Debug", MB_ICONEXCLAMATION);
820           break;
821
822        case WM_CLOSE:
823           if (DoCloseFile()) {
824              PostQuitMessage(0);
825           }
826           break;
827
828        case WM_DESTROY:
829              PostQuitMessage (0);
830           break;
831
832        case WM_SIZE:
833           GetClientRect(Globals.hMainWnd, &Windowsize);
834           break;
835
836        case WM_DROPFILES:
837           /* User has dropped a file into main window */
838           hDrop = (HANDLE) wParam;
839           DragQueryFile(hDrop, 0, (CHAR *) &szFileName, sizeof(szFileName));
840           DragFinish(hDrop);
841           DoOpenFile(szFileName);
842           break;        
843
844        default:
845           return DefWindowProc (hWnd, msg, wParam, lParam);
846     }
847     return 0l;
848 }
849
850 int AlertFileDoesNotExist(LPSTR szFileName) {
851
852    int nResult;
853    CHAR szMessage[MAX_STRING_LEN];
854    CHAR szRessource[MAX_STRING_LEN];
855
856    LoadString(Globals.hInstance, IDS_DOESNOTEXIST, szRessource,
857               sizeof(szRessource));
858    wsprintf(szMessage, szRessource, szFileName);
859    
860    LoadString(Globals.hInstance, IDS_ERROR,  szRessource, sizeof(szRessource));
861
862    nResult = MessageBox(Globals.hMainWnd, szMessage, szRessource,
863                         MB_ICONEXCLAMATION | MB_YESNO);
864    
865    return(nResult);
866 }
867
868 void HandleCommandLine(LPSTR cmdline)
869 {
870     
871     while (*cmdline && (*cmdline == ' ' || *cmdline == '-')) 
872     
873     {
874         CHAR   option;
875
876         if (*cmdline++ == ' ') continue;
877
878         option = *cmdline;
879         if (option) cmdline++;
880         while (*cmdline && *cmdline == ' ') cmdline++;
881
882         switch(option)
883         {
884             case 'p':
885             case 'P': printf("Print file: ");
886                       /* Not yet able to print a file */
887                       break;
888         }
889     }
890
891     if (*cmdline) 
892     {
893         /* file name is passed in the command line */
894         char *file_name;
895         BOOL file_exists;
896         char buf[MAX_PATH];
897
898         if (FileExists(cmdline)) 
899         {
900             file_exists = TRUE;
901             file_name = cmdline;
902         }
903         else 
904         {
905             /* try to find file with ".txt" extention */ 
906             if (!strcmp(".txt", cmdline + strlen(cmdline) - strlen(".txt"))) 
907             {
908                 file_exists = FALSE;
909                 file_name = cmdline;
910             }
911             else
912             {
913                 strncpy(buf, cmdline, MAX_PATH - strlen(".txt") - 1);
914                 strcat(buf, ".txt");
915                 file_name = buf;
916                 file_exists = FileExists(buf);
917             }
918         }
919
920         if (file_exists)
921         {
922             DoOpenFile(file_name);
923             InvalidateRect(Globals.hMainWnd, NULL, FALSE);
924         }
925         else
926         {
927             switch (AlertFileDoesNotExist(file_name)) {
928             case IDYES:
929                 DoOpenFile(file_name);
930                 break;
931
932             case IDNO:
933                 break;
934             }
935         }
936      }
937 }
938
939
940 /***********************************************************************
941  *
942  *           WinMain
943  */
944
945 int PASCAL WinMain (HINSTANCE hInstance, HINSTANCE prev, LPSTR cmdline, int show)
946 {
947     MSG      msg;
948     WNDCLASS class;
949     char className[] = "NPClass";  /* To make sure className >= 0x10000 */
950     char winName[]   = "Notepad";
951
952     /* setup buffer */
953     InitBuffer();
954
955     /* Setup Globals */
956
957     Globals.lpszIniFile = "notepad.ini";
958     Globals.lpszIcoFile = "notepad.ico";
959
960     Globals.hInstance       = hInstance;
961
962 #ifndef LCC
963     Globals.hMainIcon       = ExtractIcon(Globals.hInstance, 
964                                         Globals.lpszIcoFile, 0);
965 #endif
966     if (!Globals.hMainIcon) {
967         Globals.hMainIcon = LoadIcon(0, MAKEINTRESOURCE(DEFAULTICON));
968     }
969
970     lstrcpy(Globals.szFindText,     "");
971     lstrcpy(Globals.szFileName,     "");
972     lstrcpy(Globals.szMarginTop,    "25 mm");
973     lstrcpy(Globals.szMarginBottom, "25 mm");
974     lstrcpy(Globals.szMarginLeft,   "20 mm");
975     lstrcpy(Globals.szMarginRight,  "20 mm");
976     lstrcpy(Globals.szHeader,       "&n");
977     lstrcpy(Globals.szFooter,       "Page &s");
978     lstrcpy(Globals.Buffer, "Hello World");
979
980     if (!prev){
981         class.style         = CS_HREDRAW | CS_VREDRAW;
982         class.lpfnWndProc   = NOTEPAD_WndProc;
983         class.cbClsExtra    = 0;
984         class.cbWndExtra    = 0;
985         class.hInstance     = Globals.hInstance;
986         class.hIcon         = LoadIcon (0, IDI_APPLICATION);
987         class.hCursor       = LoadCursor (0, IDC_ARROW);
988         class.hbrBackground = GetStockObject (WHITE_BRUSH);
989         class.lpszMenuName  = 0;
990         class.lpszClassName = className;
991     }
992
993     if (!RegisterClass (&class)) return FALSE;
994
995     /* Setup windows */
996
997
998     Globals.hMainWnd = CreateWindow (className, winName, 
999        WS_OVERLAPPEDWINDOW + WS_HSCROLL + WS_VSCROLL,
1000        CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 0, 
1001        LoadMenu(Globals.hInstance, STRING_MENU_Xx),
1002        Globals.hInstance, 0);
1003
1004     Globals.hFindReplaceDlg = 0;
1005
1006     LANGUAGE_SelectByNumber(0);
1007
1008     SetMenu(Globals.hMainWnd, Globals.hMainMenu);               
1009                         
1010     ShowWindow (Globals.hMainWnd, show);
1011     UpdateWindow (Globals.hMainWnd);
1012
1013     /* Set up dialogs */
1014
1015     /* Identify Messages originating from FindReplace */
1016
1017     Globals.nCommdlgFindReplaceMsg = RegisterWindowMessage("commdlg_FindReplace");
1018     if (Globals.nCommdlgFindReplaceMsg==0) {
1019        MessageBox(Globals.hMainWnd, "Could not register commdlg_FindReplace window message", 
1020                   "Error", MB_ICONEXCLAMATION);
1021     }
1022
1023     HandleCommandLine(cmdline);
1024
1025     /* Set up Drag&Drop */
1026
1027     DragAcceptFiles(Globals.hMainWnd, TRUE);
1028
1029     /* now enter mesage loop     */
1030     
1031     while (GetMessage (&msg, 0, 0, 0)) {
1032         if (IsDialogMessage(Globals.hFindReplaceDlg, &msg)!=0) {
1033           /* Message belongs to FindReplace dialog */
1034           /* We just let IsDialogMessage handle it */
1035         } 
1036           else
1037         { 
1038           /* Message belongs to the Notepad Main Window */
1039           TranslateMessage (&msg);
1040           DispatchMessage (&msg);
1041         }
1042     }
1043     return 0;
1044 }