# HG changeset patch # User Bram Moolenaar # Date 1560548706 -7200 # Node ID 76ae212a9075908d6b071a37acd092d4e9c435b0 # Parent 70b81915f7f7134a3ca1aa07826ddd0db4be4f06 patch 8.1.1534: modeless selection in popup window selects too much commit https://github.com/vim/vim/commit/bd75b5333d10e63f7a667d4b65e80d309435629e Author: Bram Moolenaar Date: Fri Jun 14 23:41:55 2019 +0200 patch 8.1.1534: modeless selection in popup window selects too much Problem: Modeless selection in popup window selects too much. Solution: Restrict the selection to insde of the popup window. diff --git a/src/testdir/dumps/Test_popupwin_select_01.dump b/src/testdir/dumps/Test_popupwin_select_01.dump new file mode 100644 --- /dev/null +++ b/src/testdir/dumps/Test_popupwin_select_01.dump @@ -0,0 +1,10 @@ +>1+0&#ffffff0| @73 +|2| @73 +|3| @7|╔+0#0000001#ffd7ff255|═@17|╗| +0#0000000#ffffff0@45 +|4| @7|║+0#0000001#ffd7ff255|t|h|e| |w+1#0000000#ffffff0|o|r|d| @9|║+0#0000001#ffd7ff255| +0#0000000#ffffff0@45 +|5| @7|║+0#0000001#ffd7ff255|s+1#0000000#ffffff0|o|m|e| |m|o|r|e| @8|║+0#0000001#ffd7ff255| +0#0000000#ffffff0@45 +|6| @7|║+0#0000001#ffd7ff255|s+1#0000000#ffffff0|e|v|e|r|a|l| |w|o|r|d|s| +0#0000001#ffd7ff255|h|e|r|e|║| +0#0000000#ffffff0@45 +|7| @7|╚+0#0000001#ffd7ff255|═@17|╝| +0#0000000#ffffff0@45 +|8| @73 +|9| @73 +|:|c|a|l@1| |S|e|l|e|c|t|1|(|)| @41|1|,|1| @10|T|o|p| diff --git a/src/testdir/dumps/Test_popupwin_select_02.dump b/src/testdir/dumps/Test_popupwin_select_02.dump new file mode 100644 --- /dev/null +++ b/src/testdir/dumps/Test_popupwin_select_02.dump @@ -0,0 +1,10 @@ +|1+0&#ffffff0>w|o|r|d| @69 +|s|o|m|e| |m|o|r|e| @65 +|s|e|v|e|r|a|l| |w|o|r|d|s| @61 +|2| @73 +|3| @73 +|4| @73 +|5| @73 +|6| @73 +|7| @73 +@57|1|,|2| @10|T|o|p| diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim --- a/src/testdir/test_popupwin.vim +++ b/src/testdir/test_popupwin.vim @@ -319,6 +319,39 @@ func Test_popup_drag() call delete('XtestPopupDrag') endfunc +func Test_popup_select() + if !CanRunVimInTerminal() + throw 'Skipped: cannot make screendumps' + endif + " create a popup with some text to be selected + let lines =<< trim END + call setline(1, range(1, 20)) + let winid = popup_create(['the word', 'some more', 'several words here'], { + \ 'drag': 1, + \ 'border': [], + \ 'line': 3, + \ 'col': 10, + \ }) + func Select1() + call feedkeys("\\\\\", "xt") + endfunc + map :call test_setmouse(4, 15) + map :call test_setmouse(6, 23) + END + call writefile(lines, 'XtestPopupSelect') + let buf = RunVimInTerminal('-S XtestPopupSelect', {'rows': 10}) + call term_sendkeys(buf, ":call Select1()\") + call VerifyScreenDump(buf, 'Test_popupwin_select_01', {}) + + call term_sendkeys(buf, ":call popup_close(winid)\") + call term_sendkeys(buf, "\"*p") + call VerifyScreenDump(buf, 'Test_popupwin_select_02', {}) + + " clean up + call StopVimInTerminal(buf) + call delete('XtestPopupSelect') +endfunc + func Test_popup_in_tab() " default popup is local to tab, not visible when in other tab let winid = popup_create("text", {}) diff --git a/src/ui.c b/src/ui.c --- a/src/ui.c +++ b/src/ui.c @@ -988,13 +988,13 @@ clip_isautosel_plus(void) * Stuff for general mouse selection, without using Visual mode. */ -static void clip_invert_area(int, int, int, int, int how); -static void clip_invert_rectangle(int row, int col, int height, int width, int invert); +static void clip_invert_area(Clipboard_T *, int, int, int, int, int how); +static void clip_invert_rectangle(Clipboard_T *, int row, int col, int height, int width, int invert); static void clip_get_word_boundaries(Clipboard_T *, int, int); -static int clip_get_line_end(int); +static int clip_get_line_end(Clipboard_T *, int); static void clip_update_modeless_selection(Clipboard_T *, int, int, int, int); -/* flags for clip_invert_area() */ +// "how" flags for clip_invert_area() #define CLIP_CLEAR 1 #define CLIP_SET 2 #define CLIP_TOGGLE 3 @@ -1071,6 +1071,33 @@ clip_start_selection(int col, int row, i cb->end = cb->start; cb->origin_row = (short_u)cb->start.lnum; cb->state = SELECT_IN_PROGRESS; +#ifdef FEAT_TEXT_PROP + { + win_T *wp; + int row_cp = row; + int col_cp = col; + + wp = mouse_find_win(&row_cp, &col_cp, FIND_POPUP); + if (wp != NULL && bt_popup(wp->w_buffer)) + { + // Click in a popup window restricts selection to that window, + // excluding the border. + cb->min_col = wp->w_wincol + wp->w_popup_border[3]; + cb->max_col = wp->w_wincol + popup_width(wp) - 1 + - wp->w_popup_border[1]; + cb->min_row = wp->w_winrow + wp->w_popup_border[0]; + cb->max_row = wp->w_winrow + popup_height(wp) - 1 + - wp->w_popup_border[2]; + } + else + { + cb->min_col = 0; + cb->max_col = screen_Columns; + cb->min_row = 0; + cb->max_row = screen_Rows; + } + } +#endif if (repeated_click) { @@ -1090,7 +1117,7 @@ clip_start_selection(int col, int row, i { case SELECT_MODE_CHAR: cb->origin_start_col = cb->start.col; - cb->word_end_col = clip_get_line_end((int)cb->start.lnum); + cb->word_end_col = clip_get_line_end(cb, (int)cb->start.lnum); break; case SELECT_MODE_WORD: @@ -1098,14 +1125,14 @@ clip_start_selection(int col, int row, i cb->origin_start_col = cb->word_start_col; cb->origin_end_col = cb->word_end_col; - clip_invert_area((int)cb->start.lnum, cb->word_start_col, + clip_invert_area(cb, (int)cb->start.lnum, cb->word_start_col, (int)cb->end.lnum, cb->word_end_col, CLIP_SET); cb->start.col = cb->word_start_col; cb->end.col = cb->word_end_col; break; case SELECT_MODE_LINE: - clip_invert_area((int)cb->start.lnum, 0, (int)cb->start.lnum, + clip_invert_area(cb, (int)cb->start.lnum, 0, (int)cb->start.lnum, (int)Columns, CLIP_SET); cb->start.col = 0; cb->end.col = Columns; @@ -1223,7 +1250,7 @@ clip_process_selection( case SELECT_MODE_CHAR: /* If we're on a different line, find where the line ends */ if (row != cb->prev.lnum) - cb->word_end_col = clip_get_line_end(row); + cb->word_end_col = clip_get_line_end(cb, row); /* See if we are before or after the origin of the selection */ if (clip_compare_pos(row, col, cb->origin_row, @@ -1316,7 +1343,7 @@ clip_may_redraw_selection(int row, int c if (row == clip_star.end.lnum && end > (int)clip_star.end.col) end = clip_star.end.col; if (end > start) - clip_invert_area(row, start, row, end, 0); + clip_invert_area(&clip_star, row, start, row, end, 0); } } # endif @@ -1331,8 +1358,8 @@ clip_clear_selection(Clipboard_T *cbd) if (cbd->state == SELECT_CLEARED) return; - clip_invert_area((int)cbd->start.lnum, cbd->start.col, (int)cbd->end.lnum, - cbd->end.col, CLIP_CLEAR); + clip_invert_area(cbd, (int)cbd->start.lnum, cbd->start.col, + (int)cbd->end.lnum, cbd->end.col, CLIP_CLEAR); cbd->state = SELECT_CLEARED; } @@ -1388,13 +1415,21 @@ clip_scroll_selection( */ static void clip_invert_area( - int row1, - int col1, - int row2, - int col2, - int how) + Clipboard_T *cbd, + int row1, + int col1, + int row2, + int col2, + int how) { int invert = FALSE; + int max_col; + +#ifdef FEAT_TEXT_PROP + max_col = cbd->max_col; +#else + max_col = Columns - 1; +#endif if (how == CLIP_SET) invert = TRUE; @@ -1417,28 +1452,29 @@ clip_invert_area( /* If all on the same line, do it the easy way */ if (row1 == row2) { - clip_invert_rectangle(row1, col1, 1, col2 - col1, invert); + clip_invert_rectangle(cbd, row1, col1, 1, col2 - col1, invert); } else { /* Handle a piece of the first line */ if (col1 > 0) { - clip_invert_rectangle(row1, col1, 1, (int)Columns - col1, invert); + clip_invert_rectangle(cbd, row1, col1, 1, + (int)Columns - col1, invert); row1++; } /* Handle a piece of the last line */ - if (col2 < Columns - 1) + if (col2 < max_col) { - clip_invert_rectangle(row2, 0, 1, col2, invert); + clip_invert_rectangle(cbd, row2, 0, 1, col2, invert); row2--; } /* Handle the rectangle thats left */ if (row2 >= row1) - clip_invert_rectangle(row1, 0, row2 - row1 + 1, (int)Columns, - invert); + clip_invert_rectangle(cbd, row1, 0, row2 - row1 + 1, + (int)Columns, invert); } } @@ -1448,15 +1484,36 @@ clip_invert_area( */ static void clip_invert_rectangle( - int row, - int col, - int height, - int width, - int invert) + Clipboard_T *cbd, + int row_arg, + int col_arg, + int height_arg, + int width_arg, + int invert) { + int row = row_arg; + int col = col_arg; + int height = height_arg; + int width = width_arg; + #ifdef FEAT_TEXT_PROP // this goes on top of all popup windows screen_zindex = 32000; + + if (col < cbd->min_col) + { + width -= cbd->min_col - col; + col = cbd->min_col; + } + if (width > cbd->max_col - col + 1) + width = cbd->max_col - col + 1; + if (row < cbd->min_row) + { + height -= cbd->min_row - row; + row = cbd->min_row; + } + if (height > cbd->max_row - row + 1) + height = cbd->max_row - row + 1; #endif #ifdef FEAT_GUI if (gui.in_use) @@ -1507,6 +1564,16 @@ clip_copy_modeless_selection(int both UN { row = col1; col1 = col2; col2 = row; } +#ifdef FEAT_TEXT_PROP + if (col1 < clip_star.min_col) + col1 = clip_star.min_col; + if (col2 > clip_star.max_col + 1) + col2 = clip_star.max_col + 1; + if (row1 < clip_star.min_row) + row1 = clip_star.min_row; + if (row2 > clip_star.max_row) + row2 = clip_star.max_row; +#endif /* correct starting point for being on right halve of double-wide char */ p = ScreenLines + LineOffset[row1]; if (enc_dbcs != 0) @@ -1530,17 +1597,31 @@ clip_copy_modeless_selection(int both UN if (row == row1) start_col = col1; else +#ifdef FEAT_TEXT_PROP + start_col = clip_star.min_col; +#else start_col = 0; +#endif if (row == row2) end_col = col2; else +#ifdef FEAT_TEXT_PROP + end_col = clip_star.max_col + 1; +#else end_col = Columns; - - line_end_col = clip_get_line_end(row); +#endif + + line_end_col = clip_get_line_end(&clip_star, row); /* See if we need to nuke some trailing whitespace */ - if (end_col >= Columns && (row < row2 || end_col > line_end_col)) + if (end_col >= +#ifdef FEAT_TEXT_PROP + clip_star.max_col + 1 +#else + Columns +#endif + && (row < row2 || end_col > line_end_col)) { /* Get rid of trailing whitespace */ end_col = line_end_col; @@ -1556,6 +1637,7 @@ clip_copy_modeless_selection(int both UN if (row > row1 && !LineWraps[row - 1]) *bufp++ = NL; + // Safetey check for in case resizing went wrong if (row < screen_Rows && end_col <= screen_Columns) { if (enc_dbcs != 0) @@ -1690,16 +1772,22 @@ clip_get_word_boundaries(Clipboard_T *cb /* * Find the column position for the last non-whitespace character on the given - * line. + * line at or before start_col. */ static int -clip_get_line_end(int row) +clip_get_line_end(Clipboard_T *cbd UNUSED, int row) { int i; if (row >= screen_Rows || ScreenLines == NULL) return 0; - for (i = screen_Columns; i > 0; i--) + for (i = +#ifdef FEAT_TEXT_PROP + cbd->max_col + 1; +#else + screen_Columns; +#endif + i > 0; i--) if (ScreenLines[LineOffset[row] + i - 1] != ' ') break; return i; @@ -1720,7 +1808,7 @@ clip_update_modeless_selection( /* See if we changed at the beginning of the selection */ if (row1 != cb->start.lnum || col1 != (int)cb->start.col) { - clip_invert_area(row1, col1, (int)cb->start.lnum, cb->start.col, + clip_invert_area(cb, row1, col1, (int)cb->start.lnum, cb->start.col, CLIP_TOGGLE); cb->start.lnum = row1; cb->start.col = col1; @@ -1729,7 +1817,7 @@ clip_update_modeless_selection( /* See if we changed at the end of the selection */ if (row2 != cb->end.lnum || col2 != (int)cb->end.col) { - clip_invert_area((int)cb->end.lnum, cb->end.col, row2, col2, + clip_invert_area(cb, (int)cb->end.lnum, cb->end.col, row2, col2, CLIP_TOGGLE); cb->end.lnum = row2; cb->end.col = col2; diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -778,6 +778,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1534, +/**/ 1533, /**/ 1532, diff --git a/src/vim.h b/src/vim.h --- a/src/vim.h +++ b/src/vim.h @@ -2008,34 +2008,41 @@ typedef int sock_T; /* Info about selected text */ typedef struct { - int available; /* Is clipboard available? */ - int owned; /* Flag: do we own the selection? */ - pos_T start; /* Start of selected area */ - pos_T end; /* End of selected area */ - int vmode; /* Visual mode character */ + int available; // Is clipboard available? + int owned; // Flag: do we own the selection? + pos_T start; // Start of selected area + pos_T end; // End of selected area + int vmode; // Visual mode character - /* Fields for selection that doesn't use Visual mode */ + // Fields for selection that doesn't use Visual mode short_u origin_row; short_u origin_start_col; short_u origin_end_col; short_u word_start_col; short_u word_end_col; +#ifdef FEAT_TEXT_PROP + // limits for selection inside a popup window + short_u min_col; + short_u max_col; + short_u min_row; + short_u max_row; +#endif - pos_T prev; /* Previous position */ - short_u state; /* Current selection state */ - short_u mode; /* Select by char, word, or line. */ + pos_T prev; // Previous position + short_u state; // Current selection state + short_u mode; // Select by char, word, or line. # if defined(FEAT_GUI_X11) || defined(FEAT_XCLIPBOARD) - Atom sel_atom; /* PRIMARY/CLIPBOARD selection ID */ + Atom sel_atom; // PRIMARY/CLIPBOARD selection ID # endif # ifdef FEAT_GUI_GTK - GdkAtom gtk_sel_atom; /* PRIMARY/CLIPBOARD selection ID */ + GdkAtom gtk_sel_atom; // PRIMARY/CLIPBOARD selection ID # endif # if defined(MSWIN) || defined(FEAT_CYGWIN_WIN32_CLIPBOARD) - int_u format; /* Vim's own special clipboard format */ - int_u format_raw; /* Vim's raw text clipboard format */ + int_u format; // Vim's own special clipboard format + int_u format_raw; // Vim's raw text clipboard format # endif } Clipboard_T; #else