--- programs/notepad/main.c	2006-05-23 08:49:17.000000000 -0400
+++ programs/notepad/main.c	2006-06-09 12:55:14.000000000 -0400
@@ -5,6 +5,7 @@
  *  Copyright 1997,98 Marcel Baur <mbaur@g26.ethz.ch>
  *  Copyright 2002 Sylvain Petreolle <spetreolle@yahoo.fr>
  *  Copyright 2002 Andriy Palamarchuk
+ *  Copyright 2006 Anoni Moose <moose.anoni@gmail.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -106,6 +108,8 @@
 
     case CMD_SEARCH:           DIALOG_Search(); break;
     case CMD_SEARCH_NEXT:      DIALOG_SearchNext(); break;
+    case CMD_REPLACE:          DIALOG_Replace(); break;
+    case CMD_GOTO:             DIALOG_Goto(); break;
                                
     case CMD_WRAP:             DIALOG_EditWrap(); break;
     case CMD_FONT:             DIALOG_SelectFont(); break;
@@ -176,6 +181,24 @@
 static LRESULT WINAPI NOTEPAD_WndProc(HWND hWnd, UINT msg, WPARAM wParam,
                                LPARAM lParam)
 {
+
+     if (msg == aFINDMSGSTRING) {
+	  Globals.lpfr = (LPFINDREPLACE)lParam;
+
+	  /* If the FR_DIALOGTERM flag is set,
+	   * invalidate the handle identifying the dialog box. */
+	  if (Globals.lpfr->Flags & FR_DIALOGTERM){
+	       Globals.hFindReplaceDlg = NULL;
+	       return 0;
+	  }
+
+	  if (Globals.lpfr->Flags & FR_FINDNEXT) {
+	       NOTEPAD_SearchText(Globals.lpfr);
+	  } else if (Globals.lpfr->Flags & FR_REPLACE || Globals.lpfr->Flags & FR_REPLACEALL) {
+	       NOTEPAD_ReplaceText(Globals.lpfr);
+	  }
+
+     } else {
     switch (msg) {
 
     case WM_CREATE:
@@ -247,9 +270,46 @@
     default:
         return DefWindowProc(hWnd, msg, wParam, lParam);
     }
+     }
     return 0;
 }
 
+BOOL CALLBACK GotoDlgProc(HWND hWnd, UINT msg, WPARAM wParam,
+			  LPARAM lParam) {
+
+     if (!IsDialogMessage(hWnd, (LPMSG)&msg)) {
+
+	  switch (msg) {
+
+	  case WM_INITDIALOG: {
+	       Globals.hGotoDlg = hWnd;
+	       SetDlgItemInt(hWnd, IDC_LINENUM, GetCurrentLineNum(Globals.hEdit), FALSE);
+	       SendDlgItemMessage(hWnd, IDC_LINENUM, EM_SETSEL, 0, -1);
+	       return TRUE;
+	  }
+
+	  case WM_COMMAND:
+
+	       switch(LOWORD(wParam)) {
+
+	       case IDOK: {
+		    if (NOTEPAD_GotoLine(GetDlgItemInt(hWnd, IDC_LINENUM, NULL, FALSE))) {
+			 EndDialog(hWnd, 0);
+		    }
+		    return TRUE;
+	       }
+
+	       case IDCANCEL:
+		    EndDialog(hWnd, 1);
+		    return TRUE;
+	       }
+	       break;
+	  }
+     }
+    return FALSE;
+}
+
+
 static int AlertFileDoesNotExist(LPCWSTR szFileName)
 {
    int nResult;
@@ -424,3 +484,243 @@
     }
     return msg.wParam;
 }
+
+/*
+ * NOTEPAD_SearchText: Call SearchText.
+ */
+VOID NOTEPAD_SearchText(LPFINDREPLACE find) {
+
+     INT result = SearchText(Globals.hEdit, find, 0);
+     SendMessage(Globals.hEdit, WM_SETFOCUS, 0, 0);
+
+     if (result == -1) {	/* text not found. */
+	  CHAR szResource[MAX_STRING_LEN];
+	  LoadStringA(Globals.hInstance, STRING_CANT_FIND_TEXT, szResource, SIZEOF(szResource));
+	  MessageBoxA(Globals.hEdit, szResource, NULL, MB_OK | MB_ICONINFORMATION);
+     } else {
+	  SendMessage(Globals.hEdit, EM_SETSEL, result, result+lstrlenW(find->lpstrFindWhat));
+	  SendMessage(Globals.hEdit, WM_SETFOCUS, 0, 0);
+     }
+}
+
+/*
+ * NOTEPAD_ReplaceText: Replace text.
+ */
+VOID NOTEPAD_ReplaceText(LPFINDREPLACE replace) {
+
+     static DWORD cur_pos = 0;
+
+     if (replace->Flags & FR_REPLACEALL) {
+	  while ((cur_pos = SearchText(Globals.hEdit, replace, 0)) != -1) {
+	       SendMessage(Globals.hEdit, EM_SETSEL, cur_pos, cur_pos+lstrlenW(replace->lpstrFindWhat));
+	       SendMessage(Globals.hEdit, EM_REPLACESEL, 0, (LPARAM)replace->lpstrReplaceWith);
+	  }
+
+     } else {
+	  DWORD sel_start = 0, sel_end = 0;
+
+	  SendMessage(Globals.hEdit, WM_SETFOCUS, 0, 0);
+	  SendMessage(Globals.hEdit, EM_GETSEL, (WPARAM)&sel_start, (LPARAM)&sel_end);
+
+	  if (sel_start != sel_end && sel_end > sel_start+1) {
+	       SendMessage(Globals.hEdit, EM_REPLACESEL, 0, (LPARAM)replace->lpstrReplaceWith);
+	       SendMessage(Globals.hEdit, EM_SETSEL, cur_pos, cur_pos+lstrlenW(replace->lpstrReplaceWith));
+	       SendMessage(Globals.hEdit, WM_SETFOCUS, 0, 0);
+	  }
+
+	  cur_pos = SearchText(Globals.hEdit, replace, 0);
+
+	  if (cur_pos == -1) {
+	       CHAR szResource[MAX_STRING_LEN];
+	       LoadStringA(Globals.hInstance, STRING_CANT_FIND_TEXT, szResource, SIZEOF(szResource));
+	       MessageBoxA(Globals.hEdit, szResource, NULL, MB_OK | MB_ICONINFORMATION);
+	  } else {
+
+	       if (sel_start == sel_end) {
+		    SendMessage(Globals.hEdit, EM_SETSEL, cur_pos, cur_pos+lstrlenW(replace->lpstrFindWhat));
+		    SendMessage(Globals.hEdit, WM_SETFOCUS, 0, 0);
+		    return;
+	       }
+
+	       NOTEPAD_SearchText(replace);
+	  }
+     }
+}
+
+/*
+ * SearchText: Actual text-searching code.
+ *
+ * Set pos to +zero if you want to start searching at that position.
+ * Otherwise, set it to 0, and we'll start at the correct place, making
+ * sure we take in account whether the user wants to search from up/down.
+ */
+INT SearchText(HWND hWnd, LPFINDREPLACE find, INT pos) {
+
+     BOOL going_down = find->Flags & FR_DOWN;
+     BOOL matching_case = find->Flags & FR_MATCHCASE;
+     BOOL whole_word = find->Flags & FR_WHOLEWORD;
+     BOOL match_found = FALSE;
+
+     DWORD nchars = 0, cur_byte = 0, needle_len = 0;
+     WCHAR *data = NULL;
+
+     data = GrabWindowTextW(hWnd, &nchars);
+     if (nchars <= 0) {
+	  fprintf(stderr, "Something weird happened; expected non-negative byte count, received %ld. Quitting.\n",
+		  nchars);
+     }
+
+     needle_len = lstrlenW(find->lpstrFindWhat);
+
+     cur_byte = (pos == 0 ? GetCursorPosition(hWnd) : pos);
+
+     do {
+
+	  if ( (matching_case && wcsncmp(data+cur_byte, (WCHAR *)find->lpstrFindWhat, needle_len) == 0) ||
+	       (!matching_case && wcsnicmp(data+cur_byte, (WCHAR *)find->lpstrFindWhat, needle_len) == 0) ) {
+
+	       /* We've potentially found a match. However, the user may only want to match
+		* the whole word, so let's do another check if that's so. */
+	       if (whole_word) {
+		    if (cur_byte == 0 || iswspace( data[ cur_byte-1 ] ) ) {
+			 if ( ( iswspace( data[ cur_byte+needle_len ] ) ||
+				cur_byte+needle_len >= nchars ) ) {
+			      match_found = TRUE;
+			      break;
+			 }
+		    }
+	       } else {
+		    match_found = TRUE;
+		    break;
+	       }
+	  }
+
+     } while (going_down == TRUE ? cur_byte++ < nchars : cur_byte-- > 0);
+
+     HeapFree(GetProcessHeap(), 0, data);
+     return match_found ? cur_byte : -1;
+}
+
+/*
+ * NOTEPAD_GotoLine: Go to certain line.
+ *
+ * The way we do this is different than how the native Notepad does it.
+ * In the native Notepad, you "can't" go to a different line if word wrap
+ * is enabled. However, we can with Wine! We actually load the file into
+ * RAM, and count up the newlines we pass. Once we're at the correct place,
+ * we move the cursor position. Take that, Windows! */
+INT NOTEPAD_GotoLine(INT num) {
+
+     DWORD pos = 0, nchars = 0, lines = 1, total_lines = 0;
+     CHAR *data = NULL;
+
+     /* I think this is annoying, but it's how "MS" Notepad works...
+      * I'd simply just rather go to the last line if num inputted was
+      * more than the # of lines. To do that, just remove the following
+      * "if" block. */
+     total_lines = SendMessage(Globals.hEdit, EM_GETLINECOUNT, 0, 0);
+
+     if (total_lines == -1) { return TRUE; }
+
+     if (num > total_lines) {
+	  CHAR szResource[MAX_STRING_LEN];
+	  LoadStringA(Globals.hInstance, STRING_LINE_NUM_OUTOFRANGE, szResource, SIZEOF(szResource));
+	  MessageBoxA(Globals.hEdit, szResource, NULL, MB_OK);
+
+	  SetDlgItemInt(Globals.hGotoDlg, IDC_LINENUM, total_lines, FALSE);
+	  SendDlgItemMessage(Globals.hGotoDlg, IDC_LINENUM, EM_SETSEL, 0, -1);
+	  SendDlgItemMessage(Globals.hGotoDlg, IDC_LINENUM, WM_SETFOCUS, 0, 0);
+	  return FALSE;
+     }
+
+     data = GrabWindowTextA(Globals.hEdit, &nchars);
+     if (nchars < 0) nchars = 0;
+
+     for (pos = 0; pos < nchars; ++pos) {
+	  if (data[pos] == '\n') {
+	       lines++;
+	  }
+
+	  if (lines == num || pos+1 >= nchars) {
+	       pos++;		/* get past the newline char */
+	       SendMessage(Globals.hEdit, EM_SETSEL, pos, pos);
+	       break;
+	  }
+     }
+     return TRUE;
+}
+
+/*
+ * GetCursorPosition: Get the cursor's position in the file.
+ */
+DWORD GetCursorPosition(HWND hWnd) {
+
+     DWORD pos = 0;
+     SendMessage(hWnd, EM_GETSEL, 0, (LPARAM)&pos);
+
+     if (pos < 0) pos = 0;
+     return pos;
+}
+
+/*
+ * GetCurrentLineNum: Get the current line number.
+ *
+ * FIXME: There's _gotta_ be a faster way...
+ */
+DWORD GetCurrentLineNum(HWND hWnd) {
+
+     DWORD pos = 0, lines = 1, cursor = 0;
+     CHAR *data = NULL;
+
+     cursor = GetCursorPosition(hWnd) + 1;
+     if (cursor <= 0) { return 1; }
+
+     data = GrabWindowTextA(hWnd, &cursor);
+
+     for (pos = 0; pos < cursor; ++pos) {
+	  if (data[pos] == '\n') {
+	       lines++;
+	  }
+     }
+     HeapFree(GetProcessHeap(), 0, data);
+     return lines;
+
+}
+
+/*
+ * GrabWindowTextW:
+ *
+ * Grab all of the window text, put it into a static WCHAR buffer, return it.
+ * If *nchars == 0/NULL, then we grab all of the text; otherwise, just *nchars+1.
+ */
+WCHAR *GrabWindowTextW(HWND hWnd, DWORD *nchars) {
+
+     WCHAR *data = NULL;
+     DWORD _nchars = 0;
+
+     if (nchars == NULL) nchars = &_nchars;
+
+     *nchars = (*nchars == 0 ? GetWindowTextLengthW(hWnd) + 1 : *nchars);
+     data = HeapAlloc(GetProcessHeap(), 0, (*nchars) * sizeof(WCHAR));
+     GetWindowTextW(hWnd, data, (*nchars));
+     return data;
+}
+
+/*
+ * GrabWindowTextA:
+ *
+ * Grab all of the window text, put it into a static CHAR buffer, return it.
+ * If *nchars == 0/NULL, then we grab all of the text; otherwise, just *nchars+1.
+ */
+CHAR *GrabWindowTextA(HWND hWnd, DWORD *nchars) {
+
+     CHAR *data = NULL;
+     DWORD _nchars = 0;
+
+     if (nchars == NULL) nchars = &_nchars;
+
+     *nchars = (*nchars == 0 ? GetWindowTextLengthA(hWnd) + 1 : *nchars);
+     data = HeapAlloc(GetProcessHeap(), 0, (*nchars) * sizeof(CHAR));
+     GetWindowTextA(hWnd, data, *nchars);
+     return data;
+}
--- programs/notepad/main.h	2006-05-23 08:49:17.000000000 -0400
+++ programs/notepad/main.h	2006-06-09 11:18:35.000000000 -0400
@@ -31,10 +31,12 @@
   HWND    hMainWnd;
   HWND    hFindReplaceDlg;
   HWND    hEdit;
+  HWND    hGotoDlg;
   HFONT   hFont; /* Font used by the edit control */
   LOGFONT lfFont;
   BOOL    bWrapLongLines;
   WCHAR   szFindText[MAX_PATH];
+  WCHAR   szReplaceText[MAX_PATH];
   WCHAR   szFileName[MAX_PATH];
   WCHAR   szFileTitle[MAX_PATH];
   WCHAR   szFilter[2 * MAX_STRING_LEN + 100];
@@ -46,6 +48,8 @@
   WCHAR   szFooter[MAX_PATH];
 
   FINDREPLACE find;
+  LPFINDREPLACE lpfr;
+  FINDREPLACE replace;
   HGLOBAL hDevMode; /* printer mode */
   HGLOBAL hDevNames; /* printer names */
 } NOTEPAD_GLOBALS;
@@ -53,3 +57,14 @@
 extern NOTEPAD_GLOBALS Globals;
 
 VOID SetFileName(LPCWSTR szFileName);
+VOID NOTEPAD_SearchText(LPFINDREPLACE lpfr);
+VOID NOTEPAD_ReplaceText(LPFINDREPLACE lpfr);
+INT NOTEPAD_GotoLine(INT num);
+INT SearchText(HWND hWnd, LPFINDREPLACE find, INT pos);
+BOOL CALLBACK GotoDlgProc(HWND hWnd, UINT msg, WPARAM wParam,
+				 LPARAM lParam);
+
+DWORD GetCursorPosition(HWND hWnd);
+DWORD GetCurrentLineNum(HWND hWnd);
+WCHAR *GrabWindowTextW(HWND hWnd, DWORD *nbytes);
+CHAR *GrabWindowTextA(HWND hWnd, DWORD *nbytes);
--- programs/notepad/dialog.c	2006-05-23 08:49:17.000000000 -0400
+++ programs/notepad/dialog.c	2006-06-09 12:29:27.000000000 -0400
@@ -4,6 +4,7 @@
  *  Copyright 1998,99 Marcel Baur <mbaur@g26.ethz.ch>
  *  Copyright 2002 Sylvain Petreolle <spetreolle@yahoo.fr>
  *  Copyright 2002 Andriy Palamarchuk
+ *  Copyright 2006 Anoni Moose <moose.anoni@gmail.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -29,6 +30,7 @@
 
 #include "main.h"
 #include "dialog.h"
+#include "settings.h"
 
 static const WCHAR helpfileW[] = { 'n','o','t','e','p','a','d','.','h','l','p',0 };
 
@@ -645,7 +648,10 @@
 
 VOID DIALOG_Search(VOID)
 {
+
+     if (Globals.find.hwndOwner == NULL) {
         ZeroMemory(&Globals.find, sizeof(Globals.find));
+     }
         Globals.find.lStructSize      = sizeof(Globals.find);
         Globals.find.hwndOwner        = Globals.hMainWnd;
         Globals.find.hInstance        = Globals.hInstance;
@@ -655,9 +661,9 @@
 
         /* We only need to create the modal FindReplace dialog which will */
         /* notify us of incoming events using hMainWnd Window Messages    */
-
         Globals.hFindReplaceDlg = FindText(&Globals.find);
         assert(Globals.hFindReplaceDlg !=0);
+
 }
 
 VOID DIALOG_SearchNext(VOID)
@@ -662,8 +668,37 @@
 
 VOID DIALOG_SearchNext(VOID)
 {
-    /* FIXME: Search Next */
+     if (Globals.find.lpstrFindWhat == NULL || 
+	 Globals.find.hwndOwner == NULL ||
+	 Globals.lpfr == NULL) {
     DIALOG_Search();
+     } else {
+	  Globals.hFindReplaceDlg = NULL;
+	  NOTEPAD_SearchText(Globals.lpfr);
+     }
+}
+
+VOID DIALOG_Replace(VOID) {
+
+     if (Globals.replace.hwndOwner == NULL) {
+	  ZeroMemory(&Globals.replace, sizeof(Globals.replace));
+     }
+     
+     Globals.replace.lStructSize = sizeof(Globals.replace);
+     Globals.replace.hwndOwner = Globals.hMainWnd;
+     Globals.replace.hInstance = Globals.hInstance;
+     Globals.replace.lpstrFindWhat = Globals.szFindText;
+     Globals.replace.wFindWhatLen = SIZEOF(Globals.szFindText);
+     Globals.replace.lpstrReplaceWith = Globals.szReplaceText;
+     Globals.replace.wReplaceWithLen = SIZEOF(Globals.szReplaceText);
+     
+     Globals.hFindReplaceDlg = ReplaceText(&Globals.replace);
+     assert(Globals.hFindReplaceDlg != 0);
+}
+
+VOID DIALOG_Goto(VOID) {
+
+     DialogBoxParam(Globals.hInstance, MAKEINTRESOURCE(DLG_GOTO), Globals.hMainWnd, GotoDlgProc, (LPARAM)NULL);
 }
 
 VOID DIALOG_HelpContents(VOID)
@@ -750,7 +785,8 @@
         case IDHELP:
         {
           /* FIXME: Bring this to work */
-          static const WCHAR sorryW[] = { 'S','o','r','r','y',',',' ','n','o',' ','h','e','l','p',' ','a','v','a','i','l','a','b','l','e',0 };
+          static const WCHAR sorryW[] = { 'S','o','r','r','y',',',
+					  ' ','n','o',' ','h','e','l','p',' ','a','v','a','i','l','a','b','l','e',0 };
           static const WCHAR helpW[] = { 'H','e','l','p',0 };
           MessageBox(Globals.hMainWnd, sorryW, helpW, MB_ICONEXCLAMATION);
           return TRUE;
--- programs/notepad/dialog.h	2006-05-23 08:49:17.000000000 -0400
+++ programs/notepad/dialog.h	2006-06-08 17:27:16.000000000 -0400
@@ -38,6 +38,8 @@
 
 VOID DIALOG_Search(VOID);
 VOID DIALOG_SearchNext(VOID);
+VOID DIALOG_Replace(VOID);
+VOID DIALOG_Goto(VOID);
 
 VOID DIALOG_SelectFont(VOID);
 
--- programs/notepad/En.rc	2006-05-23 08:49:17.000000000 -0400
+++ programs/notepad/En.rc	2006-06-09 12:05:09.000000000 -0400
@@ -4,6 +4,7 @@
  *  Copyright 1997,98 Marcel Baur <mbaur@g26.ethz.ch>
  *  Proofread 1998 by David Lee Lambert <lamber45@egr.msu.edu>
  *  Copyright 2002 Sylvain Petreolle <spetreolle@yahoo.fr>
+ *  Copyright 2006 Anoni Moose <moose.anoni@gmail.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -51,8 +52,12 @@
   MENUITEM "&Font...",          CMD_FONT
  }
 POPUP "&Search" {
-  MENUITEM "&Search\tCtrl+F",   CMD_SEARCH
-  MENUITEM "&Search next\tF3",  CMD_SEARCH_NEXT
+  MENUITEM "&Search...\tCtrl+F",   CMD_SEARCH
+  MENUITEM "Search &Next\tF3",  CMD_SEARCH_NEXT
+  MENUITEM SEPARATOR
+  MENUITEM "&Replace...\tCtrl+H",  CMD_REPLACE
+  MENUITEM SEPARATOR
+  MENUITEM "&Go To...\tCtrl+G",	CMD_GOTO
  }
 POPUP "&Help" {
   MENUITEM "&Contents",         CMD_HELP_CONTENTS
@@ -67,6 +72,17 @@
  }
 }
 
+DLG_GOTO DIALOG DISCARDABLE  0, 0, 100, 45
+STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
+FONT 8, "MS Shell Dlg"
+CAPTION "Go To Line"
+{
+    LTEXT           "Line Number:",   IDIGNORE,   5,   7,  50,  10
+    EDITTEXT        IDC_LINENUM,   59,  5,  35,  11,  WS_CHILD | WS_BORDER | WS_TABSTOP | ES_NUMBER
+    DEFPUSHBUTTON   "OK",     IDOK,      5,  25,  30,  15, WS_TABSTOP
+    PUSHBUTTON      "Cancel", IDCANCEL,  55,  25,  40,  15, WS_TABSTOP
+}
+
 /* Dialog `Page setup' */
 
 DIALOG_PAGESETUP DIALOG 0, 0, 225, 95
@@ -125,6 +141,8 @@
 STRING_OUT_OF_MEMORY,                   "Not enough memory to complete this \
 task. \nClose one or more applications to increase the amount of \nfree \
 memory."
+STRING_CANT_FIND_TEXT,			"Cannot find text."
+STRING_LINE_NUM_OUTOFRANGE,		"Line number out of range."
 
 }
 
--- programs/notepad/notepad_res.h	2006-05-23 08:49:17.000000000 -0400
+++ programs/notepad/notepad_res.h	2006-06-09 11:24:24.000000000 -0400
@@ -21,6 +21,7 @@
 
 #define MAIN_MENU               0x201
 #define DIALOG_PAGESETUP        0x202
+#define DLG_GOTO                0x204
 #define ID_ACCEL                0x203
 
 /* Commands */
@@ -43,6 +44,8 @@
 
 #define CMD_SEARCH              0x120
 #define CMD_SEARCH_NEXT         0x121
+#define CMD_REPLACE             0x122
+#define CMD_GOTO                0x123
 
 #define CMD_WRAP                0x119
 #define CMD_FONT                0x140
@@ -77,7 +80,12 @@
 #define STRING_NOTFOUND 0x17B
 #define STRING_OUT_OF_MEMORY 0x17C
 
+#define STRING_CANT_FIND_TEXT 0x17D
+#define STRING_LINE_NUM_OUTOFRANGE 0x17E
+
 #define IDS_LICENSE_CAPTION       0x180
 #define IDS_LICENSE               0x181
 #define IDS_WARRANTY_CAPTION      0x182
 #define IDS_WARRANTY              0x183
+
+#define IDC_LINENUM 0x199
--- programs/notepad/rsrc.rc	2006-05-23 08:49:17.000000000 -0400
+++ programs/notepad/rsrc.rc	2006-06-09 11:19:11.000000000 -0400
@@ -30,9 +30,11 @@
     "^A", CMD_SELECT_ALL
     "^C", CMD_COPY
     "^F", CMD_SEARCH
+    "^G", CMD_GOTO
     "^N", CMD_NEW
     "^O", CMD_OPEN
     "^P", CMD_PRINT
+    "^H", CMD_REPLACE
     "^S", CMD_SAVE
     "^V", CMD_PASTE
     "^X", CMD_CUT
