I have an EditBox in a MFC-dialog. The user is supposed to enter a number. I'm trying to automatically add separators to the number while the user is inputting it:
When the number is more than 3 digits long, a separator is added between the hundreds and the thousands digit; a second one between the hundredthousands and the millions when it gets longer than 6 digits and so forth (so that 1234567 becomes 1,234,567 for example).
This is done in a function executed by ON_EN_CHANGE and basically works fine already. But the problem is that the caret position is set to the beginning of the EditBox once my function changes the length of the string in it, preventing continous typing.
I tried simulating the press of the end-key to send the caret to the end of the EditBox, which works as long as the user only enters a number from left to right. But it won't work when the user is trying to add, remove or edit digits in the middle of the number. I need the caret position at the exact spot of the number where it was before the user pressed a key.
I tried calculating the new caret position from the previous one (gotten with CEdit::GetSel()) and the previous length of the number:
OnEnChange()
{
int prevCursPos {HIWORD(m_editCtrl.GetSel())};
int prevStrLen {m_editCtrl.GetWindowTextLengthW()};
UpdateData(TRUE);
// Adding/Removing of separators as needed
UpdateData(FALSE);
int difference {m_editCtrl.GetWindowTextLengthW() - prevStrLen))};
if(difference > 0) // a separator has been added to the string
m_editCtrl.SetSel(-1, prevCursPos + 1);
}
However, the SetSel() function doesn't seem to have any effect. I also tried to send an EM_SETSEL message instead, but I couldn't figure out how to make that work either, the caret always resets itself to the beginning of the EditBox.
Does anyone have another idea on how to accomplish what I'm trying to do?
It is unclear how you are handling "/ Adding/Removing of separators as needed".
SetSel with the first parameter set to -1 will position the caret at the beginning of the string if the string changes after calling UpdateData(false)
Create CString type of the variable (m_csEdit for example) for this edit control and
int iLen = m_csDDx.GetLength();
m_editCtrl.SetSel(iLen, -1);
or
m_editCtrl.SetSel(iLen, iLen);
You can calculate the length differently from what I suggested.
Consider using masked edit control (maybe).
Forgot to mention. Use GetNumberFormatEx to format number with thousand delimiter.
Related
I'm using a CSpinButtonCtrl to modify the integer value of a buddy CEdit. Nice at it is the CSpinButtonCtrl places a thousands separator period in the number once the value gets higher than 1.000. Problem is that MFC's Direct Data Exchange only reads the digits before the thousands separator so that 1.000 in the CEdit becomes 1 in the int that the value is exchanged with.
Right now I'm just checking the CEdit for a thousands separator period and comma every time it changes and then possibly remove it like this:
//get current line from CEdit
CString line;
CEdit* pEdit = (CEdit*)GetDlgItem(nId);
pEdit->GetWindowText(line);
//replace periods and commas with nothing
line.Replace(".", "");
line.Replace(",", "");
//Write the CString back to the CEdit
pEdit->SetWindowText(line);
//Set the cursor to the end of the line again
pEdit->SetFocus();
pEdit->SetSel(-1);
But that is obviusly somewhat hacky and gives wrong behaviour when a user enters a digit not a the end of the CEdit. The CEdit is already set to "numerical only" but that is either being ignored by the CSpinButtonCtrl or a thousands separator period or comma are tolerated as numerical.
Isn't there a way to simply turn that thousands separator period placement off? I dont need it anyway.
In that case, from the documentation of CSpinButtonCtrl's create() function:
dwStyle
Specifies the spin button control's style
And one of those style is:
UDS_NOTHOUSANDS
Does not insert a thousands separator between every
three decimal digits.
so make sure your CSpinButtonCtrl has that style set to disable the thousand separators. You can probably do that from the Spin Button styles in the Properties window
When bringing in data into excel via whatever method (import, paste, ...) I sometimes get the following issue. At the beginning of the cell there is an extra space in front of the text. Now I know the usual procedures to handle this namely:
trim(cell number)
and if its not a space character
=TRIM(SUBSTITUTE(cell number,CHAR(160),CHAR(32)))
But this time both of these didn't work. I did try other substitute CHAR's.
AND the character at the beginning is just plain weird. When I go to the very beginning of the cell and try to delete it I must hit the delete key twice to remove one space! But when I go to the first character in the cell and instead hit backspace I only need to press it once.
What else can I do to eliminate this weird non-space whitespace character?
If cell A1 contains non-visible junk characters, you must identify them before you can remove them.
Pick some cell and enter:
=IFERROR(CODE(MID($A$1,ROWS($1:1),1)),"")
and copy down. This will give you the CHAR code for each character in A1
Then you can use SUBSTITUTE() to remove the offender.
Lets assume column A has text where some cells are good and some have text with the weird space like character at the front. So some cells we want to change and some we don't.
1) Create a one column table with one letter in each cell. I decided to go over to the right to column H for the table. So for example cell H1 has A, cell H2 has B and so on.
2) Get the length of the cell we want to edit. I've put this formula in cell B1.
=LEN(A1)
3) Test the cell for the first letter. This gives us which cell to change and which not. I've put this formula in cell C1.
=ISNA(VLOOKUP(LEFT(A1),$H$1:$H$26,1,0))
4) Change (or not depending on step 3) using RIGHT and the result from LEN.
=IF(B1,RIGHT(A1,B1-2),A1)
Notice that I have to subtract 2 spaces and not one? Like I said it was a strange character.
5) Repeat down the column.
If the first legitimate character in your string will be in the set [A-Za-z0-9] then you could use this formula:
=MID(A1,MIN(SEARCH({"a";"b";"c";"d";"e";"f";"g";"h";"i";"j";"k";"l";"m";"n";"o";"p";"q";"r";"s";"t";"u";"v";"w";"x";"y";"z";0;1;2;3;4;5;6;7;8;9},A1&"abcdefghijklmnopqrstuvwxyz1234567890")),99)
where 99 is longer than the longest string might be. If there are other legitimate starting characters, then add them to both the array constant and the string at the end.
If you might need to remove trailing spaces (char(32)), you can enclose the above in a TRIM function.
It seems that you can go back one character from current line in the console using \b. However, the console doesn't seem to be able to jump one line up.
I want to mark invalid user input red. After typping input, user presses Enter which put's unerasable new line in the console.
My plan was to do the following:
Check the input for validity.
If it's invalid, print input.length()+1 times \b
Turn console color red
Print the input, print \n
But, the \b will not jump back to the line where user input is. So I have plan B:
Remember the length of string that was before user input (query_string)
Check the input for validity.
If it's invalid, go line up (where the input was entered)
Jump to query_string.length() character
Turn console color red
Print the input, print \n
However, I don't know how to do this using the console API.
There are at least two ways you can do this.
One way is, as #chris implied in a comment, is to save the cursor position of where the user started typing. When you find bad input, you set the cursor back to that position and change the text attribute of the characters he entered.
You probably don't want to scroll the window back up one line. If you do, then the window will appear to "jump" when the user makes an error. It's a really jarring user interface experience. But if you want to try, you can call ScrollConsoleScreenBuffer.
Another way to do it would be to change the console mode so that it doesn't automatically echo characters when the user types them. Instead, you read each character individually, append it to your input buffer, and when the user presses Enter you validate. If the input is valid, you issue a newline to move to the next line. Otherwise you back up and highlight the erroneous input. This sounds like a lot more work, but it's not that difficult and it results in a much better UI experience.
I have a file containing (hundreds) of blocks of numbers like below;
This one is fine (16x20, correct number of rows and columns)
11111111111111111110
16666616666666661110
16111616111111162610
16111646111663132610
16162616261623132610
16162313261623132610
16162313261623132610
16162313261623132610
16162313261623132610
16162313261623132610
16162313261623132610
16162313261626132610
16166313661116632610
16111111111116132610
16666666666666136610
11111111111111111110
This one needs to be padded with trailing zeroes so it is (16x20)
111111111111111111
166616666666663661
166611111111111661
166666366663661661
113161111111161611
1316166666616161
1616162262616161
11616166112616161
16616166116616161
16616162262616161
16616166266616161
16616111161116161
1661666666666616111
1661666166163366661
1641666166166613661
1111111111111111111
I would like to pad them with zeroes so they are all like the first example. I'm aware of the regular expressions feature in notepad++ but am struggling to get it to work. I appreciate any help given.
You could do it via a macro.
First append a large number of zeroes to the end of each line using a macro.
Caret on the first entry
click record macro
press end
type out 20 zeroes
press down arrow
click stop recording
play the macro until all lines look like this
11111111111111111100000000000000000000000000000000000000000000
16661666666666366100000000000000000000000000000000000000000000
16661111111111166100000000000000000000000000000000000000000000
16666636666366166100000000000000000000000000000000000000000000
11316111111116161100000000000000000000000000000000000000000000
131616666661616100000000000000000000000000000000000000000000
161616226261616100000000000000000000000000000000000000000000
1161616611261616100000000000000000000000000000000000000000000
1661616611661616100000000000000000000000000000000000000000000
1661616226261616100000000000000000000000000000000000000000000
1661616626661616100000000000000000000000000000000000000000000
1661611116111616100000000000000000000000000000000000000000000
166166666666661611100000000000000000000000000000000000000000000
166166616616336666100000000000000000000000000000000000000000000
164166616616661366100000000000000000000000000000000000000000000
111111111111111111100000000000000000000000000000000000000000000
Then...
Caret on first line
click record
press home key
press the right arrow key 20 times
hold shift and press end key
press delete key
press down arrow
click stop recording
play the macro until all lines are processed
You could save the entire process as a single macro so its just a single click in the future.
I can give you a macro solution
go to the beginning of your text
select Macro/Start Recording
press end, press 0 16 times then press Home and down arrow key
select Macro/End Recording
You now have a macro to add sixteen zeros to the end of all lines.
Playback this macro on all lines.
You now have appended zeroes to all lines.
Pressing Alt key and using mouse select the required block(columns) of text you want and paste it into another empty notepad tab
help on column mode editing is there inside notepad ? / help contents menu
Good luck
You can use the plugin ConyEdit to do this.
With ConyEdit running in the background, follow these steps:
use the command line cc.aal 00000000000000000000 to append after lines with twenty zero character.
use the command line cc.gc 1/\d{20}/ to get the first column of regex match.
Looking to do this manualy and not progomaticly ?
Open Findreplace
Copy from the last to rhe first WITHOUT NUMBERS on a line so...
in this example
111111111111111111 <---from here
to here ---> 166616666666663661
166611111111111661
paste that into the fine ( yes your effecticly copying the return wich some applications allow you to manualy input others wont )
then in the replace box, type '0' then your return
Hit that magic replace all :D
This will then add a 0 every time it hits a new line, then add a new... new line....
edit : quickly reviewing another method a second to recover for alternate options :P give me 10
edit 2:
Ah ok somthing like this will work :P just tested it.
use [0-9] in the find replace. so if im looking for 123123123123 ( wich is 12 long ) and i need to buff i up to 20,
Your FIND must be in ()
so..
the find would be
([0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9] )
and the replace is referd to as \1 no the regex, this was my mistake
\100000000
tested and confirmed !dont forget YOU NEED MATCH ALL on, WRAP off!
And so on for your other numbers, Not sure if you can loop this with macros nd stuff :P but hope it helps more than you have now
two good resources.
http://blog.creativeitp.com/posts-and-articles/editors/understanding-regex-with-notepad/comment-page-1/
http://regexpal.com/
base on OP's comment: you could try an editor called vim/gvim
open your file in vim, then type:
:%s/.*/\=printf("%-20s",getline("."))/|%s/ *$/\=substitute(submatch(0)," ","0","g")/
don't forget pressing <Enter> after the above typing.
then you will see the text has been changed into what you want.
of course vim macro can work as well, however, I feel command better... :)
In Qt, I want to make something which will show the next char of the char input.
For example, I entered 'a' into a QTextEdit, it automatically turns to 'b' in another QTextEdit, and when I again enter 'b', it turns to 'c'. Which algorithm is perfect for this?
As mentioned in a comment, you want to do the following things (which are not code):
Detect a change in the first input box
Decide how to modify the second text box given the value of the first text box
Update the second text box
Step 2 might be (from your example) to ensure the text has more than one character and, assuming its a std::string, get text[text.length() -1] and Step 3 might be to get the text of the second text box, append that character and assign it.
This would work for a modifiable or non-modifiable second text box since it does not re-modify each character in the string -- just the added one.
But, you need to clearly define what you want to happen.
If you can modify the characters directly, you can set each character like tChar = iChar + 1. It'd get a little wonky if you put in something that isn't a letter (spaces or enter key). In that case you'd wrap it with if (iChar >= 'a' && iChar <= 'Z') { which uses the 'n' syntax to alias the actual number codes. Another method would be to subclass the Qtextedit control and overload the void Qtextedit::keyReleaseEvent(QKeyEvent* event) virtual function to intercept the key strokes and check that way. The comparison is similar, but you'd use Qt::Key_A instead of 'a' to check that it's a letter key, then modify the resulting char appropriately.
See also: http://doc.qt.nokia.com/4.7-snapshot/qtextedit.html