Jump to content

I want to make a Notepad alternative in C


Recommended Posts

IME? No, it's just a notepad.

I don't think I subclassed my RichEdit control, as I don't even know what that means.

EDIT: I looked it up after posting this message. However, my context menu still doesn't work. I don't seem to get the WM_CONTEXTMENU message either. I've verified that the new window procedure is called, though (which caused a stack error after lots of message boxes popping up...).

My code.

Edited by BenoitRen
Link to comment
Share on other sites


I did some research. You don't need to subclass for this purpose (and as you found it seems not to work). There are two ways to get the message:

1) Use SendMessage( hEdit, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS ). The parent will get a WM_NOTIFY/EN_MSGFILTER for all mouse events. Filter for WM_RBUTTONDOWN. http://msdn.microsoft.com/en-us/library/bb787974(VS.85).aspx

2) More powerful: Add some code to the messagepump to filter the WM_RBUTTONDOWN.

while(GetMessage(&Msg, NULL, 0, 0) > 0) {
TranslateMessage(&Msg);
if( Msg.message ==WM_RBUTTONDOWN && hEdit == GetFocus() )
{
/* Popup menu */
}
DispatchMessage(&Msg);
}

(BTW, your messagepump is not completely safe. Have a look here)

(BTW2, you know you can create menu's in a resourcefile and load/create them witch LoadMenu()?)

Link to comment
Share on other sites

IME? No, it's just a notepad.

I don't follow... Notepad supports localized texts. AFAIK, on native systems, it supports IME. It doesn't seem to support 9x-style installed IME, though.

On the topic of subclassing, it simply means using:

// In global
WNDCLASS richedit_wc;
// In init
GetClassInfo(NULL, RICHEDIT_CLASS, &richedit_wc);

Somewhere in initialization, creating a message proc with:

LRESULT CALLBACK EditProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) {
//...
}
return CallWindowProc(richedit_wc.lpfnWndProc, hwnd, message, wParam, lParam);
}

The applying it to your control using something like:

// Creating the control (replace with your creation code)
HWND edit = CreateWindow(RICHEDIT_CLASS, NULL, WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_AUTOVSCROLL | ES_MULTILINE, rect->left, rect->top, rect->right-rect->left+1, rect->bottom-rect->top+1, parent, (HMENU) id, (HINSTANCE) GetWindowLong(parent, GWL_HINSTANCE), NULL);
// Setting the new proc...
SetWindowLong(edit, GWL_WNDPROC, (LONG)EditProc);

As for WM_CONTEXTMENU, you will need to forward WM_RBUTTONUP and WM_RBUTTONDOWN to DefWindowProc in order for the message to be generated. It may also be worthwhile to test if Shift+F10 works. This may also warrant for appropriate forwarding of messages.

For more details see this little bit of info on WM_CONTEXTMENU in RichEdit, and this description of WM_CONTEXTMENU.

Also, if you want, I can post my little code snippet for adding IME to a RichEdit control.

Link to comment
Share on other sites

Thanks Mijzelf. I've taken approach 2. It seems to be the most efficient. I've also improved my message loop.

Now how can I have the context menu appear with the top left corner on the place my mouse is pointing at? It appears either above or to the left now.

The RichEdit control has some weird behaviour. It doesn't show the horizontal scroll bar until I start typing. All the appropriate flags are set, though.

you know you can create menu's in a resourcefile and load/create them witch LoadMenu()?

Yes. Would this be more efficient?

Don't know what to make of this IME stuff. As far as I know, on Win9x, it's an IE module.

Refreshed source file.

Link to comment
Share on other sites

If you ask me, placing message handling of a specific control in the main message loop is just plain wrong. It's the sort of hacking you'd expect a Mac-developer to do on Windows, "because it's not quite the same". Creating your own window proc is not that difficult, nor is setting it to your control.

Don't know what to make of this IME stuff. As far as I know, on Win9x, it's an IE module.

It's available for Riched20.dll. I've noticed you're using Riched32.dll which is slightly different. Not sure if it works with 32.

At any rate, supporting IME is fairly simple. You just need to load up a small COM object which offers a TranslateMessage alternative (if loading fails, you just use the normal TranslateMessage), plus handling of WM_SETFOCUS and WM_KILLFOCUS in the RichEdit control.

It basically enables the creation of multi-byte characters with multiple key-strokes, for languages where 102-keys just doesn't quite cut it.

One more thing, if GetMessage returns "-1" then it's probably a good idea to leave the loop, not just destroy the window and keep going. After all, having already failed once, there's no reason to believe it would successfully return the WM_DESTROY or WM_QUIT which would be posted by a normal DestroyWindow. A "return -1;" or "throw;" may be more appropriate there.

Link to comment
Share on other sites

Now how can I have the context menu appear with the top left corner on the place my mouse is pointing at? It appears either above or to the left now.
WM_RBUTTONDOWN gives the mouse position in client coords, but TrackPopupMenu needs them in screen coords. So you'll have to use the ClientToScreen() function to convert it.
Would this be more efficient?
Your code will not run faster, and the executable will not be smaller. But your code will be cleaner, and it's easier to translate your program when all language dependend stuff is in the resources.
Link to comment
Share on other sites

Creating your own window proc is not that difficult, nor is setting it to your control.

But as I already said, it doesn't work for what I want to do!

You are only testing for WM_RBUTTONDOWN when g_hRichEdit is focused. You can just as easily test for WM_RBUTTONDOWN in the window proc of the rich edit control for a similar effect.

The behavior would slightly differ: If you right-click on the main window's border or title while the rich edit is focused, currently it opens a pop-up menu, but it won't if the WM_RBUTTONDOWN is done in the rich edit control. This behavior can be mimicked by having WM_RBUTTONDOWN also detected in the main window's window proc. Although I doubt this behavior is desired, correct me if I'm wrong.

Also, instead of actually detecting WM_RBUTTONDOWN yourself, you can forward it to DefWindowProc from the rich edit's proc to get it to generate real WM_CONTEXTMENU messages itself. Your code would then be able to detect Shift+F10 and Context-Key as well. Although you would only care about Context-Key if you have a 104-keys keyboard.

Link to comment
Share on other sites

The behavior would slightly differ: If you right-click on the main window's border or title while the rich edit is focused, currently it opens a pop-up menu, but it won't if the WM_RBUTTONDOWN is done in the rich edit control.

Actually, I tested this, and it doesn't.

Also, instead of actually detecting WM_RBUTTONDOWN yourself, you can forward it to DefWindowProc from the rich edit's proc to get it to generate real WM_CONTEXTMENU messages itself.

Again, this is what I was doing in the first place! Everything was forwarded to DefWindowProc. I only tried to catch the WM_CONTEXTMENU message, which I never got.

The RichEdit control isn't acting funny anymore, by the way. Weird.

EDIT: Actually, it still is. Depends on the file.

My main menu is now a resource. The context menu appears at the right place now, too!

Edited by BenoitRen
Link to comment
Share on other sites

The RichEdit control has some weird behaviour. It doesn't show the horizontal scroll bar until I start typing

The RichEdit control isn't acting funny anymore, by the way. Weird.

EDIT: Actually, it still is. Depends on the file.

Maybe it helps to do a InvalidateRect( g_hRichEdit, 0, TRUE ) after SetWindowText( g_hRichEdit, ... )

Link to comment
Share on other sites

  • 1 month later...

I'm back to developing this. My main hurdle that demotivates me is that I don't know how to make my window dialogs the same as NotePadEx and Wordpad. Is there an easy way to replicate them? Like extracting the resources?

I've fixed the long-standing line number problem. It always was off by one, since control counts starting from 0.

I've tried doing InvalidateRect(g_hRichEdit, 0, TRUE); to solve the scrollbar problem. It seems to help a bit (a bit more is accesible), but it doesn't entirely fix the problem.

Also, I'm trying to implement the "This file has been changed, do you wish to save it?" paradigm. But RichEdit controls normally don't send the EN_CHANGE message that I require for this. Maybe I need to subclass the control after all? This is confusing.

EDIT: I figured it out. Send a WM_SETEVENTMASK message to the control with the ENM_CHANGE mask.

Edited by BenoitRen
Link to comment
Share on other sites

Is there an easy way to replicate them? Like extracting the resources?

There are several ways. You can extract them with a program like 'Resource Hacker'. This will give an .rc file which you can compile with your project. You can also load them dynamically from Wordpad.

HMODULE hWordpad = LoadLibrary( "Wordpad.exe" );</P>
DialogBox( hWordpad, MAKEINTRESOURCE( resourcevalue ), hParent, DlgProc );</P>
FreeLibrary( hWordpad );

Pro of this approach is that your program will automatically use the systems language. Con is that your program will fail when Wordpad is not installed.

Link to comment
Share on other sites

Yeah, I figured I could use Resource Hacker, but I was asking in case that the dialogs I wanted are not a resource... which it turns out they aren't. :( They aren't resources in either Wordpad, NotepadEx or plain Notepad.

EDIT: I quickly figured to look in the DLLs that Wordpad uses, and it looks like the two dialogs I'm after are part of COMDLG32.DLL.

Edited by BenoitRen
Link to comment
Share on other sites

Using the comdlg32.dll resources I was able to shave off an entire kB off my executable. Of course, now it's growing again.

Anyway, something's been puzzling me. In NotepadEx, when you search, you see the RichEdit control highlighting the text while the search dialog is still focussed. To simulate this, I focus the RichEdit control after searching, but of course the search dialog isn't focussed anymore then. How do I achieve the same behaviour? I'm using modeless dialogs, by the way.

Also, is there a way to find out which window type you're working with? Then I could set the right HWND handle of the dialog I'm closing to NULL while using the same message loop for both the search and replace windows. I've looked at GetWindow, but it doesn't seem to do what I want. I use these global handles so only one window of the same type can be opened.

EDIT: I'm almost done with the Replace functionality. But I must be missing something obvious. Relevant piece of code:

HWND hReplaceText = GetDlgItem(hwnd, IDC_REPLACE_TEXT);
DWORD dwReplaceTextLen = GetWindowTextLength(hReplaceText);
LPSTR pszReplaceText = GlobalAlloc(GPTR, dwReplaceTextLen + 1);
GetWindowText(hReplaceText, pszReplaceText, dwSearchTextLen + 1);
SendMessage(g_hRichEdit, EM_REPLACESEL, TRUE, pszReplaceText);
GlobalFree(pszReplaceText);

My compiler complains: "Error E2342 Kladblok.c 201: Type mismatch in parameter 'lParam' (wanted 'long', got 'signed char * *') in function ReplaceDlgProc".

It's probably something to do with the difference between LPSTR and LPCTSTR, but I'm clueless. I've already tried passing the address instead, but it doesn't work.

Edited by BenoitRen
Link to comment
Share on other sites

Also, is there a way to find out which window type you're working with?

GetClassName(), maybe?

My compiler complains: "Error E2342 Kladblok.c 201: Type mismatch in parameter 'lParam' (wanted 'long', got 'signed char * *') in function ReplaceDlgProc".

I suppose it's the SendMessage() line? SendMessage() has four parameters, HWND, UINT, WPARAM and LPARAM. Your compiler doesn't cast a pointer silently to an integer value, so you'll have to do it self:

SendMessage(g_hRichEdit, EM_REPLACESEL, TRUE, (LPARAM)pszReplaceText);

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...