To: vim_dev@googlegroups.com Subject: Patch 8.1.1534 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.1.1534 Problem: Modeless selection in popup window selects too much. Solution: Restrict the selection to insde of the popup window. Files: src/vim.h, src/ui.c, src/testdir/test_popupwin.vim, src/testdir/dumps/Test_popupwin_select_01.dump, src/testdir/dumps/Test_popupwin_select_02.dump *** ../vim-8.1.1533/src/vim.h 2019-06-14 23:14:41.936219907 +0200 --- src/vim.h 2019-06-14 23:36:54.345446982 +0200 *************** *** 2008,2041 **** /* 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 */ ! /* 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; ! 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 */ # endif # ifdef FEAT_GUI_GTK ! 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 */ # endif } Clipboard_T; #else --- 2008,2048 ---- /* 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 ! // 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. # if defined(FEAT_GUI_X11) || defined(FEAT_XCLIPBOARD) ! Atom sel_atom; // PRIMARY/CLIPBOARD selection ID # endif # ifdef FEAT_GUI_GTK ! 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 # endif } Clipboard_T; #else *** ../vim-8.1.1533/src/ui.c 2019-06-14 21:36:51.018437479 +0200 --- src/ui.c 2019-06-14 23:09:19.161413017 +0200 *************** *** 988,1000 **** * 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_get_word_boundaries(Clipboard_T *, int, int); ! static int clip_get_line_end(int); static void clip_update_modeless_selection(Clipboard_T *, int, int, int, int); ! /* flags for clip_invert_area() */ #define CLIP_CLEAR 1 #define CLIP_SET 2 #define CLIP_TOGGLE 3 --- 988,1000 ---- * Stuff for general mouse selection, without using Visual mode. */ ! 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(Clipboard_T *, int); static void clip_update_modeless_selection(Clipboard_T *, int, int, int, int); ! // "how" flags for clip_invert_area() #define CLIP_CLEAR 1 #define CLIP_SET 2 #define CLIP_TOGGLE 3 *************** *** 1071,1076 **** --- 1071,1103 ---- 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,1096 **** { case SELECT_MODE_CHAR: cb->origin_start_col = cb->start.col; ! cb->word_end_col = clip_get_line_end((int)cb->start.lnum); break; case SELECT_MODE_WORD: --- 1117,1123 ---- { case SELECT_MODE_CHAR: cb->origin_start_col = cb->start.col; ! cb->word_end_col = clip_get_line_end(cb, (int)cb->start.lnum); break; case SELECT_MODE_WORD: *************** *** 1098,1111 **** 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, (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, (int)Columns, CLIP_SET); cb->start.col = 0; cb->end.col = Columns; --- 1125,1138 ---- cb->origin_start_col = cb->word_start_col; cb->origin_end_col = cb->word_end_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(cb, (int)cb->start.lnum, 0, (int)cb->start.lnum, (int)Columns, CLIP_SET); cb->start.col = 0; cb->end.col = Columns; *************** *** 1223,1229 **** 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); /* See if we are before or after the origin of the selection */ if (clip_compare_pos(row, col, cb->origin_row, --- 1250,1256 ---- 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(cb, row); /* See if we are before or after the origin of the selection */ if (clip_compare_pos(row, col, cb->origin_row, *************** *** 1316,1322 **** 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); } } # endif --- 1343,1349 ---- if (row == clip_star.end.lnum && end > (int)clip_star.end.col) end = clip_star.end.col; if (end > start) ! clip_invert_area(&clip_star, row, start, row, end, 0); } } # endif *************** *** 1331,1338 **** 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); cbd->state = SELECT_CLEARED; } --- 1358,1365 ---- if (cbd->state == SELECT_CLEARED) return; ! 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,1400 **** */ static void clip_invert_area( ! int row1, ! int col1, ! int row2, ! int col2, ! int how) { int invert = FALSE; if (how == CLIP_SET) invert = TRUE; --- 1415,1435 ---- */ static void clip_invert_area( ! 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,1444 **** /* If all on the same line, do it the easy way */ if (row1 == row2) { ! clip_invert_rectangle(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); row1++; } /* Handle a piece of the last line */ ! if (col2 < Columns - 1) { ! clip_invert_rectangle(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); } } --- 1452,1480 ---- /* If all on the same line, do it the easy way */ if (row1 == row2) { ! clip_invert_rectangle(cbd, row1, col1, 1, col2 - col1, invert); } else { /* Handle a piece of the first line */ if (col1 > 0) { ! clip_invert_rectangle(cbd, row1, col1, 1, ! (int)Columns - col1, invert); row1++; } /* Handle a piece of the last line */ ! if (col2 < max_col) { ! clip_invert_rectangle(cbd, row2, 0, 1, col2, invert); row2--; } /* Handle the rectangle thats left */ if (row2 >= row1) ! clip_invert_rectangle(cbd, row1, 0, row2 - row1 + 1, ! (int)Columns, invert); } } *************** *** 1448,1462 **** */ static void clip_invert_rectangle( ! int row, ! int col, ! int height, ! int width, ! int invert) ! { #ifdef FEAT_TEXT_PROP // this goes on top of all popup windows screen_zindex = 32000; #endif #ifdef FEAT_GUI if (gui.in_use) --- 1484,1519 ---- */ static void clip_invert_rectangle( ! 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,1512 **** --- 1564,1579 ---- { 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,1546 **** if (row == row1) start_col = col1; else start_col = 0; if (row == row2) end_col = col2; else end_col = Columns; ! line_end_col = clip_get_line_end(row); /* See if we need to nuke some trailing whitespace */ ! if (end_col >= Columns && (row < row2 || end_col > line_end_col)) { /* Get rid of trailing whitespace */ end_col = line_end_col; --- 1597,1627 ---- 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; + #endif ! line_end_col = clip_get_line_end(&clip_star, row); /* See if we need to nuke some trailing whitespace */ ! 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,1561 **** --- 1637,1643 ---- 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,1705 **** /* * Find the column position for the last non-whitespace character on the given ! * line. */ static int ! clip_get_line_end(int row) { int i; if (row >= screen_Rows || ScreenLines == NULL) return 0; ! for (i = screen_Columns; i > 0; i--) if (ScreenLines[LineOffset[row] + i - 1] != ' ') break; return i; --- 1772,1793 ---- /* * Find the column position for the last non-whitespace character on the given ! * line at or before start_col. */ static int ! clip_get_line_end(Clipboard_T *cbd UNUSED, int row) { int i; if (row >= screen_Rows || ScreenLines == NULL) return 0; ! 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,1726 **** /* 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_TOGGLE); cb->start.lnum = row1; cb->start.col = col1; --- 1808,1814 ---- /* See if we changed at the beginning of the selection */ if (row1 != cb->start.lnum || col1 != (int)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,1735 **** /* 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_TOGGLE); cb->end.lnum = row2; cb->end.col = col2; --- 1817,1823 ---- /* See if we changed at the end of the selection */ if (row2 != cb->end.lnum || col2 != (int)cb->end.col) { ! clip_invert_area(cb, (int)cb->end.lnum, cb->end.col, row2, col2, CLIP_TOGGLE); cb->end.lnum = row2; cb->end.col = col2; *** ../vim-8.1.1533/src/testdir/test_popupwin.vim 2019-06-14 19:23:35.502289836 +0200 --- src/testdir/test_popupwin.vim 2019-06-14 23:11:23.872960635 +0200 *************** *** 319,324 **** --- 319,357 ---- 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", {}) *** ../vim-8.1.1533/src/testdir/dumps/Test_popupwin_select_01.dump 2019-06-14 23:36:26.653637674 +0200 --- src/testdir/dumps/Test_popupwin_select_01.dump 2019-06-14 23:10:20.829190935 +0200 *************** *** 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| *** ../vim-8.1.1533/src/testdir/dumps/Test_popupwin_select_02.dump 2019-06-14 23:36:26.657637645 +0200 --- src/testdir/dumps/Test_popupwin_select_02.dump 2019-06-14 22:40:51.239667179 +0200 *************** *** 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| *** ../vim-8.1.1533/src/version.c 2019-06-14 23:27:24.553231791 +0200 --- src/version.c 2019-06-14 23:35:07.838200123 +0200 *************** *** 779,780 **** --- 779,782 ---- { /* Add new patch number below this line */ + /**/ + 1534, /**/ -- hundred-and-one symptoms of being an internet addict: 179. You wonder why your household garbage can doesn't have an "empty recycle bin" button. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///