MFC SDI rich edit 2.0 control bolding words - c++

How would i go about formatting text in a rich edit 2.0 control? As of right now i just have a simple little MFC program with a single view and just one rich edit 2.0 control. It's currently empty but i want to insert some text into it.
The control itself is labeled as StringToChange2 and the member within my class is m_StringToChange2.
TCHAR INIValue2[256] = _T("Here is some random text!");
SetDlgItemText(StringToChange2, INIValue2);
So as it stands now, when i run my program it inserts the text into my control. How can i make a word bold from the whole string?
For example i just want it to say : "Here is some random text!"
As it stands now, i can make the whole control bold but I don't want the whole thing to be bold, just a word.
This Link has a very similar question to what I am asking but there is 2 things wrong with it. First, almost all the comments tell him to use a HTML control which i don't want to turn to yet. Second, the one person who did respond to him has such a long snippet of code i don't understand what's happening. The very last answer recommends he use word pad since it uses RTF?
I tried to insert RTF code into my INIValue2 but it won't take it. Unless I'm using it wrong, which could highly be the case.
I've been stalking MSDN and reading the functions, but my level of expertise with MFC and richedit control is very limited. If someone could post a small example of this, it doesn't even have to relate to my question , but something i could use as a base for.
Edit1: It's not that my INIValue2 doesn't take it, it's that when it appears on my single view - it shows everything - including all the RTF code and header.

You have to format the text using EM_SETCHARFORMAT message. In MFC, you can use CRichEditCtrl::SetSelectionCharFormat
First, declare CRichEditCtrl member data in your dialog or window class
CRichEditCtrl m_richedit;
In OnInitDialog put
m_richedit.SubclassDlgItem(IDC_RICHEDIT21, this);
Apply the CHARFORMAT as follows:
CHARFORMAT cf = { sizeof(cf) };
cf.dwEffects = CFM_BOLD;
cf.dwMask = CFM_BOLD;
m_richedit.SetSel(0,2);
m_richedit.SetSelectionCharFormat(cf);
You can use helper functions to make this easier. For example see this post
To assign RTF text directly, you must use EM_STREAMIN. For some reason MFC has no function for this, so you have to write your own function
DWORD __stdcall callback_rtf_settext(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb)
{
CString *psBuffer = (CString*)dwCookie;
if (cb > psBuffer->GetLength())
cb = psBuffer->GetLength();
for (int i = 0; i < cb; i++)
*(pbBuff + i) = (BYTE)psBuffer->GetAt(i);
*pcb = cb;
*psBuffer = psBuffer->Mid(cb);
return 0;
}
bool setrtf(CRichEditCtrl &edit, const CString &s)
{
EDITSTREAM es;
edit.SetSel(0, -1);
edit.Clear();
memset(&es, 0, sizeof(es));
es.dwCookie = (DWORD_PTR)&s;
es.pfnCallback = callback_rtf_settext;
edit.StreamIn(SF_RTF, es);
return es.dwError == 0;
}
usage:
setrf(m_richedit, L"\\rtf data...");

Related

Input Method Manager Functions - correct call order for Hiragana to Kanji candidates list c++ covnersion

I'm trying to convert the Hiaragana characters to candidates list (Kanji). Looks like it should be possible by using the Input Method Manager Functions but it doesn't work as I expected.
I installed the Japanese Language pack and I'm able to open IME in the text editor so some functionalities should be enabled.
In src we have sth like:
HIMC context = ImmCreateContext();//non-NULL result
bool result = ImmSetOpenStatus(context, true);//result == true
std::wstring wstr = L"こいび";//Hiragana string for conversion
result = ImmSetConversionStatus(context, IME_CMODE_JAPANESE, IME_SMODE_NONE);//result == true
result = ImmSetCompositionString(context, SCS_SETSTR, (LPVOID)wstr.c_str(), wstr.length(), NULL, 0);//result == true
DWORD listCount = 0;
DWORD lpdResult = 0;
lpdResult = ImmGetCandidateListCount(context, &listCount);//listCount == 0, lpdResult == 144
Looks like it works somehow but I'm always getting similar results (I checked it for standard letters, shorter JPN string and by creating std::string + conversion to wstring).
Unfortunately I didn't found example related to it so maybe I didn't call something or I called something in wrong order.
The MS documentation contains not too much details about it so I'm not sure what's wrong.
Imm function must use in window message loop.
for example,
https://learn.microsoft.com/en-us/windows/desktop/api/Imm/nf-imm-immgetcandidatelistcounta
look remark part.
"Applications typically call this function in response to an IMN_OPENCANDIDATE or IMN_CHANGECANDIDATE command."
when message loop type is "IMN_OPENCANDIDATE" or "IMN_CHANGECANDIDATE", you can use immgetcandidatelistcount function.

Wt WText Scrollbar

I have a little question about Wt. When I create WText (child of a WContainerWidget and having a long long text), Scrollbars are not created for this WText. I have tried many things like setting overflow (with setOverflow function of WContainerWidget), putting this text class into WScrollArea and etc..
If you have some kind of opinions about that problem, I would be glad.
Thanks everyone by now.
Mustafa
Edit: Code example
MyApplication::MyApplication(Wt::WEnvironment const &env)
: Wt::WApplication(env)
{
std::string str = "..." // a very long html string
auto text = new Wt::WText(str, Wt::XHTMLUnsafeText);
root()->setOverflow(OverflowAuto);
root()->addWidget(text);
}
You could use a Wt::WTextArea instead of WText, if its a large file. Its scrollable.

C++ SendMessage loop stop and end of line

Here is my dilemma. I have a program that uses SendMessage to get text from a chat program. Now when I did this in Visual Basic I just put the SendMessage in a loop and whenever the chat was updated so would my output.
This is not the case with C++. When I put the SendMessage in a loop it loops forever. Say the Chat is something like:
Apples
Bananas
Cheerios
Now lets say I run my program it finds the text and starts looping. Now in Visual Basic it would keep looping until it hit Cheerios and it would stop and wait until someone in the chat typed something else.
With C++ it would output:
Apples
Bananas
Cheerios
Apples
Bananas
Cheerios
...
And go on forever. Is there a way to stop this? I was thinking along the terms of EOF but that wouldn't help because as soon as it hit the last line it would go out of the loop and wouldn't pick up anything else that people typed in chat.
Here is the portion of the code that gets the text. Now not that I do have an loop that won't end until I set bLoop to True.
cout << " + found RichEdit20W window at: " << hwndRichEdit20W << endl << endl;
cout << "- get text " << endl;
bool bLoop = false;
int textLen = (int)SendMessage(hwndRichEdit20W, WM_GETTEXTLENGTH, 0, 0);
while (bLoop == false)
{
const int MAXSIZE = 32678;
wchar_t szBuf[MAXSIZE];
SendMessage(hwndRichEdit20W, WM_GETTEXT, (WPARAM)textLen, (LPARAM)szBuf);
wcout << szBuf;
}
Thanks for any help!
VB CODE UPDATE
This is how I did it in my VB program. This is for a game console and not a chat window but it should be the same concept right?
Note Do While PARENThwnd <> IntPtr.Zero The handle is never 0 so it will be an infinite loop.
Private Sub bwConsole_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bwConsole.DoWork
Dim PARENThwnd As IntPtr
Dim CHILDhwnd As IntPtr
PARENThwnd = FindWindow(Nothing, "ET Console")
If PARENThwnd = IntPtr.Zero Then
txtConsole.Text = "ET Console is not availble."
Else
Do While PARENThwnd <> IntPtr.Zero
CHILDhwnd = GetDlgItem(PARENThwnd, 100)
Dim Handle As IntPtr = Marshal.AllocHGlobal(32767)
Dim NumText As Integer = CInt(SendMessage(CType(CHILDhwnd, IntPtr), WM_GETTEXT, CType(32767 \ Marshal.SystemDefaultCharSize, IntPtr), Handle))
Dim Text As String = Marshal.PtrToStringAuto(Handle)
txtConsole.Text = Text
Marshal.FreeHGlobal(Handle)
txtConsole.SelectionStart = txtConsole.TextLength
txtConsole.ScrollToCaret()
rtxtDefinition.Text = ""
Call GetDefinitions(txtConsole.Text)
Loop
End If
End Sub
Also will not be able to answer any questions until later tonight. Off to work.
You never update your bLoop variable, causing the loop to run infinitely. If you want to break your loop, implement a loop termination condition that changes the value of bLoop.
A condensed version of your code does the following:
bool bLoop = false;
while ( bLoop == false ) {
// This loop never terminates, unless bLoop is set to true
}
It is somewhat hard to understand, why you are looping to begin with. I suppose you hope the SendMessage call would somehow guess that you don't want it to run, until some condition is met. There is no such magic built into functions.
To respond to text changes you should choose a different solution altogether: An event-based model. The supported way to implement this is through UI Automation.
The first thing you need to understand is that the two versions of the code are doing very different things with the contents of the chat window. The VisualBasic version is taking that text and stuffing it into a text control. This has the effect of replacing the existing text with the new text from the chat window so you never see duplicates. The C++ version outputs the text to the output stream (console) each time it retrieves it. Unlike the text control used in the VB version the output stream appends the text rather than replaces it.
To get around this you need to keep tract of the previous text retrieved from the chat window and only output the difference. This may mean appending new text from the chat or outputting the entire string. The code below maintains a string buffer and manages appending or replacing the history of the chat. It also creates a new string containing only the differences from the last change allowing you to easily deal with updates.
class ChatHistory
{
std::wstring history;
public:
std::wstring update(const std::wstring& newText)
{
const std::wstring::size_type historySize = history.size();
if(newText.compare(0, historySize, history.c_str()) == 0)
{
history.append(newText.c_str() + historySize, newText.size() - historySize);
return history.c_str() + historySize;
}
else
{
history = newText;
}
return newText;
}
};
Below are the necessary changes to your code to use it. I haven't had a chance to test it with your exact set up but it should work.
ChatHistory history;
while (bLoop == false)
{
const int MAXSIZE = 32678;
wchar_t szBuf[MAXSIZE];
SendMessage(hwndRichEdit20W, WM_GETTEXT, (WPARAM)textLen, (LPARAM)szBuf);
std::wcout << history.update(szBuf);
}
In both VBA and C++ that structure will cause an infinite loop.
It sounds like you want an infinite loop, but don't want an infinite print out of the contents of the buffer.
As #IInspectable has pointed out (thanks!) sendmessage will copy the contents of the chat program into the buffer at every iteration of the loop.
So, either purge the chat window or let the program know what content is new and needs to be printed.
I'm on a linux box at the moment, so can't test this very easily. Please let me know if the following works...
#include <cwchar> // so we can use wcschr function
const int MAXSIZE = 32678; // moved outside of the loop
wchar_t szBuf[MAXSIZE]; // moved outside of the loop
szBuf[0] = L'\0'; // set sentinel (wide NULL!)
wchar_t* end_printed_message = szBuf;
//pointer to the end of the content that has been printed
while (bLoop == false)
{
SendMessage(hwndRichEdit20W, WM_GETTEXT, (WPARAM)textLen, (LPARAM)szBuf);
wchar_t* end_message_buffer = wcschr(szBuf, L'\0'); // find end of the string using wchar version of strchr()
if(end_printed_message != end_message_buffer){ // if the buffer contains something new
wcout << end_printed_message; // print out the new stuff (start at old end)
end_printed_message = end_message_buffer; // update the pointer to the end of the content
}
}
I don't know what headers you've already included, hence the use of 'ol faithful <cstring>
As an aside (yes, I will still bang on about this) mixed use of (w)cout is generally bad and may cause problems.

Gtk::TextView with constant string

I am using Gtkmm 3+ and What I am trying to do is have the text buffer have the constant string "> " even if the user tries to delete it. In addition when the user pressed return it will automatically be there again. Basically have a constant string like a terminal does.
The only way I can think about about accomplishing this would be to connect to the delete and backspace signals so the user cannot delete the string. But, is there a better way?
so far this is the only way I can think of:
//in constructor
txt_view_i_.signal_event().connect(sigc::mem_fun(*this, &MainWindow::inputEvent));
//function
bool MainWindow::inputEvent(GdkEvent* event)
{
if((event->key.keyval == GDK_KEY_BackSpace || event->key.keyval == GDK_KEY_Delete) && buffer_input_->get_char_count() < 3)
return true;
return false;
}
But doesn't work perfectly, because if you type in more then 3 characters then go to the beginning of the line you can delete the constant string.
Another way I just thought about was to add a label to the TextView widget. I did that but, the user could still delete it. Here is the code for that:
Gtk::TextBuffer::iterator it = buffer_input_->get_iter_at_line(1);
Glib::RefPtr<Gtk::TextChildAnchor> refAnchor = buffer_input_->create_child_anchor(it);
Gtk::Label* lbl = Gtk::manage(new Gtk::Label("> "));
txt_view_i_.add_child_at_anchor(*lbl, refAnchor);
This is very similar, but not quite identical, to the question I answered here: You can create a GtkTextTag that makes its contents uneditable, and apply it from the beginning of the buffer up to and including the "> " prompt.
Then when you receive input, append your output to the buffer and then append a new prompt on the next line, and re-apply the tag to make the whole thing uneditable.
The links in the linked answer show some C code where this is done, even including a prompt. It's not Gtkmm or C++, but it should serve as an illustration.
Here is the code I used to solve it:
Glib::RefPtr<Gtk::TextBuffer::Tag> tag = Gtk::TextBuffer::Tag::create();
tag->property_editable() = false;
Glib::RefPtr<Gtk::TextBuffer::TagTable> tag_table = Gtk::TextBuffer::TagTable::create();
tag_table->add(tag);
buffer_input_ = Gtk::TextBuffer::create(tag_table);
txt_view_i_.set_buffer(buffer_input_);
scroll_win_i_.add(txt_view_i_);
Gtk::TextBuffer::iterator buffer_it_ = buffer_input_->begin();
buffer_input_->insert_with_tag(buffer_it_, "> ", tag);
Here is how I made it so that the user cannot edit before the constant string:
//connect to the mark set signal
buffer_input_->signal_mark_set().connect(sigc::mem_fun(*this, &MainWindow::setMark));
//make the box uneditable
void MainWindow::setMark(const Gtk::TextBuffer::iterator& it, const Glib::RefPtr<Gtk::TextBuffer::Mark>& mark)
{
if(it.get_offset() < 2)
txt_view_i_.set_editable(false);
else
txt_view_i_.set_editable(true);
}
Hopefully someone will find this useful.

Having trouble concatenating CStrings in an MFC calculator application

void CcalculatorDlg::OnBnClickedButton1()
{
CString grabData = _T("");
m_display.GetLine(0,grabData.GetBuffer(10),10);
grabData += _T("1");
m_display.SetWindowTextW(grabData.GetBuffer());
grabData.ReleaseBuffer();
}
I am trying to make a basic calculator application using MFC, and I am having some trouble with the number inputs.
Above is the code for when the "1" button is pressed. I want it to read in what's already being displayed in the display control, and then add a 1 onto the end of it like real calculators do. However I just can't get it to work.
Basically the first button press it works and changes the blank display (edit control) to a 1. But then successive presses don't continue to add 1's, and I cannot figure out why.
I think the problem in your code is that you tried to modify the string (concatenating _T("1")) after calling GetBuffer() but before calling ReleaseBuffer(). Moreover, you have unbalanced GetBuffer()/ReleaseBuffer() calls.
Assuming that m_display is a CEdit instance, you can try code like this (worked for me):
void CcalculatorDlg::OnBnClickedButton1()
{
// Get current text from edit control
// (assume a single-line edit control)
CString grabData;
m_display.GetWindowText(grabData);
// Concatenate "1"
grabData += L'1';
// Update edit control text
m_display.SetWindowText(grabData);
}
If you have a multi-line edit control and you want to grab the first (top-most) line using CEdit::GetLine(), you can use code like this (note that according to MSDN documentation, EM_GETLINE doesn't NUL-terminate the copied line, so you have to explicitly specify line length to ReleaseBuffer()):
//
// Read first line from edit control
//
CString grabData;
static const int kMaxBufferLength = 80;
wchar_t* buffer = grabData.GetBuffer(kMaxBufferLength + 1);
// Note '+ 1' for NUL string terminator (it seems that EM_GETLINE, which is
// wrapped by CEdit::GetLine(), doesn't NUL-terminate the returned string).
const int grabDataLength = m_display.GetLine(0, buffer, kMaxBufferLength);
grabData.ReleaseBuffer(grabDataLength);
// *After* calling ReleaseBuffer(), you can modify the string, e.g.:
grabData += L'1'; // concatenate "1"