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