Style Buffer for the Fl_Text_Editor?

64 views
Skip to first unread message

Svets

unread,
Jul 20, 2017, 7:33:21 AM7/20/17
to fltk.general
I'm trying to create a text editor where I can select some text, and then press a button to Bold that text.  I'm going to use other formats too.

My approach is to use the add_modify_callback to access information about the change, then pass that information to global variables, then use that information in my 'Bold' callback  to bold the text I have selected.

I realize I will have to constantly update the style_buffer string and pass it to  style_buffer->text("String") .

Is there an easier way (than the explanation I have given) to do this?

I am imagining a scenario where I have pasted data into a text_editor and want to highlight sections of that data by Bolding them.

I would first have to get the existing style_buffer string wouldn't I.  How would I do that?

Thanks




Greg Ercolano

unread,
Jul 20, 2017, 3:44:12 PM7/20/17
to fltkg...@googlegroups.com
On 07/20/17 04:33, Svets wrote:
> I'm trying to create a text editor where I can select some text, and then press a button to *Bold *that text. I'm going to use other formats too.
>
> My approach is to use the add_modify_callback to access information about the change, then pass that information to global variables, then use that information in my '*Bold*' callback to bold the text I have selected.
>
> I realize I will have to constantly update the style_buffer string and pass it to *style_buffer->text("String") .*
> *
> *
> *Is there an easier way (than the explanation I have given) to do this?*
>
> I am imagining a scenario where I have pasted data into a text_editor and want to highlight sections of that data by Bolding them.
>
> *I would first have to get the existing style_buffer string wouldn't I. How would I do that?*


I haven't tried this, but see if it works.

IIRC, the style buffer is just like the regular text buffer,
so you can manipulate its contents just like text.

Both the style buffer and text buffer have to be the same size,
each 'character' in the style buffer affects the corresponding
character in the regular buffer.

In your case instead of changing the text buffer, you just
want to change the style buffer (e.g. from regular to bold).

Assuming you've highlighted the text in the regular text buffer:

1) Get the start/end positions from that buffer for the selected text

2) Create a style buffer string that's exactly that many chars
of the character type you've assigned as 'bold text'.
This will likely be a string that is just the same character
over and over again, e.g. "BBBB" if 4 characters are selected in (1).

3) Use the index values from (1) in a replace() operation
on the *style buffer*, using the text string in (2) as the replacement text.

This should let you replace the corresponding style buffer chars
for the text selection.

Pretty sure that's how it should work.

Svets

unread,
Jul 21, 2017, 1:26:52 AM7/21/17
to fltk.general
I think using the replace method in the style buffer is the key point ;)
Originally I was trying to think of how I would get the existing style_buffer for a editor full of text.  But hopefully now I don't need to :)

Thanks for the help

Svets

unread,
Jul 24, 2017, 7:49:25 AM7/24/17
to fltk.general

I'm having some problems with this.  
As per the below example, the Bold Button on the Window serves to bold selected text in the Text_Editor.

The problem is that when text has been formatted, it pushes all the formatting one or two spaces to the right, depending
on how many characters I am reformatting.

The idea of this is to select the characters you want to bold and then select the 'Button' to bold those characters.
Not sure where I have gone wrong.  I thought the replace method was to delete and then replace the chars.  It doesn't seem to do that, instead it seems to just move them along.    Any clues as to how to make this work?

Just type 123456789 in the editor.   
Select 89 in the editor.
Hit the 'Bold' button and 89 should show as Bold Red.

Now select 23 and hit the 'Bold' button.
23 will turn red and bold but the formatting on 89 will be pushed two spaces to the right.
You can see this if you type two more characters after the 89, for example 'AB'.  The 'AB' will turn bold but if
I type another character it won't turn bold because I've only pushed two formatted spaces to the right (which it shouldn't do).    It just illustrates the problem.  Anyone know how to make this work?
Thanks
Code below:








#include <iostream>
#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Text_Editor.h>
#include <FL/Fl_Text_Buffer.h>
#include <FL/Fl_Button.h>
#include <FL/Fl_Input.h>
#include <cstdlib>
#include <string>



using namespace std;



Fl_Double_Window *win;
Fl_Text_Editor *ted;
Fl_Button *buta;
Fl_Text_Buffer *buf;
Fl_Text_Buffer *style_buffer;


int position = 0;
int inserted = 0;
int deleted = 0;
const char* deleted_text;
 
int *start_s = NULL;
   
int *end_s = NULL;


Fl_Text_Editor::Style_Table_Entry styles[] = {{FL_RED,FL_BOLD,16}/*,{FL_BLUE,FL_BOLD,15},{FL_BLACK,FL_COURIER,13}*/};



void  change_cback(int  pos,            // I - Position of update
             
int  nInserted,      // I - Number of inserted chars
             
int  nDeleted,       // I - Number of deleted chars
             
int  nRestyled,  // I - Number of restyled chars
             
const char *deletedText,// I - Text that was deleted
             
void  *cbArg)
{

      position
= pos;
 

}





void change_text(Fl_Widget *wid, void *data)
{


string s_char;
int int_cnt = 0;
   
int n_chars_selected = strlen(buf->selection_text());
   
for(int i = 0; i < n_chars_selected; i++)
   
{
       s_char
+="A";
       int_cnt
+=i;
   
}




   
int start_pos = position;
   
int end_pos = 0;


   
if(n_chars_selected > 1)
   end_pos
= position+(n_chars_selected-1);
   
else
   end_pos
= position;




ted
->highlight_data(style_buffer,styles,1,'A',style_unfinished_cb,0);


string s = style_buffer->text();


style_buffer
->replace(start_pos,end_pos,s_char.c_str());


Fl::redraw();




}




void style_unfinished_cb(int,void*)
{
  cout
<< "Font Found" << endl;
}






int main()
{


    win
= new Fl_Double_Window(800,700,"Test_Window");
    ted
= new Fl_Text_Editor(50,50,600,500);
    buta
= new Fl_Button(50,600,80,40, "Bold");
    buf
= new Fl_Text_Buffer();
    ted
->buffer(buf);


    style_buffer
= new Fl_Text_Buffer();


    buf
->add_modify_callback(change_cback,ted);
    buta
->callback(change_text);


win
->show();
   
return(Fl::run()); // sets event loop
}








Greg Ercolano

unread,
Jul 24, 2017, 11:34:14 AM7/24/17
to fltkg...@googlegroups.com
On 07/24/17 04:49, Svets wrote:
> The problem is that when text has been formatted, it pushes all the
> formatting one or two spaces to the right, depending on how many
> characters I am reformatting.

I changed a bit here, but I think the key change was
setting up ted->highlight_data() in main(), instead of
in the callback.

The reason for this is on creation of the text editor,
you want the style buffer association setup.

Otherwise, switching gears on the no-style-buffer vs.
with-style-buffer during mid-edit comes as a surprise
to the internals.

I also removed some unused code, and added one needed bit
of code (the text_modified() callback) so that when the user
inserts new text, the style buffer is updated accordingly.

foo.cxx

Albrecht Schlosser

unread,
Jul 24, 2017, 1:15:51 PM7/24/17
to fltkg...@googlegroups.com
On 24.07.2017 13:49 Svets wrote:
>
> I'm having some problems with this.
> As per the below example, the Bold Button on the Window serves to bold
> selected text in the Text_Editor.
>
> The problem is that when text has been formatted, it pushes all the
> formatting one or two spaces to the right, depending
> on how many characters I am reformatting.
> [...]

See my attached test file for improved and (almost?) working code. After
my code was ready I realized that Greg had already posted his version.
The modifications we made to the code are very similar, but here are
some notes:

I left some test statements in the code to show the params and outcome
of change_cback() and change_text().

I *thought* that change_cback() could be called with both nDeleted and
nInserted > 0 in the case you select text and paste another text from
the clipboard over the selected text, but as you can see with my test
code it is called twice (first with nDeleted > 0, then with nInserted >
0). I don't think that this is documented (please check!), so I'd prefer
my version that would handle both > 0 in a single call as well.

I also removed unused variables, updated the style table, and put the
highlight_data() call into main.

I found some strange behavior when you:

(a) select some text
(b) click "Bold"
(c) click anywhere in the editor to unselect the text
(d) click "Bold" again w/o text selection.

In this case buf->primary_selection() returns the old selection, but
strlen(buf->selection_text()) == 0. The former seems to be wrong [1],
the latter is IMHO correct.

The following code in change_text() handles this correctly:

int start_pos = ts->start();
int end_pos = ts->start() + n_chars_selected; // should be: ts->end();

if (n_chars_selected > 0)
style_buffer->replace(start_pos, end_pos, s_char.c_str());

Instead of using ts->end() directly the end() is calculated, and then
the condition (n_chars_selected > 0) prevents calling the replacement
with an empty string. You could also just return if n_chars_selected == 0.

I didn't test Greg's code for this special behavior - I'll leave it to
the OP to compare and test...


[1] I wonder if the fact that primary_selection() returns a valid
selection (the old one) even if the selection has been "clicked away" is
a bug. Maybe we should check that in FLTK or file an STR at least...
editor_highlight.cxx

Albrecht Schlosser

unread,
Jul 24, 2017, 2:24:46 PM7/24/17
to fltkg...@googlegroups.com
On 24.07.2017 19:15 Albrecht Schlosser wrote:
>
> I *thought* that change_cback() could be called with both nDeleted and
> nInserted > 0 in the case you select text and paste another text from
> the clipboard over the selected text, but as you can see with my test
> code it is called twice (first with nDeleted > 0, then with nInserted >
> 0). I don't think that this is documented (please check!), so I'd prefer
> my version that would handle both > 0 in a single call as well.

Correction: Although I still don't know if the modify_callback can be
called with both mentioned arguments > 0, I see that Greg's code also
handles this correctly. Sorry.

> I found some strange behavior when you:
>
> (a) select some text
> (b) click "Bold"
> (c) click anywhere in the editor to unselect the text
> (d) click "Bold" again w/o text selection.
>
> In this case buf->primary_selection() returns the old selection, but
> strlen(buf->selection_text()) == 0. The former seems to be wrong [1],
> the latter is IMHO correct.

Correction: I found out that nothing is wrong with
Fl_Text_Buffer::primary_selection(). The returned selection contains a
flag that you can query with selected(), and if it is 0 then the
selection is invalid (no selected text). This *must* be used to check
for a valid selection.

> The following code in change_text() handles this correctly:

void change_text(Fl_Widget *wid, void *data) {

Fl_Text_Selection *ts = buf->primary_selection();
cout << "Selection = (" << ts->selected() << ','
<< ts->start() << ',' << ts->end() << ")" << endl;

if (!ts->selected()) {
cout << "No text selected, nothing to do!" << endl;
return;
}

int start_pos = ts->start();
int end_pos = ts->end();

string s_char;
for (int i = start_pos; i < end_pos; i++) {
s_char += "B";
}

style_buffer->replace(start_pos, end_pos, s_char.c_str());

cout << "change_text(" << start_pos << ',' << end_pos << ")"
<< ", length(style_buffer) = " << style_buffer->length() << endl;

ted->redraw();
}

> [1] I wonder if the fact that primary_selection() returns a valid
> selection (the old one) even if the selection has been "clicked away" is
> a bug. Maybe we should check that in FLTK or file an STR at least...

This is not a bug (see above), but I'll check the documentation to see
if we can document this better.


PS: in Greg's example I would replace

const Fl_Text_Selection *ts = buf->primary_selection();
int start = ts->start();
int end = ts->end();
// No selection? do nothing..
if (start == end)
return; // no selection? do nothing..
...

with

const Fl_Text_Selection *ts = buf->primary_selection();
// No selection? do nothing..

if (!ts->selected())

return; // no selection? do nothing..

int start = ts->start();
int end = ts->end();
...

(this is untested).

Svets

unread,
Jul 25, 2017, 4:17:37 PM7/25/17
to fltk.general, Albrech...@online.de
Thank you both for all the help, I sincerely appreciate this.  The last example works perfectly and I will apply the information below. 
I was getting some strange results using my version.   Some of the extra variables you removed were just left there from my testing and I forgot to remove them before putting this code up on this forum. 

Greg Ercolano

unread,
Jul 25, 2017, 5:51:47 PM7/25/17
to fltkg...@googlegroups.com
On 07/24/17 11:24, Albrecht Schlosser wrote:
> PS: in Greg's example I would replace
>
> const Fl_Text_Selection *ts = buf->primary_selection();
> int start = ts->start();
> int end = ts->end();
> // No selection? do nothing..
> if (start == end)
> return; // no selection? do nothing..
> ...
>
> with
>
> const Fl_Text_Selection *ts = buf->primary_selection();
> // No selection? do nothing..
> if (!ts->selected())
> return; // no selection? do nothing..
>
> int start = ts->start();
> int end = ts->end();
> ...

Yikes, primary_selection() isn't documented to return NULL.
As implemented it can't be NULL.

We should probably document what the behavior is when there's no
selection (start == end == 0).

Albrecht Schlosser

unread,
Jul 26, 2017, 6:17:20 AM7/26/17
to fltkg...@googlegroups.com
On 25.07.2017 23:51 Greg Ercolano wrote:
> On 07/24/17 11:24, Albrecht Schlosser wrote:
>> PS: in Greg's example I would replace
>>
>> const Fl_Text_Selection *ts = buf->primary_selection();
>> int start = ts->start();
>> int end = ts->end();
>> // No selection? do nothing..
>> if (start == end)
>> return; // no selection? do nothing..
>> ...
>>
>> with
>>
>> const Fl_Text_Selection *ts = buf->primary_selection();
>> // No selection? do nothing..
>> if (!ts->selected())
>> return; // no selection? do nothing..
>>
>> int start = ts->start();
>> int end = ts->end();
>> ...
>
> Yikes, primary_selection() isn't documented to return NULL.
> As implemented it can't be NULL.

Correct, it doesn't return NULL, but when the selection is invalidated,
then only the (internal) value of selected() is set to false. Currently,
as of FLTK 1.3.x, the start() and end() values are NOT cleared when the
selection is invalidated by calling 'selected(false)'.

> We should probably document what the behavior is when there's no
> selection (start == end == 0).

Currently (and this will be kept in FLTK 1.3 as-is) you must check
selected() before you compare start() and end() because the latter two
methods could return arbitrary values (those left behind by the last
selection).

I prepared documentation updates and I changed the return values of
start() and end() so they always return 0 if the selection is invalid.
That's obviously what users expect.

I'm going to commit these changes in FLTK 1.4 soon, after some more
tests and proof reading.

FYI: here are the "highlights":

- int start() const { return mStart; }
+ int start() const { return mSelected ? mStart : 0; }


- int end() const { return mEnd; }
+ int end() const { return mSelected ? mEnd : 0; }


int Fl_Text_Selection::position(int *startpos, int *endpos) const {
- if (!mSelected)
+ if (!mSelected) {
+ *startpos = 0;
+ *endpos = 0;
return 0;
+ }
*startpos = mStart;
*endpos = mEnd;
-
return 1;
}

There's more, particularly documentation, but this is OT here.

Albrecht Schlosser

unread,
Jul 26, 2017, 8:34:54 AM7/26/17
to fltkg...@googlegroups.com
On 26.07.2017 12:17 Albrecht Schlosser wrote:

> I prepared documentation updates and I changed the return values of
> start() and end() so they always return 0 if the selection is invalid.
> That's obviously what users expect.
>
> I'm going to commit these changes in FLTK 1.4 soon, after some more
> tests and proof reading.

Done, svn r12356.

I also added a new method length() that returns the length of the
selection and 0 if nothing is selected.
Reply all
Reply to author
Forward
0 new messages