ole32 now requires linking with kernel32
[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  *  - seperate 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 DoCloseFile(void);
38 extern void DoOpenFile(LPCSTR szFileName);
39
40 NOTEPAD_GLOBALS Globals;
41
42
43 /* Using a pointer to pointer data structure to
44    achieve a little more efficiency. Hopefully
45    it will be worth it, because it complicates the
46    code - mjm 26 Jun 2000 */
47
48 #define BUFFERCHUNKSIZE 0xe0
49 typedef struct TAGLine {
50     LPSTR lpLine;
51     DWORD dwWidth;
52     DWORD dwMaxWidth;
53 } LINE, *LPLINE;
54
55 /* FIXME: make this info into a structure */
56 /* typedef struct tagBUFFER { */
57 DWORD dwVOffset=0;
58 DWORD dwLines=0;
59 DWORD dwMaxLines=0;
60 LPLINE lpBuffer=NULL;
61 DWORD dwXpos=0,dwYpos=0;        /* position of caret in char coords */
62 DWORD dwCaretXpos=0,dwCaretYpos=0; /* position of caret in pixel coords */
63 TEXTMETRIC tm;                  /* textmetric for current font */
64 RECT rectClient;        /* client rectangle of the window we're drawing in */
65 /* } BUFFER, *LPBUFFER */
66
67 VOID InitFontInfo(HWND hWnd)
68 {
69     HDC hDC = GetDC(hWnd);
70
71     if(hDC)
72     {
73         GetTextMetrics(hDC, &tm);
74         ReleaseDC(hWnd,hDC);
75     }
76 }
77
78 void InitBuffer(void)
79 {
80     lpBuffer = NULL;
81     dwLines = 0;
82     dwMaxLines = 0;
83     dwXpos=0;
84     dwYpos=0;
85 }
86
87 /* convert x,y character co-ords into x pixel co-ord */
88 DWORD CalcStringWidth(HDC hDC, DWORD x, DWORD y)
89 {
90     DWORD len;
91     SIZE size;
92
93     size.cx = 0;
94     size.cy = 0;
95
96     if(y>dwLines)
97         return size.cx;
98     if(lpBuffer == NULL) 
99         return size.cx;
100     if(lpBuffer[y].lpLine == NULL)
101         return size.cx;
102     len = (x<lpBuffer[y].dwWidth) ? 
103            x : lpBuffer[y].dwWidth;
104     GetTextExtentPoint(hDC, lpBuffer[y].lpLine, len, &size);
105
106     return size.cx;
107 }
108
109 void CalcCaretPos(HDC hDC, DWORD dwXpos, DWORD dwYpos)
110 {
111     dwCaretXpos = CalcStringWidth(hDC, dwXpos, dwYpos);
112     dwCaretYpos = tm.tmHeight*(dwYpos-dwVOffset);
113     SetCaretPos(dwCaretXpos,dwCaretYpos);
114 }
115
116 DWORD GetLinesPerPage(HWND hWnd)
117 {
118     return (rectClient.bottom/tm.tmHeight); /* round down */
119 }
120
121 /* render one line of text and blank space */
122 void RenderLine(HDC hDC, DWORD lineno)
123 {
124     RECT rect;
125     HBRUSH hPrev;
126
127     if(!hDC)
128         return;
129
130     /* erase the space at the end of a line using a white rectangle */
131     rect.top = tm.tmHeight*(lineno-dwVOffset);
132     rect.bottom = tm.tmHeight*(lineno-dwVOffset+1);
133
134     if(lpBuffer && (lineno<dwLines))
135         rect.left = CalcStringWidth(hDC, lpBuffer[lineno].dwWidth,lineno);
136     else
137         rect.left = 0;
138     rect.right = rectClient.right;
139
140     /* use the white pen so there's not outline */
141     hPrev = SelectObject(hDC, GetStockObject(WHITE_PEN));
142     Rectangle(hDC, rect.left, rect.top, rect.right, rect.bottom);
143     SelectObject(hDC, hPrev);
144     
145     if(lpBuffer && lpBuffer[lineno].lpLine)
146     {
147         TextOut(hDC, 0, rect.top, lpBuffer[lineno].lpLine, 
148                        lpBuffer[lineno].dwWidth);
149     }
150 }
151
152 /*
153  * Paint the buffer onto the window.
154  */
155 void RenderWindow(HDC hDC) {
156     DWORD i;
157
158     if(!hDC)
159         return;
160
161     /* FIXME: render only necessary lines */
162     for(i = dwVOffset; i < (dwVOffset+GetLinesPerPage(0)); i++)
163     {
164         RenderLine(hDC,i);
165     }
166 }
167
168 /* 
169  * Check that correct buffers exist to access line y pos x
170  * Only manages memory.
171  *
172  * Returns TRUE if the line is accessable
173  *         FALSE if there is a problem
174  */
175 BOOL ValidateLine(DWORD y,DWORD x)
176 {
177     DWORD max;
178
179     /* check to see that the BUFFER has enough lines */
180     max = dwMaxLines;
181     if( (max<=y) || (lpBuffer == NULL))
182     {
183         while(max<=y)
184             max += BUFFERCHUNKSIZE;
185         /* use GlobalAlloc first time round */
186         if(lpBuffer)
187             lpBuffer = (LPLINE) GlobalReAlloc((HGLOBAL)lpBuffer,GMEM_FIXED,
188                                           max*sizeof(LINE)) ;
189         else
190             lpBuffer = (LPLINE) GlobalAlloc(GMEM_FIXED, max*sizeof(LINE));
191         if(lpBuffer == NULL)
192             return FALSE;
193         ZeroMemory(&lpBuffer[dwLines], sizeof(LINE)*(max-dwLines) );
194         dwMaxLines = max;
195     }
196
197     /* check to see that the LINE is wide enough */
198     max = lpBuffer[y].dwMaxWidth;
199     if( (max <= x) || (lpBuffer[y].lpLine == NULL) )
200     {
201         while(max <= x)
202             max += BUFFERCHUNKSIZE;
203         /* use GlobalAlloc first */
204         if(lpBuffer[y].lpLine)
205             lpBuffer[y].lpLine = (LPSTR)GlobalReAlloc((HGLOBAL)lpBuffer[y].lpLine,
206                                                   GMEM_FIXED, max) ;
207         else
208             lpBuffer[y].lpLine = (LPSTR)GlobalAlloc( GMEM_FIXED, max);
209         if(lpBuffer[y].lpLine == NULL)
210             return FALSE;
211         lpBuffer[y].dwWidth = 0;
212         lpBuffer[y].dwMaxWidth = max;
213     }
214     return TRUE;
215 }
216
217 /* inserts a new line into the buffer */
218 BOOL DoNewLine(HDC hDC)
219 {
220     DWORD i,cnt;
221     LPSTR src,dst;
222
223     /* check to see if we need more memory for the buffer pointers */
224     if(!ValidateLine(dwLines,0))
225         return FALSE;
226
227     /* shuffle up all the lines */
228     for(i=dwLines; i>(dwYpos+1); i--)
229     {
230         lpBuffer[i] = lpBuffer[i-1];
231         RenderLine(hDC,i);
232     }
233     ZeroMemory(&lpBuffer[dwYpos+1],sizeof(LINE));
234
235     /* copy the characters after the carat (if any) to the next line */
236     src = &lpBuffer[dwYpos].lpLine[dwXpos];
237     cnt = lpBuffer[dwYpos].dwWidth-dwXpos;
238     if(!ValidateLine(dwYpos+1,cnt)) /* allocates the buffer */
239         return FALSE; /* FIXME */
240     dst = &lpBuffer[dwYpos+1].lpLine[0];
241     memcpy(dst, src, cnt);
242     lpBuffer[dwYpos+1].dwWidth = cnt;
243     lpBuffer[dwYpos].dwWidth  -= cnt;
244
245     /* move the cursor */
246     dwLines++;
247     dwXpos = 0;
248     dwYpos++;
249
250     /* update the window */
251     RenderLine(hDC, dwYpos-1);
252     RenderLine(hDC, dwYpos);
253     CalcCaretPos(hDC, dwXpos, dwYpos);
254     /* FIXME: don't use globals */
255     SetScrollRange(Globals.hMainWnd, SB_VERT, 0, dwLines, TRUE);
256
257     return TRUE;
258 }
259
260 /*
261  * Attempt a basic edit buffer
262  */
263 BOOL AddCharToBuffer(HDC hDC, char ch)
264 {
265     /* we can use lpBuffer[dwYpos] */
266     if(!ValidateLine(dwYpos,0))
267         return FALSE;
268
269     /* shuffle the rest of the line*/
270     if(!ValidateLine(dwYpos, lpBuffer[dwYpos].dwWidth))
271         return FALSE;
272     lpBuffer[dwYpos].dwWidth++;
273     memmove(&lpBuffer[dwYpos].lpLine[dwXpos+1],
274             &lpBuffer[dwYpos].lpLine[dwXpos],
275             lpBuffer[dwYpos].dwWidth-dwXpos);
276
277     /* add the character */
278     lpBuffer[dwYpos].lpLine[dwXpos] = ch;
279     if(dwLines == 0)
280         dwLines++;
281     dwXpos++;
282
283     /* update the window and cursor position */
284     RenderLine(hDC,dwYpos);
285     CalcCaretPos(hDC,dwXpos,dwYpos);
286
287     return TRUE;
288 }
289
290
291 /* erase a character */
292 BOOL DoBackSpace(HDC hDC)
293 {
294     DWORD i;
295
296     if(lpBuffer == NULL)
297         return FALSE;
298     if(lpBuffer[dwYpos].lpLine && (dwXpos>0))
299     {
300         dwXpos --;
301         /* FIXME: use memmove */
302         for(i=dwXpos; i<(lpBuffer[dwYpos].dwWidth-1); i++)
303             lpBuffer[dwYpos].lpLine[i]=lpBuffer[dwYpos].lpLine[i+1];
304
305         lpBuffer[dwYpos].dwWidth--;
306         RenderLine(hDC, dwYpos);
307         CalcCaretPos(hDC,dwXpos,dwYpos);
308     }
309     else 
310     {
311         /* Erase a newline. To do this we join two lines */
312         LPSTR src, dest;
313         DWORD len, oldlen;
314
315         if(dwYpos==0)
316             return FALSE;
317
318         oldlen = lpBuffer[dwYpos-1].dwWidth;
319         if(lpBuffer[dwYpos-1].lpLine&&lpBuffer[dwYpos].lpLine)
320         {
321             /* concatonate to the end of the line above line */
322             src  = &lpBuffer[dwYpos].lpLine[0];
323             dest = &lpBuffer[dwYpos-1].lpLine[lpBuffer[dwYpos-1].dwWidth];
324             len  = lpBuffer[dwYpos].dwWidth;
325
326             /* check the length of the new line */
327             if(!ValidateLine(dwYpos-1,lpBuffer[dwYpos-1].dwWidth + len))
328                 return FALSE;
329
330             memcpy(dest,src,len);
331             lpBuffer[dwYpos-1].dwWidth+=len;
332             GlobalFree( (HGLOBAL)lpBuffer[dwYpos].lpLine);
333         }
334         else if (!lpBuffer[dwYpos-1].lpLine)
335         {
336             lpBuffer[dwYpos-1]=lpBuffer[dwYpos];
337         } /* else both are NULL */
338         RenderLine(hDC,dwYpos-1);
339
340         /* don't zero - it's going to get trashed anyhow */
341
342         /* shuffle up all the lines below this one */
343         for(i=dwYpos; i<(dwLines-1); i++)
344         {
345             lpBuffer[i] = lpBuffer[i+1];
346             RenderLine(hDC,i);
347         }
348
349         /* clear the last line */
350         ZeroMemory(&lpBuffer[dwLines-1],sizeof (LINE));
351         RenderLine(hDC,dwLines-1);
352         dwLines--;
353
354         /* adjust the cursor position to joining point */
355         dwYpos--;
356         dwXpos = oldlen;
357
358         CalcCaretPos(hDC,dwXpos,dwYpos);
359         SetScrollRange(Globals.hMainWnd, SB_VERT, 0, dwLines, TRUE);
360     }
361     return TRUE;
362 }
363
364 /* as used by File->New */
365 void TrashBuffer(void)
366 {
367     DWORD i;
368
369     /* variables belonging to the buffer */
370     if(lpBuffer)
371     {
372         for(i=0; i<dwLines; i++)
373         {
374             if(lpBuffer[i].lpLine)
375                 GlobalFree ((HGLOBAL)lpBuffer[i].lpLine);
376             ZeroMemory(&lpBuffer[i],sizeof (LINE));
377         }
378         GlobalFree((HGLOBAL)lpBuffer);
379         lpBuffer=NULL;
380     }
381     dwLines = 0;
382     dwMaxLines = 0;
383
384     /* variables belonging to the view */
385     dwXpos = 0;
386     dwYpos = 0;
387     dwVOffset = 0 ;
388     /* FIXME: don't use globals */
389     SetScrollPos(Globals.hMainWnd, SB_VERT, dwVOffset, FALSE);
390     SetScrollRange(Globals.hMainWnd, SB_VERT, 0, dwLines, TRUE);
391 }
392
393
394 /*
395  * Add a line to the buffer
396  */
397 /* FIXME: this breaks lines longer than BUFFERCHUNKSIZE */
398 DWORD CreateLine(
399     LPSTR buffer, /* pointer to buffer with file data */
400     DWORD size, /* number of bytes available in buffer */
401     BOOL nomore)
402 {
403     DWORD i;
404     
405     if(size == 0)
406         return 0;
407
408     for(i=0; i<size; i++)
409     {
410         if(buffer[i]==0x0a)
411         {
412             if(ValidateLine(dwLines,i))
413             {
414                 memcpy(&lpBuffer[dwLines].lpLine[0],&buffer[0],i);
415                 lpBuffer[dwLines].dwWidth = i;
416                 dwLines++;
417             }
418             return i+1;
419         }
420     }
421
422     /* make a line of the rest */
423     if( (i == BUFFERCHUNKSIZE) || nomore )
424     {
425         if(ValidateLine(dwLines,i))
426         {
427             memcpy(&lpBuffer[dwLines].lpLine[0],&buffer[0],i);
428             lpBuffer[dwLines].dwWidth = i;
429             dwLines++;
430         }
431         return i;
432     }
433
434     return 0;
435 }
436
437
438 /* 
439  * This is probably overcomplicated by lpBuffer data structure... 
440  * Read blocks from the file, then add each line from the
441  *  block to the buffer until there is none left. If all
442  *  a slab isn't used, try load some more data from the file.
443  */
444 void LoadBufferFromFile(LPCSTR szFileName)
445 {
446     HFILE hFile;
447     OFSTRUCT ofs;
448     CHAR *pTemp;
449     DWORD size,i,len,bytes_left,bytes_read;
450
451     hFile = OpenFile(szFileName, &ofs, OF_READ);
452     if(hFile == INVALID_HANDLE_VALUE)
453         return;
454     size = BUFFERCHUNKSIZE;
455     pTemp = (LPSTR) GlobalAlloc(GMEM_FIXED, size);
456     if(!pTemp)
457         return;
458     bytes_read = 1; /* anything non-zero */
459     bytes_left = 0;
460     while(bytes_read)
461     {
462         if(!ReadFile(hFile, 
463                      &pTemp[bytes_left], 
464                      size-bytes_left, 
465                      &bytes_read, NULL))
466             break;
467         bytes_left+=bytes_read;
468
469         /* add strings to buffer */
470         for(i = 0; 
471             (i<size) &&
472             (len  = CreateLine(&pTemp[i], bytes_left, !bytes_read));
473             i+= len,bytes_left-=len );
474
475         /* move leftover to front of buffer */
476         if(bytes_left)
477             memmove(&pTemp[0],&pTemp[i], bytes_left);
478     }
479     CloseHandle(hFile);
480     MessageBox(Globals.hMainWnd, "Finished", "Info", MB_OK);
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 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
851
852 /***********************************************************************
853  *
854  *           WinMain
855  */
856
857 int PASCAL WinMain (HINSTANCE hInstance, HINSTANCE prev, LPSTR cmdline, int show)
858 {
859     MSG      msg;
860     WNDCLASS class;
861     char className[] = "NPClass";  /* To make sure className >= 0x10000 */
862     char winName[]   = "Notepad";
863
864     /* Select Language */
865     LANGUAGE_Init();
866
867     /* setup buffer */
868     InitBuffer();
869
870     /* Setup Globals */
871
872     Globals.lpszIniFile = "notepad.ini";
873     Globals.lpszIcoFile = "notepad.ico";
874
875     Globals.hInstance       = hInstance;
876
877 #ifndef LCC
878     Globals.hMainIcon       = ExtractIcon(Globals.hInstance, 
879                                         Globals.lpszIcoFile, 0);
880 #endif
881     if (!Globals.hMainIcon) {
882         Globals.hMainIcon = LoadIcon(0, MAKEINTRESOURCE(DEFAULTICON));
883     }
884
885     lstrcpy(Globals.szFindText,     "");
886     lstrcpy(Globals.szFileName,     "");
887     lstrcpy(Globals.szMarginTop,    "25 mm");
888     lstrcpy(Globals.szMarginBottom, "25 mm");
889     lstrcpy(Globals.szMarginLeft,   "20 mm");
890     lstrcpy(Globals.szMarginRight,  "20 mm");
891     lstrcpy(Globals.szHeader,       "&n");
892     lstrcpy(Globals.szFooter,       "Page &s");
893     lstrcpy(Globals.Buffer, "Hello World");
894
895     if (!prev){
896         class.style         = CS_HREDRAW | CS_VREDRAW;
897         class.lpfnWndProc   = NOTEPAD_WndProc;
898         class.cbClsExtra    = 0;
899         class.cbWndExtra    = 0;
900         class.hInstance     = Globals.hInstance;
901         class.hIcon         = LoadIcon (0, IDI_APPLICATION);
902         class.hCursor       = LoadCursor (0, IDC_ARROW);
903         class.hbrBackground = GetStockObject (WHITE_BRUSH);
904         class.lpszMenuName  = 0;
905         class.lpszClassName = className;
906     }
907
908     if (!RegisterClass (&class)) return FALSE;
909
910     /* Setup windows */
911
912
913     Globals.hMainWnd = CreateWindow (className, winName, 
914        WS_OVERLAPPEDWINDOW + WS_HSCROLL + WS_VSCROLL,
915        CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 0, 
916        LoadMenu(Globals.hInstance, STRING_MENU_Xx),
917        Globals.hInstance, 0);
918
919     Globals.hFindReplaceDlg = 0;
920
921     LANGUAGE_SelectByName(Globals.lpszLanguage);
922
923     SetMenu(Globals.hMainWnd, Globals.hMainMenu);               
924                         
925     ShowWindow (Globals.hMainWnd, show);
926     UpdateWindow (Globals.hMainWnd);
927
928     /* Set up dialogs */
929
930     /* Identify Messages originating from FindReplace */
931
932     Globals.nCommdlgFindReplaceMsg = RegisterWindowMessage("commdlg_FindReplace");
933     if (Globals.nCommdlgFindReplaceMsg==0) {
934        MessageBox(Globals.hMainWnd, "Could not register commdlg_FindReplace window message", 
935                   "Error", MB_ICONEXCLAMATION);
936     }
937
938     /* now handle command line */
939     
940     while (*cmdline && (*cmdline == ' ' || *cmdline == '-')) 
941     
942     {
943         CHAR   option;
944 /*      LPCSTR topic_id; */
945
946         if (*cmdline++ == ' ') continue;
947
948         option = *cmdline;
949         if (option) cmdline++;
950         while (*cmdline && *cmdline == ' ') cmdline++;
951
952         switch(option)
953         {
954             case 'p':
955             case 'P': printf("Print file: ");
956                       /* Not yet able to print a file */
957                       break;
958         }
959     }
960
961     /* Set up Drag&Drop */
962
963     DragAcceptFiles(Globals.hMainWnd, TRUE);
964
965     /* now enter mesage loop     */
966     
967     while (GetMessage (&msg, 0, 0, 0)) {
968         if (IsDialogMessage(Globals.hFindReplaceDlg, &msg)!=0) {
969           /* Message belongs to FindReplace dialog */
970           /* We just let IsDialogMessage handle it */
971         } 
972           else
973         { 
974           /* Message belongs to the Notepad Main Window */
975           TranslateMessage (&msg);
976           DispatchMessage (&msg);
977         }
978     }
979     return 0;
980 }