Update upstream source from tag 'upstream/0.35'
[ncmpc-debian.git] / src / screen_find.cxx
1 /* ncmpc (Ncurses MPD Client)
2  * (c) 2004-2019 The Music Player Daemon Project
3  * Project homepage: http://musicpd.org
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19
20 #include "screen_find.hxx"
21 #include "screen_utils.hxx"
22 #include "screen_status.hxx"
23 #include "screen.hxx"
24 #include "ListWindow.hxx"
25 #include "AsyncUserInput.hxx"
26 #include "i18n.h"
27 #include "Command.hxx"
28 #include "Options.hxx"
29 #include "util/LocaleString.hxx"
30
31 #include <ctype.h>
32
33 #define FIND_PROMPT  _("Find")
34 #define RFIND_PROMPT _("Find backward")
35 #define JUMP_PROMPT _("Jump")
36
37 /* query user for a string and find it in a list window */
38 bool
39 screen_find(ScreenManager &screen, ListWindow &lw, Command findcmd,
40             const ListText &text) noexcept
41 {
42         bool found;
43         const char *prompt = FIND_PROMPT;
44
45         const bool reversed =
46                 findcmd == Command::LIST_RFIND ||
47                 findcmd == Command::LIST_RFIND_NEXT;
48         if (reversed)
49                 prompt = RFIND_PROMPT;
50
51         switch (findcmd) {
52         case Command::LIST_FIND:
53         case Command::LIST_RFIND:
54                 screen.findbuf.clear();
55                 /* fall through */
56
57         case Command::LIST_FIND_NEXT:
58         case Command::LIST_RFIND_NEXT:
59                 if (screen.findbuf.empty()) {
60                         char *value = options.find_show_last_pattern
61                                 ? (char *) -1 : nullptr;
62                         screen.findbuf=screen_readln(prompt,
63                                                      value,
64                                                      &screen.find_history,
65                                                      nullptr);
66                 }
67
68                 if (screen.findbuf.empty())
69                         return true;
70
71                 found = reversed
72                         ? lw.ReverseFind(text,
73                                          screen.findbuf.c_str(),
74                                          options.find_wrap,
75                                          options.bell_on_wrap)
76                         : lw.Find(text,
77                                   screen.findbuf.c_str(),
78                                   options.find_wrap,
79                                   options.bell_on_wrap);
80                 if (!found) {
81                         screen_status_printf(_("Unable to find \'%s\'"),
82                                              screen.findbuf.c_str());
83                         screen_bell();
84                 }
85                 return true;
86         default:
87                 break;
88         }
89         return false;
90 }
91
92 /* query user for a string and jump to the entry
93  * which begins with this string while the users types */
94 void
95 screen_jump(ScreenManager &screen, ListWindow &lw,
96             const ListText &text,
97             const ListRenderer &renderer) noexcept
98 {
99         constexpr size_t WRLN_MAX_LINE_SIZE = 1024;
100         int key = 65;
101
102         char buffer[WRLN_MAX_LINE_SIZE];
103
104         /* In screen.findbuf is the whole string which is displayed in the status_window
105          * and search_str is the string the user entered (without the prompt) */
106         char *search_str = buffer + snprintf(buffer, WRLN_MAX_LINE_SIZE, "%s: ", JUMP_PROMPT);
107         char *iter = search_str;
108
109         while(1) {
110                 key = screen_getch(buffer);
111                 /* if backspace or delete was pressed, process instead of ending loop */
112                 if (key == KEY_BACKSPACE || key == KEY_DC) {
113                         const char *prev = PrevCharMB(buffer, iter);
114                         if (search_str <= prev)
115                                 iter = const_cast<char *>(prev);
116                         *iter = '\0';
117                         continue;
118                 }
119                 /* if a control key was pressed, end loop */
120                 else if (iscntrl(key) || key == KEY_NPAGE || key == KEY_PPAGE) {
121                         break;
122                 }
123                 else if (iter < buffer + WRLN_MAX_LINE_SIZE - 3) {
124                         *iter++ = key;
125                         *iter = '\0';
126                 }
127                 lw.Jump(text, search_str);
128
129                 /* repaint the list_window */
130                 lw.Paint(renderer);
131                 lw.Refresh();
132         }
133
134         screen.findbuf = search_str;
135
136         /* ncmpc should get the command */
137         keyboard_unread(screen.get_io_service(), key);
138 }