Add RMB pulldown menu to Fl_Help_View, #75

- append underscores to private varaibles per CMP
- add public Fl_Help_View::copy() and Fl_Help_View::text_selected()
- add public Fl_Help_View::copy_menu_text
- fixes some of the focus handling
- add pulldown menu to copy selected text
This commit is contained in:
Matthias Melcher 2024-07-29 14:03:16 +02:00
parent e7b8a24685
commit 089f31018b
2 changed files with 153 additions and 91 deletions

View File

@ -235,20 +235,20 @@ class FL_EXPORT Fl_Help_View : public Fl_Group { // Help viewer widget
Fl_Scrollbar scrollbar_, ///< Vertical scrollbar for document
hscrollbar_; ///< Horizontal scrollbar
static int selection_first;
static int selection_last;
static int selection_push_first;
static int selection_push_last;
static int selection_drag_first;
static int selection_drag_last;
static int selected;
static int draw_mode;
static int mouse_x;
static int mouse_y;
static int current_pos;
static Fl_Help_View *current_view;
static Fl_Color hv_selection_color;
static Fl_Color hv_selection_text_color;
static int selection_first_;
static int selection_last_;
static int selection_push_first_;
static int selection_push_last_;
static int selection_drag_first_;
static int selection_drag_last_;
static int selected_;
static int draw_mode_;
static int mouse_x_;
static int mouse_y_;
static int current_pos_;
static Fl_Help_View *current_view_;
static Fl_Color hv_selection_color_;
static Fl_Color hv_selection_text_color_;
void initfont(Fl_Font &f, Fl_Fontsize &s, Fl_Color &c) { f = textfont_; s = textsize_; c = textcolor_; fstack_.init(f, s, c); }
@ -286,6 +286,8 @@ private:
public:
static const char *copy_menu_text;
Fl_Help_View(int xx, int yy, int ww, int hh, const char *l = 0);
~Fl_Help_View();
/** Returns the current directory for the text in the buffer. */
@ -383,6 +385,12 @@ public:
void scrollbar_size(int newSize) {
scrollbar_size_ = newSize;
}
// Check if the user selected text in this view.
int text_selected();
// If text is selected in this view, copy it to a clipboard.
int copy(int clipboard=1);
};
#endif // !Fl_Help_View_H

View File

@ -50,6 +50,7 @@
#include <FL/Fl_Shared_Image.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Pixmap.H>
#include <FL/Fl_Menu_Item.H>
#include "Fl_Int_Vector.H"
#include "Fl_String.H"
@ -130,6 +131,14 @@ static const char * const broken_xpm[] =
static Fl_Pixmap broken_image(broken_xpm);
/** [this text may be customized at run-time] */
const char *Fl_Help_View::copy_menu_text = "Copy";
static Fl_Menu_Item rmb_menu[] = {
{ NULL, 0, NULL, (void*)1 }, // Copy
{ NULL }
};
//
// Simple margin stack for Fl_Help_View::format()...
//
@ -210,51 +219,51 @@ img->draw()
// We don't put the offscreen buffer in the help view class because
// we'd need to include platform.H in the header...
static Fl_Offscreen fl_help_view_buffer;
int Fl_Help_View::selection_first = 0;
int Fl_Help_View::selection_last = 0;
int Fl_Help_View::selection_push_first = 0;
int Fl_Help_View::selection_push_last = 0;
int Fl_Help_View::selection_drag_first = 0;
int Fl_Help_View::selection_drag_last = 0;
int Fl_Help_View::selected = 0;
int Fl_Help_View::draw_mode = 0;
int Fl_Help_View::mouse_x = 0;
int Fl_Help_View::mouse_y = 0;
int Fl_Help_View::current_pos = 0;
Fl_Help_View *Fl_Help_View::current_view = 0L;
Fl_Color Fl_Help_View::hv_selection_color;
Fl_Color Fl_Help_View::hv_selection_text_color;
int Fl_Help_View::selection_first_ = 0;
int Fl_Help_View::selection_last_ = 0;
int Fl_Help_View::selection_push_first_ = 0;
int Fl_Help_View::selection_push_last_ = 0;
int Fl_Help_View::selection_drag_first_ = 0;
int Fl_Help_View::selection_drag_last_ = 0;
int Fl_Help_View::selected_ = 0;
int Fl_Help_View::draw_mode_ = 0;
int Fl_Help_View::mouse_x_ = 0;
int Fl_Help_View::mouse_y_ = 0;
int Fl_Help_View::current_pos_ = 0;
Fl_Help_View *Fl_Help_View::current_view_ = 0L;
Fl_Color Fl_Help_View::hv_selection_color_;
Fl_Color Fl_Help_View::hv_selection_text_color_;
/*
* This function must be optimized for speed!
*/
void Fl_Help_View::hv_draw(const char *t, int x, int y, int entity_extra_length)
{
if (selected && current_view==this && current_pos<selection_last && current_pos>=selection_first) {
if (selected_ && current_view_==this && current_pos_<selection_last_ && current_pos_>=selection_first_) {
Fl_Color c = fl_color();
fl_color(hv_selection_color);
fl_color(hv_selection_color_);
int w = (int)fl_width(t);
if (current_pos+(int)strlen(t)<selection_last)
if (current_pos_+(int)strlen(t)<selection_last_)
w += (int)fl_width(' ');
fl_rectf(x, y+fl_descent()-fl_height(), w, fl_height());
fl_color(hv_selection_text_color);
fl_color(hv_selection_text_color_);
fl_draw(t, x, y);
fl_color(c);
} else {
fl_draw(t, x, y);
}
if (draw_mode) {
if (draw_mode_) {
int w = (int)fl_width(t);
if (mouse_x>=x && mouse_x<x+w) {
if (mouse_y>=y-fl_height()+fl_descent()&&mouse_y<=y+fl_descent()) {
int f = (int) current_pos;
if (mouse_x_>=x && mouse_x_<x+w) {
if (mouse_y_>=y-fl_height()+fl_descent()&&mouse_y_<=y+fl_descent()) {
int f = (int) current_pos_;
int l = (int) (f+strlen(t)); // use 'quote_char' to calculate the true length of the HTML string
if (draw_mode==1) {
selection_push_first = f;
selection_push_last = l;
if (draw_mode_==1) {
selection_push_first_ = f;
selection_push_last_ = l;
} else {
selection_drag_first = f;
selection_drag_last = l + entity_extra_length;
selection_drag_first_ = f;
selection_drag_last_ = l + entity_extra_length;
}
}
}
@ -556,11 +565,11 @@ Fl_Help_View::draw()
if (!value_)
return;
if (current_view == this && selected) {
hv_selection_color = FL_SELECTION_COLOR;
hv_selection_text_color = fl_contrast(textcolor_, FL_SELECTION_COLOR);
if (current_view_ == this && selected_) {
hv_selection_color_ = FL_SELECTION_COLOR;
hv_selection_text_color_ = fl_contrast(textcolor_, FL_SELECTION_COLOR);
}
current_pos = 0;
current_pos_ = 0;
// Clip the drawing to the inside of the box...
fl_push_clip(x() + Fl::box_dx(b), y() + Fl::box_dy(b),
@ -613,7 +622,7 @@ Fl_Help_View::draw()
fl_xyline(xx + x() - leftline_, yy + y() + 1,
xx + x() - leftline_ + ww + xtra_ww);
}
current_pos = (int) (ptr-value_);
current_pos_ = (int) (ptr-value_);
xx += ww;
if ((fsize + 2) > hh)
@ -631,7 +640,7 @@ Fl_Help_View::draw()
if (underline) fl_xyline(xx + x() - leftline_, yy + y() + 1,
xx + x() - leftline_ + buf.width());
buf.clear();
current_pos = (int) (ptr-value_);
current_pos_ = (int) (ptr-value_);
if (line < 31)
line ++;
xx = block->line[line];
@ -662,7 +671,7 @@ Fl_Help_View::draw()
if (underline) fl_xyline(xx + x() - leftline_, yy + y() + 1,
xx + x() - leftline_ + ww);
xx += ww;
current_pos = (int) (ptr-value_);
current_pos_ = (int) (ptr-value_);
}
needspace = 0;
@ -673,7 +682,7 @@ Fl_Help_View::draw()
while (isspace((*ptr)&255))
ptr ++;
current_pos = (int) (ptr-value_);
current_pos_ = (int) (ptr-value_);
}
}
@ -705,7 +714,7 @@ Fl_Help_View::draw()
ptr ++;
// end of command reached, set the supposed start of printed eord here
current_pos = (int) (ptr-value_);
current_pos_ = (int) (ptr-value_);
if (buf.cmp("HEAD"))
head = 1;
else if (buf.cmp("BR"))
@ -964,7 +973,7 @@ Fl_Help_View::draw()
needspace = 0;
ptr ++;
current_pos = (int) (ptr-value_);
current_pos_ = (int) (ptr-value_);
}
else if (isspace((*ptr)&255))
{
@ -982,7 +991,7 @@ Fl_Help_View::draw()
}
ptr ++;
if (!pre) current_pos = (int) (ptr-value_);
if (!pre) current_pos_ = (int) (ptr-value_);
needspace = 1;
}
else if (*ptr == '&') // process html entity
@ -1036,7 +1045,7 @@ Fl_Help_View::draw()
hv_draw(buf.c_str(), xx + x() - leftline_, yy + y());
if (underline) fl_xyline(xx + x() - leftline_, yy + y() + 1,
xx + x() - leftline_ + ww);
current_pos = (int) (ptr-value_);
current_pos_ = (int) (ptr-value_);
}
}
@ -2837,7 +2846,7 @@ void Fl_Help_View::follow_link(Fl_Help_Link *linkp)
/** Removes the current text selection. */
void Fl_Help_View::clear_selection()
{
if (current_view==this)
if (current_view_==this)
clear_global_selection();
}
/** Selects all the text in the view. */
@ -2845,18 +2854,18 @@ void Fl_Help_View::select_all()
{
clear_global_selection();
if (!value_) return;
current_view = this;
selection_drag_last = selection_last = (int) strlen(value_);
selected = 1;
current_view_ = this;
selection_drag_last_ = selection_last_ = (int) strlen(value_);
selected_ = 1;
}
void Fl_Help_View::clear_global_selection()
{
if (selected) redraw();
selection_push_first = selection_push_last = 0;
selection_drag_first = selection_drag_last = 0;
selection_first = selection_last = 0;
selected = 0;
if (selected_) redraw();
selection_push_first_ = selection_push_last_ = 0;
selection_drag_first_ = selection_drag_last_ = 0;
selection_first_ = selection_last_ = 0;
selected_ = 0;
}
char Fl_Help_View::begin_selection()
@ -2865,18 +2874,18 @@ char Fl_Help_View::begin_selection()
if (!fl_help_view_buffer) fl_help_view_buffer = fl_create_offscreen(1, 1);
mouse_x = Fl::event_x();
mouse_y = Fl::event_y();
draw_mode = 1;
mouse_x_ = Fl::event_x();
mouse_y_ = Fl::event_y();
draw_mode_ = 1;
current_view = this;
current_view_ = this;
fl_begin_offscreen(fl_help_view_buffer);
draw();
fl_end_offscreen();
draw_mode = 0;
draw_mode_ = 0;
if (selection_push_last) return 1;
if (selection_push_last_) return 1;
else return 0;
}
@ -2885,38 +2894,44 @@ char Fl_Help_View::extend_selection()
if (Fl::event_is_click())
return 0;
// printf("old selection_first=%d, selection_last=%d\n",
// selection_first, selection_last);
// Give this widget the focus during the selection process. This will
// deselect other text selection and make sure, we receive the Copy
// keyboard shortcut.
if (Fl::focus()!=this)
Fl::focus(this);
int sf = selection_first, sl = selection_last;
// printf("old selection_first_=%d, selection_last_=%d\n",
// selection_first_, selection_last_);
selected = 1;
mouse_x = Fl::event_x();
mouse_y = Fl::event_y();
draw_mode = 2;
int sf = selection_first_, sl = selection_last_;
selected_ = 1;
mouse_x_ = Fl::event_x();
mouse_y_ = Fl::event_y();
draw_mode_ = 2;
fl_begin_offscreen(fl_help_view_buffer);
draw();
fl_end_offscreen();
draw_mode = 0;
draw_mode_ = 0;
if (selection_push_first < selection_drag_first) {
selection_first = selection_push_first;
if (selection_push_first_ < selection_drag_first_) {
selection_first_ = selection_push_first_;
} else {
selection_first = selection_drag_first;
selection_first_ = selection_drag_first_;
}
if (selection_push_last > selection_drag_last) {
selection_last = selection_push_last;
if (selection_push_last_ > selection_drag_last_) {
selection_last_ = selection_push_last_;
} else {
selection_last = selection_drag_last;
selection_last_ = selection_drag_last_;
}
// printf("new selection_first=%d, selection_last=%d\n",
// selection_first, selection_last);
// printf("new selection_first_=%d, selection_last_=%d\n",
// selection_first_, selection_last_);
if (sf!=selection_first || sl!=selection_last) {
if (sf!=selection_first_ || sl!=selection_last_) {
// puts("REDRAW!!!\n");
return 1;
} else {
@ -2947,7 +2962,7 @@ static unsigned int command(const char *cmd)
void Fl_Help_View::end_selection(int clipboard)
{
if (!selected || current_view!=this)
if (!selected_ || current_view_!=this)
return;
// convert the select part of our html text into some kind of somewhat readable UTF-8
// and store it in the selection buffer
@ -2995,7 +3010,7 @@ void Fl_Help_View::end_selection(int clipboard)
case CMD('d','d', 0 , 0 ): src = "\n - "; break;
}
int n = (int) (s-value_);
if (src && n>selection_first && n<=selection_last) {
if (src && n>selection_first_ && n<=selection_last_) {
while (*src) {
*d++ = *src++;
}
@ -3016,7 +3031,7 @@ void Fl_Help_View::end_selection(int clipboard)
}
}
int n = (int) (s2-value_);
if (n>selection_first && n<=selection_last) {
if (n>selection_first_ && n<=selection_last_) {
if (!pre && c < 256 && isspace(c)) c = ' ';
if (p != ' ' || c != ' ') {
if (s2 != s) { // c was an HTML entity
@ -3026,7 +3041,7 @@ void Fl_Help_View::end_selection(int clipboard)
}
p = c;
}
if (n>selection_last) break; // stop parsing html after end of selection
if (n>selection_last_) break; // stop parsing html after end of selection
}
*d = 0;
Fl::copy(txt, (int) strlen(txt), clipboard);
@ -3034,6 +3049,31 @@ void Fl_Help_View::end_selection(int clipboard)
free(txt);
}
/**
\brief Check if the user selected text in this view.
\return 1 if text is selected, 0 if no text is selected
*/
int Fl_Help_View::text_selected() {
if (current_view_==this)
return selected_;
else
return 0;
}
/**
\brief If text is selected in this view, copy it to a clipboard.
\param[in] clipboard for x11 only, 0=selection buffer, 1=clipboard, 2=both
\return 1 if text is selected, 0 if no text is selected
*/
int Fl_Help_View::copy(int clipboard) {
if (text_selected()) {
end_selection(clipboard);
return 1;
} else {
return 0;
}
}
/** Handles events in the widget. */
int // O - 1 if we handled it, 0 otherwise
Fl_Help_View::handle(int event) // I - Event to handle
@ -3063,6 +3103,20 @@ Fl_Help_View::handle(int event) // I - Event to handle
else fl_cursor(FL_CURSOR_DEFAULT);
return 1;
case FL_PUSH:
if (Fl::event_button() == FL_RIGHT_MOUSE) {
rmb_menu[0].label(copy_menu_text);
if (text_selected())
rmb_menu[0].activate();
else
rmb_menu[0].deactivate();
fl_cursor(FL_CURSOR_DEFAULT);
const Fl_Menu_Item *mi = rmb_menu->popup(Fl::event_x(), Fl::event_y());
if (mi) switch (mi->argument()) {
case 1:
copy();
break;
}
}
if (Fl_Group::handle(event)) return 1;
linkp = find_link(xx, yy);
if (linkp) {
@ -3084,7 +3138,7 @@ Fl_Help_View::handle(int event) // I - Event to handle
}
return 1;
}
if (current_view==this && selection_push_last) {
if (current_view_==this && selection_push_last_) {
if (extend_selection()) redraw();
fl_cursor(FL_CURSOR_INSERT);
return 1;
@ -3100,7 +3154,7 @@ Fl_Help_View::handle(int event) // I - Event to handle
linkp = 0;
return 1;
}
if (current_view==this && selection_push_last) {
if (current_view_==this && selection_push_last_) {
end_selection();
return 1;
}