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 Fl_Scrollbar scrollbar_, ///< Vertical scrollbar for document
hscrollbar_; ///< Horizontal scrollbar hscrollbar_; ///< Horizontal scrollbar
static int selection_first; static int selection_first_;
static int selection_last; static int selection_last_;
static int selection_push_first; static int selection_push_first_;
static int selection_push_last; static int selection_push_last_;
static int selection_drag_first; static int selection_drag_first_;
static int selection_drag_last; static int selection_drag_last_;
static int selected; static int selected_;
static int draw_mode; static int draw_mode_;
static int mouse_x; static int mouse_x_;
static int mouse_y; static int mouse_y_;
static int current_pos; static int current_pos_;
static Fl_Help_View *current_view; static Fl_Help_View *current_view_;
static Fl_Color hv_selection_color; static Fl_Color hv_selection_color_;
static Fl_Color hv_selection_text_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); } 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: 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(int xx, int yy, int ww, int hh, const char *l = 0);
~Fl_Help_View(); ~Fl_Help_View();
/** Returns the current directory for the text in the buffer. */ /** Returns the current directory for the text in the buffer. */
@ -383,6 +385,12 @@ public:
void scrollbar_size(int newSize) { void scrollbar_size(int newSize) {
scrollbar_size_ = 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 #endif // !Fl_Help_View_H

View File

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