Visual C++ CEdit Control - Why is the Insertion Point changed by SetWindowText() - c++

The following snippet is from the OnChange() handler of a multiline CEdit control, which has "WantReturn" set.
void DLG::OnChangeEditPrepareTape()
{
CString ss;
std::vector<char> aTape;
m_prepareTape.GetWindowText(ss);
m_prepareTape.SetWindowText(ss);
}
If the SetWindowText() is commented out, the user’s text builds up on the right, and all is well. But, with it in, the text insertion point moves to the left edge, and the user’s characters go in to the left of the existing characters..
I want to put some tinkering text between the two calls, and can get what I want by subclassing CEdit. But I’d be interested to know if there is a way of doing it by Get() & Set().
I’m using Visual C++ 6, with Service Pack 5. Eleven years old now, but then “Software does not wear out” as they say:-).

The insertion point is reset by SetWindowText() because, from the control's point of view, its whole text content has just been reset (possibly to the empty string), and both the insertion point and the current selection might not be meaningful enough to keep them around.
You can use GetSel() and SetSel() to implement this behavior yourself:
void DLG::OnChangeEditPrepareTape()
{
CString ss;
std::vector<char> aTape;
int start, end;
m_prepareTape.GetSel(start, end);
m_prepareTape.GetWindowText(ss);
// Tinker with the text...
m_prepareTape.SetWindowText(ss);
m_prepareTape.SetSel(start, end);
}

You can you use GetSel to retrieve the cursor position before you replace the text, and SetSel to place it in the same location afterwards.
void DLG::OnChangeEditPrepareTape()
{
CString ss;
int start, stop;
std::vector<char> aTape;
m_prepareTape.GetWindowText(ss);
m_prepareTape.GetSel(&start, &stop);
m_prepareTape.SetWindowText(ss);
m_prepareTape.SetSel(start, stop);
}
If you modify the text before you put it back in the text box, you can increment or decrement start (and end) accordingly.

Related

I need to use getline() every time the function is called. How to avoid repeating reading the same lines in txt? c++

I need to make a function that every time I click the button, the program read and act according to a line in the log_p.txt file.
However, if I put the read t process inside the function, every time it will read the first line.
void ai_fight::getfile()
{
std::ifstream t("log_p.txt");
ui->pushButton->setEnabled(false);
getline(t, rule);
print_rule(rule);
if(getline(t, p1hand)) print_p1hand(p1hand);
if(getline(t, p1p)) print_p1p(p1p);
if(getline(t, p2hand)) print_p2hand(p2hand);
if(getline(t, p2p)) print_p2p(p2p);
getline(t, announce);
if(announce=="1 eliminated"||announce=="0 eliminated")
{
getline(t, buf);
getline(t, buf);
getline(t, buf);
getline(t, buf);
getline(t, win);
print_win(win);
ui->pushButton->setEnabled(true);
}
else if(announce=="0 winning"||announce=="1 winning")
{
ui->pushButton->setEnabled(true);
}
else qDebug()<<"----------announcement error"<<endl;
ui->pushButton->setEnabled(true);
}
The simplest way is probably to store the ifstream as a class member.
void ai_fight::getfile()
{
if (!m_t.is_open())
m_t.open("log_p.txt");
...
You probably want to close the file when you reach EOF so that you can loop round.
Every time you open a file it will start reading it from the beginning. What you need is to remember the current position in file persistently between function calls. You can achieve it in (at least) two ways:
Open the file outside of the function (e.g. as a class member). This way the file will be opened during the whole execution and the ifstream object will remember the current position.
Store the current position in the file in some variable. You can use tellg and seekg methods to get and set the cursor position in file stream. Of course you need to store this value outside the function.

How do you abstract the information to be displayed in a screen?

I'm writing an application that requires me to write information to a TFT display (kinda like a gameboy display).
I'm outputting the information to the display line by line. The way I'm doing it now requires me to have a function for each different screen.
Like:
void displayWelcomeMessage();
void displayInsertCoinMessage();
void displayGoodByeMessage();
Each function follows this logic:
void displaWelcomeMessage()
{
writeline(0, "Hi David");
writeline(1, "Welcome");
writeline(2, "Back!");
}
Problem: I hate to have a different function for each screen. It's not scalable at all, imagine if I had 500 different screens.
How do I abstract the process of writing to the display? So that I end up with a single generic function responsible for writing to the display.
Thank you
Update
Following "Useless's" and Michael.P's advice, what I will probably end up doing is storing the format of each message in a file:
DisplayMessages.cfg
WELCOME_MESSAGE_1 = "Hi %name"
WELCOME_MESSAGE_2 = "Welcome"
WELCOME_MESSAGE_3 = "back!"
And in the code I will do something like:
using Message = std::vector<QString>;
void login(){
//Do Stuff...
QString line
Message welcomeMessage;
line=getMessageStructureFromCfg("WELCOME_MESSAGE_1").arg(userName); // loads "Hi %name" and replaces %name with the content of userName
welcomeMessage.pushBack(line); // pushes the first line to welcomeMessage
line=getMessageStructureFromCfg("WELCOME_MESSAGE_2"); // loads "Welcome"
welcomeMessage.pushBack(line); // pushes the second line to welcomeMessage
line=getMessageStructureFromCfg("WELCOME_MESSAGE_3"); // loads "back!"
welcomeMessage.pushBack(line); // pushes the third line to welcomeMessage
displayMessage(welcomeMessage);
}
void displayMessage(Message const &msg) {
int i = 0;
for (auto &line : msg) {
writeline(i, line);
i++;
}
}
Thank you all for your help!
PS: Further improvements can be made if the file containing the messages structure used JSON instead of plain text. This way you could just iterate the child members(the lines) of each message and process accordingly
How do you abstract the information to be displayed in a screen?
The same way you abstract any information - you move the data out of the code and into a data structure.
Your displayXMessage() functions perform a fixed sequence of calls on a fixed sequence of hardcoded string literals. So, split the algorithm from the data the usual way, by passing the data as an argument:
using Message = std::vector<std::pair<int, std::string>>;
void displayMessage(Message const &msg) {
for (auto &line : msg) {
writeline(line.first, line.second);
}
}
and call it with the appropriate data:
Message welcomeMsg { {0, "Hi David"},
{1, "Welcome"},
{2, "Back!"}
};
displayMessage(welcomeMsg);
I think you will find a solution to all those kind of problems by learning design patterns. In this case in particular. Strucural patterns seem to be what you are looking for; you will then have to pick the pattern which fits the most what you are trying to do.

CDialog update input box value

I am working with CDialogs and mfc. My dialog loads, and there are two input boxes and a button. When i click the button I want to have the values in the input boxes change. My current attempt is this
void
CInstanceNumberDlg::updateLeftRange(int i) {
GetDlgItem(IDC_2NDDERIV_WT)->SetWindowText((LPCTSTR)i);
UpdateData(false);
UpdateWindow();
}
I have also tried with other combinations of including UpdateData() and UpdateWindow(). This method is being reached with a valid integer, and that ID should be valid.
How can i get the value in the input box to be modified?
Thank you
Your problem is this line:
GetDlgItem(IDC_2NDDERIV_WT)->SetWindowText((LPCTSTR)i);
i is not a pointer to a string and so you should not be casting it to an LPCTSTR. What you need to do convert the value of i to a string and then pass a pointer to the resulting string to SetWindowText(). And I don't believe you need UpdateData() or UpdateWindow(). For example:
void CInstanceNumberDlg::updateLeftRange(int i)
{
TCHAR tszValue[32];
StringCchPrintf(tszValue, _countof(tszValue), TEXT("%d"), i);
GetDlgItem(IDC_2NDDERIV_WT)->SetWindowText(tszValue);
}

QValidator prevents intermediate typing

I have a QLineEdit that accepts a string that will be evaluated at a javascript expression like "[0,3]" and connected to fire changes using editingFinished(). I added a validator so the user couldn't leave the input with a bad expression, but apparently I'm misunderstanding how the validator works, because if I return QValidator::Invalid, when the expression wouldn't be valid, user's can't type any mistakes (character won't disappear on backspace). For example, temporarily changing "[0,3]" to "[0,]" to fill in another number.
I've tried changing the validator to return to QValidator::Intermediate on bad expressions thinking that would be a happy medium letter users alter text, but setting bad text back to its previous value on unfocused or return, but that seems to let user's put in anything that want. For example, they can type in "[0," and click on something else and the input still has "[0," in it as opposed to jumping back to the way it was. Am I misunderstanding how the intermediate type works?
QValidator::Invalid 0 The string is clearly invalid.
QValidator::Intermediate 1 The string is a plausible intermediate value.
QValidator::Acceptable 2 The string is acceptable as a final result;
i.e. it is valid.
Here is my current validator, which I just put on a QLineEdit:
class PointFValidator : public QValidator
{
QValidator::State validate(QString &input, int &position) const;
void fixup(QString &input) const;
};
QValidator::State PointFValidator::validate(QString &input, int &position) const
{
try {
evalPointF(input);
} catch (std::exception &e) {
return QValidator::Invalid;
}
return QValidator::Acceptable;
}
void PointFValidator::fixup(QString &input) const
{
}
And this is what actually tests the string to see if it's formatted correctly
QPointF evalPointF(QString s)
{
//initGuile();
QScriptEngine engine;
QString program = "function frame() { return 9; }\n\n" + s;
QScriptValue value = engine.evaluate(program);
QStringList pair = value.toString().split(",");
if (pair.length() < 2)
throw std::runtime_error("invalid pointf string");
return QPointF(pair[0].toFloat(), pair[1].toFloat());
}
Is the QValidator not what I need? Is it only for typing prevention? Do I need to listen to the change event, validate it myself, and set it back if it's not valid?
So trying to compile your code didn't work for me. The constness of the virtual functions in QValidator make it pretty much impossible to get your example code to compile.
So I would (like you mentioned at the end of your question) go and set up a signal to respond to the content changes of your QLineEdit, evaluate it, and then put the output somewhere helpful.
But based on the kinds of things you put in your evalPointF function, it looks like you are just writing an IDE for javascript.
Why not use the pattern that most IDE's already have of putting issues in another window, and using font's and formatting to modify the text, instead of actually changing the text?
Hope that helps.

segfault when trying to access a string member of a class

I have a class Message that has a std::string as a data member, defined like this:
class Message
{
// Member Variables
private:
std::string text;
(...)
// Member Functions
public:
Message(const std::string& t)
: text(t) {}
std::string getText() const {return text;}
(...)
};
This class is used in a vector in another class, like this:
class Console
{
// Member Variables
private:
std::vector<Message> messageLog;
(...)
// Member Functions
public:
Console()
{
messageLog.push_back(Message("Hello World!"));
}
void draw() const;
};
In draw(), there's an iterator that calls getText(). When it does, the program segfaults. I've determined that text is valid inside the Message constructor. However, I can't tell if it's valid from inside Console. I'm assuming it is, but if I try to inspect indices of Console's messageLog, gdb tells me this:
(gdb) p messageLog[0]
One of the arguments you tried to pass to operator[] could not be converted to what
the function wants.
Anyone know what's going on?
EDIT: here's draw(). TCODConsole is part of a curses library I'm using, and so this function prints each message in Console to a part of the curses screen. TL and BR are Point member objects (two ints) that tell where on the screen to draw Console. I left out parts of Message and Console in the original question to hopefully make things clearer, but if you need me to post the entire classes then I can. They aren't too long.
void Console::draw() const
{
int x = TL.getX(), y = TL.getY();
int width = BR.getX() - TL.getX();
int height = BR.getY() - TL.getY();
// draw the Console frame
TCODConsole::root->printFrame(x, y, width, height, true);
// print the Console's messages
vector<Message>::const_iterator it;
for(it=messageLog.begin(); it<messageLog.begin()+height-1; ++it)
{
string message = "%c" + it->getText();
TCODConsole::setColorControl(TCOD_COLCTRL_1,
it->getForeColor(),
it->getBackColor());
y += TCODConsole::root->printRectEx(x, y, width, height,
TCOD_BKGND_NONE,
TCOD_LEFT,
message.c_str(),
TCOD_COLCTRL_1);
}
}
My guess is that by the point you use it->getText(), the iterator is NULL. Add a check it != messageLog.end() when you walk the array, and before calling it->getText().
Is it definitely std::vector messageLog and not std::vector<Message> messageLog? That seems a bit odd.
What does the height have to do with the vector's index? You have:
messageLog.begin()+height-1;
Why are you adding the screen coordinate to the iterator? That seems to be your problem and you're most likely overindexing and that's why you're getting a SIGSEGV.
What you probably want is to simply iterate over all the messages in the vector and display them at a particular location on the screen. I see what you're trying to do, but if you're trying to calculate the screen boundary using the iterator you're definitely going about it the wrong way. Try running a counter or get messageLog.size() and then recalculate the height with each iteration. As for the loop just do:
for(it=messageLog.begin(); it!=messageLog.end(); ++it)
It's probably because the scope of the Message object created in the Console method is just the Console method. So, if your program is trying to access this object in another method, like draw, you will get this segmentation fault, since this object is deleted after the execution.
Try this (just insert a new keyword):
Console()
{
messageLog.push_back(new Message("Hello World!"));
}
In this case, the object is not deleted after Console's end.
Just remember to delete the objects created when your program doesn't need them anymore.