Why does select(QTextCursor::BlockUnderCursor) include an extra junk character? - c++

Windows 7 SP1
MSVS 2010
Qt 4.8.4
I am using QTextCursor to grab each block's text. I use select(QTextCursor::BlockUnderCursor) to grab the text and then go to the next block with movePosition(QTextCursor::NextBlock). But when I again select(QTextCursor::BlockUnderCursor) I get an extra junk character in the QString and the anchor has moved to the end of the previous block.
Using this for text.txt:
A
B
This code's comments walks through the issue and asks the questions:
#include <QTGui>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QMainWindow* window = new QMainWindow;
QTextEdit* editor = new QTextEdit(window);
QTextDocument* document = new QTextDocument(window);
editor->setDocument(document);
QFile file("test.txt");
if (file.open(QFile::ReadOnly | QFile::Text))
editor->setPlainText(file.readAll());
QTextBlock block = document->begin();
QTextCursor* cursor = new QTextCursor(document);
int pos = cursor->position(); // = 0
int anchor = cursor->anchor(); // = 0
cursor->select(QTextCursor::BlockUnderCursor);
pos = cursor->position(); // = 1
anchor = cursor->anchor(); // = 0
QString text = cursor->selectedText(); // = "A"
int size = text.size(); // = 1
cursor->movePosition(QTextCursor::NextBlock);
pos = cursor->position(); // = 2
anchor = cursor->anchor(); // = 2
cursor->select(QTextCursor::BlockUnderCursor);
pos = cursor->position(); // = 3
anchor = cursor->anchor(); // = 1 Why not 2?
text = cursor->selectedText(); // "B" in debugger
// but text.at(0) = junk & test.at(1) = "B"
size = text.size(); // = 2 Why? Why not 1?
return app.exec();
}

It is not junk. The first character includes the U+2029 paragraph separator (HTML: 
 PSEP). In other words, selecting the block includes the starting paragraph separator. The first block doesn't have a starting SEP. Therefore, need to exclude the first character if one wants to extract the text alone of subsequent blocks.

The navigation values has to do with the nature of a QTextBlock, and how navigating by block and what is determined by BlockUnderCursor. The documentation gives some insight into this:
http://doc.qt.digia.com/main-snapshot/qtextblock.html#details
Here is another part of the documentation seemed helpful to me:
http://doc.qt.digia.com/main-snapshot/qtextblockformat.html#details
I haven't experimented with what you found, but here are some of my thoughts on it:
In some ways I think of it like pressing Ctrl+Up or Ctrl+Down in Windows in a MS Word document. Some of it may have to do with the line endings you are using. "\r\n" v. "\n". I know that sometimes the "eof" character is weird to work with. Some documents and formats require a new line before the end of file character.
Hope that helps.

Related

QTextEdit::extraSelections do not work well on Linux/X11 when QTextEdit with wrapped lines has explicitly set small font size

I have recently found a bug in Qt on Linux and reported it to https://bugreports.qt.io/browse/QTBUG-108149 Tested on Ubuntu 20.04 and 22.04 with Qt 5.15.2, 6.2.4 and 6.4, all with X11. I also tested it on Windows 10 and macOS 12.6 and Wayland and these seem to work fine.
Here is a minimal reproducible example. The program should highlight every occurrence of "abcd" in the text edit. However if I set the font size of the text edit to 12 points or smaller, the words in every second line are NOT highlighted. If I do not specify the font size or set font point size of 13 or greater, the highlighting works fine. The same problem seems to be with QPlainTextEdit.
I get this (every second row not highlighted):
This is what I would expect (all rows highlighted):
#include <QApplication>
#include <QTextEdit>
void highlight(QTextEdit *textEdit)
{
QList<QTextEdit::ExtraSelection> highlights;
QString word = "abcd";
QString text = textEdit->toPlainText();
int pos = text.indexOf(word);
while (pos != -1)
{
int endPos = pos + word.length();
auto highlight = QTextEdit::ExtraSelection();
highlight.cursor = QTextCursor(textEdit->document());
highlight.cursor.setPosition(pos);
highlight.cursor.setPosition(endPos, QTextCursor::KeepAnchor);
highlight.format.setForeground(QApplication::palette().color(QPalette::Link));
highlights.append(highlight);
pos = text.indexOf(word, endPos);
}
textEdit->setExtraSelections(highlights);
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QTextEdit w;
// THE NEXT LINES!!!
// If point size 12 or smaller is used, then every second
// row in the text edit is not highlighted.
// If commented out, then everything works fine.
// If point size 13 or greater is used, then everything works fine.
QFont f;
f.setPointSize(12);
w.setFont(f);
w.setPlainText("abcd abcd abcd abcd abcd abcd abcd abcd "
"abcd abcd abcd abcd abcd abcd abcd abcd");
highlight(&w);
w.show();
return a.exec();
}
Any ideas for any workaround until this bug gets officially fixed?

Writing RTF to RichEditBox

I'm writing a program using WinRT and have a RichEditBox which I'm struggling to make behave the way I want.
The idea is that a user will type some input, the program will process this to some output, and put it on the next line, centred, and then await input on the next line, back left aligned.
I figured that to do this I need to read the box content, cut off the last part of the RTF syntax, add the output, and then put the syntax back. This mostly works but sometimes it gives two blank lines and then the output left-aligned.
Can anyone help?
Here's my function to write the output.
void MainPage::WriteOutput(std::wstring const& output)
{
hstring h_RTF;
hstring h_plain;
MainBox().Document().GetText(Text::TextGetOptions::FormatRtf, h_RTF);
//Get plain text version to help seperate RTF syntx
MainBox().Document().GetText(Text::TextGetOptions::None, h_plain);
//Convert hstring to wstring, remove new line characters from end of plain
std::wstring RTF = h_RTF.c_str();
std::wstring plain = h_plain.c_str();
plain.pop_back();
plain.pop_back();
//seperate last line
auto found = plain.find_last_of(L'\r');
if (found != std::wstring::npos)
plain = plain.substr(found + 1);
int searchLength = std::min<int>(plain.length(), 5);
std::wstring searchString = plain.substr(plain.length() - searchLength);
//find last line in RTF
found = RTF.find_last_of(searchString);
if (found == std::wstring::npos)
throw("String not found");
//cut up to end of last line
std::wstring before = RTF.substr(0, found + 1);
//cut after end of last line
std::wstring after = RTF.substr(found + 5);
//new stuff to add
std::wstring extra = L"\\par\\pard\\qc " + output;
//add strings to create new content
MainBoxContent = before + extra + after;
//put content in box
SetMainBoxContent();
MainBox().Focus(FocusState::Programmatic);
MainBox().TextDocument().Selection().EndOf(Text::TextRangeUnit::Window,false);
}

How to automatically put newline at text's end in FLTK Multiline_Input?

I am using FLTK 1.3.5 and I would like to use the Multiline_Input object. I wonder if there is a way to automatically put a newline when the inserted text reaches the end of the input field, instead of doing it manually (check the uploaded images for an example). Moreover, the newline should be put at the end of a word. I searched here on SO and on the web, but I was not able to find anything useful.
Here it is the code used to generate the images above.
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Multiline_Input.H>
int main(int argc, char **argv) {
Fl_Window *G_win = 0;
G_win = new Fl_Window(200,200,"Test multi input");
Fl_Multiline_Input* in;
in = new Fl_Multiline_Input(50,50,120,100," Test:");
G_win->end();
G_win->show(argc, argv);
return Fl::run();
}
With FLTK, you can always catch keyboard events, e.g. using (or redefining) keyboard events
A human being don't type very fast, and computers are fast. Your keyboard event handler could change the multiline content at every keystroke
We plan to do so (in summer 2020) in the RefPerSys project.
Following Basile's suggestion, I investigated more on handling events and then I came up with a solution.
I derivated a class from the original Fl_Multiline_Input which basically controls that the char[] in Fl_Multiline_input->value() does not overcome the maximum width given by the geometry (considering the current Fl_font). Maybe it is not the best solution but, hey, it works! Of course, more performant/elegant strategies are more than welcome.
The code is down below.
#include "mymulti.hpp"
/*
This is needed for converting a std::string
to a char[] (required by Fl_Input). Again,
maybe more performant alternative are out there...
*/
char* str2char(std::string S){
char *out;
out = (char*) malloc ((S.size()+1)*sizeof(char));
strcpy(out,&S[0]);
out[S.size()] = '\0';
return out;
}
mymulti::mymulti(int x, int y, int h, int l):Fl_Multiline_Input(x, y, h,l)
{}
int mymulti::handle(int event)
{
switch (event) {
// When a key is pressed and the widget is waiting for input
case FL_KEYBOARD:
return handle_key(event, Fl::event_key());
default:
return Fl_Multiline_Input::handle(event);
};
}
int mymulti::handle_key(int event, int key) {
if (key==FL_BackSpace)
// Allowing Fl_Multiline_Input to handle the delection of characters
return Fl_Multiline_Input::handle(event);
else{
// Converting to std::string just because manipulation is easer
std::string s(this->value());
std::string s2;
/*
Search for the last newline char:
the subsequent substring must be shorter
than the width of the input field
*/
std::size_t found = s.find_last_of("\n");
if (found<std::string::npos)
s2= s.substr (found);
else
s2 = s;
/*
Mean length of a char (lower case)
under the current fl_font. One can add
uppercase letters and/or numbers and/or
other symbols.
*/
double lc = fl_width("abcdefghijklmnopqrstuvwxyz")/26;
// Maximum length for the substring
int string_length = this->w()/lc -1;
// if the substring is longer than the max allowed length, then add a newline
if (s2.length()>string_length)
s+="\n";
// Update the input field
this->value(str2char(s));
return Fl_Multiline_Input::handle(event);
}
}

Qt- using getline function

I am using Qt 5.2.1 and here is part of a function that I want to use in my program -
while( getline(in,str,':')
{
getline(str,'\n');
int var = atoi(str.c_str());
}
my question is how can I implement this in qt?
I searched the docs a bit and found out about readline and split but I dont know how to use them
any help is much appreciated. :D
Edit - my first getline checks for ':' in the text file and the second one picks up the number (that comes after ':') and converts it into a integer and stores it in a variable.
2 edit :
Here's how my text file looks like...
500 - 1000 : 1
1000 - 1500 : 2
1500 - 2000 : 7
2000 - 2500 : 6
the 1, 2, 7, 6 are the values that I need in my program
I'm not totally sure what you're trying to do. If you're trying to read a file:
QFile file("/path/to/file.whatever");
if (!file.open(QIODevice::ReadOnly | QIODevice::Text) {
// error message here
return;
end
while (!file.atEnd()) {
QString line = in.readLine();
// now, line will be a string of the whole line, if you're trying to read a CSV or something, you can split the string
QStringList list = line.split(",");
// process the line here
}
QFile closes itself when it goes out of scope.
If you're trying to split a string based on the : delimiter you have there, use:
QStringList list = line.split(":");
EDIT:
Now that you defined what you want to do (read a like like this "value:integer"), you can easily do that with QStringList. Example:
QString input = "value:1";
QStringList tokens = input.split(":");
int second = tokens.at(1).toInt();
Of course, you'll need to use your own error checking, but this is an example of what I think you're trying to do.

What is the easiest way to parse an INI File in C++? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 1 year ago.
The community reviewed whether to reopen this question 1 year ago and left it closed:
Original close reason(s) were not resolved
Improve this question
I'm trying to parse an INI file using C++. Any tips on what is the best way to achieve this? Should I use the Windows API tools for INI file processing (with which I am totally unfamiliar), an open-source solution or attempt to parse it manually?
You can use the Windows API functions, such as GetPrivateProfileString() and GetPrivateProfileInt().
If you need a cross-platform solution, try Boost's Program Options library.
I have never parsed ini files, so I can't be too specific on this issue.
But i have one advice:
Don't reinvent the wheel as long as an existing one meets your requirements
http://en.wikipedia.org/wiki/INI_file#Accessing_INI_files
http://sdl-cfg.sourceforge.net/
http://sourceforge.net/projects/libini/
http://www.codeproject.com/KB/files/config-file-parser.aspx
Good luck :)
If you are already using Qt
QSettings my_settings("filename.ini", QSettings::IniFormat);
Then read a value
my_settings.value("GroupName/ValueName", <<DEFAULT_VAL>>).toInt()
There are a bunch of other converter that convert your INI values into both standard types and Qt types. See Qt documentation on QSettings for more information.
I use SimpleIni. It's cross-platform.
this question is a bit old, but I will post my answer. I have tested various INI classes (you can see them on my website) and I also use simpleIni because I want to work with INI files on both windows and winCE.
Window's GetPrivateProfileString() works only with the registry on winCE.
It is very easy to read with simpleIni. Here is an example:
#include "SimpleIni\SimpleIni.h"
CSimpleIniA ini;
ini.SetUnicode();
ini.LoadFile(FileName);
const char * pVal = ini.GetValue(section, entry, DefaultStr);
inih is a simple ini parser written in C, it comes with a C++ wrapper too. Example usage:
#include "INIReader.h"
INIReader reader("test.ini");
std::cout << "version="
<< reader.GetInteger("protocol", "version", -1) << ", name="
<< reader.Get("user", "name", "UNKNOWN") << ", active="
<< reader.GetBoolean("user", "active", true) << "\n";
The author has also a list of existing libraries here.
Have you tried libconfig; very JSON-like syntax. I prefer it over XML configuration files.
I ended up using inipp which is not mentioned in this thread.
https://github.com/mcmtroffaes/inipp
Was a MIT licensed header only implementation which was simple enough to add to a project and 4 lines to use.
If you are interested in platform portability, you can also try Boost.PropertyTree. It supports ini as persistancy format, though the property tree my be 1 level deep only.
Unless you plan on making the app cross-platform, using the Windows API calls would be the best way to go. Just ignore the note in the API documentation about being provided only for 16-bit app compatibility.
I know this question is very old, but I came upon it because I needed something cross platform for linux, win32... I wrote the function below, it is a single function that can parse INI files, hopefully others will find it useful.
rules & caveats:
buf to parse must be a NULL terminated string. Load your ini file into a char array string and call this function to parse it.
section names must have [] brackets around them, such as this [MySection], also values and sections must begin on a line without leading spaces. It will parse files with Windows \r\n or with Linux \n line endings. Comments should use # or // and begin at the top of the file, no comments should be mixed with INI entry data. Quotes and ticks are trimmed from both ends of the return string. Spaces are only trimmed if they are outside of the quote. Strings are not required to have quotes, and whitespaces are trimmed if quotes are missing. You can also extract numbers or other data, for example if you have a float just perform a atof(ret) on the ret buffer.
// -----note: no escape is nessesary for inner quotes or ticks-----
// -----------------------------example----------------------------
// [Entry2]
// Alignment = 1
// LightLvl=128
// Library = 5555
// StrValA = Inner "quoted" or 'quoted' strings are ok to use
// StrValB = "This a "quoted" or 'quoted' String Value"
// StrValC = 'This a "tick" or 'tick' String Value'
// StrValD = "Missing quote at end will still work
// StrValE = This is another "quote" example
// StrValF = " Spaces inside the quote are preserved "
// StrValG = This works too and spaces are trimmed away
// StrValH =
// ----------------------------------------------------------------
//12oClocker super lean and mean INI file parser (with section support)
//set section to 0 to disable section support
//returns TRUE if we were able to extract a string into ret value
//NextSection is a char* pointer, will be set to zero if no next section is found
//will be set to pointer of next section if it was found.
//use it like this... char* NextSection = 0; GrabIniValue(X,X,X,X,X,&NextSection);
//buf is data to parse, ret is the user supplied return buffer
BOOL GrabIniValue(char* buf, const char* section, const char* valname, char* ret, int retbuflen, char** NextSection)
{
if(!buf){*ret=0; return FALSE;}
char* s = buf; //search starts at "s" pointer
char* e = 0; //end of section pointer
//find section
if(section)
{
int L = strlen(section);
SearchAgain1:
s = strstr(s,section); if(!s){*ret=0; return FALSE;} //find section
if(s > buf && (*(s-1))!='\n'){s+=L; goto SearchAgain1;} //section must be at begining of a line!
s+=L; //found section, skip past section name
while(*s!='\n'){s++;} s++; //spin until next line, s is now begining of section data
e = strstr(s,"\n["); //find begining of next section or end of file
if(e){*e=0;} //if we found begining of next section, null the \n so we don't search past section
if(NextSection) //user passed in a NextSection pointer
{ if(e){*NextSection=(e+1);}else{*NextSection=0;} } //set pointer to next section
}
//restore char at end of section, ret=empty_string, return FALSE
#define RESTORE_E if(e){*e='\n';}
#define SAFE_RETURN RESTORE_E; (*ret)=0; return FALSE
//find valname
int L = strlen(valname);
SearchAgain2:
s = strstr(s,valname); if(!s){SAFE_RETURN;} //find valname
if(s > buf && (*(s-1))!='\n'){s+=L; goto SearchAgain2;} //valname must be at begining of a line!
s+=L; //found valname match, skip past it
while(*s==' ' || *s == '\t'){s++;} //skip spaces and tabs
if(!(*s)){SAFE_RETURN;} //if NULL encounted do safe return
if(*s != '='){goto SearchAgain2;} //no equal sign found after valname, search again
s++; //skip past the equal sign
while(*s==' ' || *s=='\t'){s++;} //skip spaces and tabs
while(*s=='\"' || *s=='\''){s++;} //skip past quotes and ticks
if(!(*s)){SAFE_RETURN;} //if NULL encounted do safe return
char* E = s; //s is now the begining of the valname data
while(*E!='\r' && *E!='\n' && *E!=0){E++;} E--; //find end of line or end of string, then backup 1 char
while(E > s && (*E==' ' || *E=='\t')){E--;} //move backwards past spaces and tabs
while(E > s && (*E=='\"' || *E=='\'')){E--;} //move backwards past quotes and ticks
L = E-s+1; //length of string to extract NOT including NULL
if(L<1 || L+1 > retbuflen){SAFE_RETURN;} //empty string or buffer size too small
strncpy(ret,s,L); //copy the string
ret[L]=0; //null last char on return buffer
RESTORE_E;
return TRUE;
#undef RESTORE_E
#undef SAFE_RETURN
}
How to use... example....
char sFileData[] = "[MySection]\r\n"
"MyValue1 = 123\r\n"
"MyValue2 = 456\r\n"
"MyValue3 = 789\r\n"
"\r\n"
"[MySection]\r\n"
"MyValue1 = Hello1\r\n"
"MyValue2 = Hello2\r\n"
"MyValue3 = Hello3\r\n"
"\r\n";
char str[256];
char* sSec = sFileData;
char secName[] = "[MySection]"; //we support sections with same name
while(sSec)//while we have a valid sNextSec
{
//print values of the sections
char* next=0;//in case we dont have any sucessful grabs
if(GrabIniValue(sSec,secName,"MyValue1",str,sizeof(str),&next)) { printf("MyValue1 = [%s]\n",str); }
if(GrabIniValue(sSec,secName,"MyValue2",str,sizeof(str),0)) { printf("MyValue2 = [%s]\n",str); }
if(GrabIniValue(sSec,secName,"MyValue3",str,sizeof(str),0)) { printf("MyValue3 = [%s]\n",str); }
printf("\n");
sSec = next; //parse next section, next will be null if no more sections to parse
}
Maybe a late answer..But, worth knowing options..If you need a cross-platform solution , definitely you can try GLIB,, its interesting.. (https://developer.gnome.org/glib/stable/glib-Key-value-file-parser.html)