Having trouble concatenating CStrings in an MFC calculator application - c++

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"

Related

How to write white spaces in a CStdioFile?

I'm using a CStdioFile and trying to leave a number of white spaces before writing a CString.
I tried CStdioFile.Seek(iNumOfSpaces, CStdioFile::current) the i write the string.
the problem is that when I open the file in Notepad++ it writes NUL instead of the white spaces.
How to write the white spaces to be viewed as white not NUL?
Thanks in advance
You misunderstood what CFile::Seek() method does. It moves the file pointer to a specified position, absolutely or relatively. It does not add/modify the content of the file.
Instead you should use CString::Format() method that supports padding. Here is an example that shows how to use it:
CString s;
s.Format(_T("|%-10s|"), _T("Data")); // left-align
s.Format(_T("|%10s|"), _T("Data")); // right-align
The result string is going to look like:
|Data |
| Data|
Here is an example that shows how to implement dynamic (variable length) padding:
CString s;
int n = 10;
s.Format(_T("|%-*s|"), n, _T("Data")); // left-align
s.Format(_T("|%*s|"), n, _T("Data")); // right-align
1.
CString hh="i7mjmhb";
CString h(' ',31);
hh=h+hh;
2.
CString hh=L"i7mjmhb";
CString h(L' ',31);
hh=h+hh;
With files it's bad not to know forward is it ascii or unicode stuff

MFC SDI rich edit 2.0 control bolding words

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...");

push_back() in std::vector<std::string> overwriting current string

I'm trying to implement an undo function when inserting a new line for a very simple text editor as a project for class. In the program, the column is where the cursor currently is, and the row is the current line the vector is on.
I was able to successfully create my "insertNewLine()" function which uses a std::vector<std::string> that is able to display the text to the screen. This is how I implemented it:
void Editor::insertNewLine()
{
// get a substring
prevLine = lines[row - 1];
size_t tempSize = prevLine.size();
int lengthOffset = getSubstringOffset(tempSize, (column - 1));
std::string cutTemp = prevLine.substr((column - 1), lengthOffset);
lines[row - 1].erase(column - 1);
// after incrementing, row and amount of lines, initialize the new row
row++;
numberOfLines++;
column = 1;
lines.push_back(cutTemp); // insert substring into new line
}
Here is an example of what the current output looks like this (where | is the cursor):
hello world| (user enters hello world, column = 11, row = 1)
hello|world (user moves cursor to column 5, still on row 1)
(user presses button that calls insertNewLine())
hello
|world (splits where the cursor is to a new line, cursor begins at column 1)
Now, I am able to undo any other command, but when trying to undo a new line, I need to have the cursor return to the previous column, and push the word back where it originally was. I tried implementing that by doing this:
void Editor::undoNewLine()
{
std::string source = lines[row - 1]; // save current line
lines[row-1].clear(); // clear current line
row--; // revert up one row
numberOfLines--; // revert amount of lines
lines.push_back(source); // append
}
With this function, I expected the output to look like this (from the example above):
(user presses a button that calls undoNewLine())
hello|world
But, the problem is, this is the output I get from the current code:
(user presses a button that calls undoNewLine())
|world
Essentially, using push_back(source) overwrites whatever was originally there and brings the cursor to the front. I tried to increment column to the original position it was in before the undo stage, however, this didn't work either. I just ended up with this output:
(user presses a button that calls undoNewLine())
world|
So how should I try and implement this undo function? Any tips or ideas on what I'm doing wrong?
In your solution, you're erasing the previous line's content (hello) with the call to clear(). Instead, just append the current line. The string class makes this easy:
lines[row-1] += lines[row];
After that you can remove the current line with vector::erase.
Note
Be aware that this is might be inefficient, since all the lines below need to be repositioned.
If this does become an issue you can switch to std::list but then you lose random access to your lines.

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.