ListWindow: replace callback function with abstract class
[ncmpc-debian.git] / src / screen_find.cxx
1 /* ncmpc (Ncurses MPD Client)
2  * (c) 2004-2018 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 "keyboard.hxx"
25 #include "i18n.h"
26 #include "options.hxx"
27
28 #define FIND_PROMPT  _("Find")
29 #define RFIND_PROMPT _("Find backward")
30 #define JUMP_PROMPT _("Jump")
31
32 /* query user for a string and find it in a list window */
33 bool
34 screen_find(ScreenManager &screen, ListWindow *lw, command_t findcmd,
35             list_window_callback_fn_t callback_fn,
36             void *callback_data)
37 {
38         bool found;
39         const char *prompt = FIND_PROMPT;
40
41         const bool reversed =
42                 findcmd == CMD_LIST_RFIND || findcmd == CMD_LIST_RFIND_NEXT;
43         if (reversed)
44                 prompt = RFIND_PROMPT;
45
46         switch (findcmd) {
47         case CMD_LIST_FIND:
48         case CMD_LIST_RFIND:
49                 screen.findbuf.clear();
50                 /* fall through */
51
52         case CMD_LIST_FIND_NEXT:
53         case CMD_LIST_RFIND_NEXT:
54                 if (screen.findbuf.empty()) {
55                         char *value = options.find_show_last_pattern
56                                 ? (char *) -1 : nullptr;
57                         screen.findbuf=screen_readln(prompt,
58                                                      value,
59                                                      &screen.find_history,
60                                                      nullptr);
61                 }
62
63                 if (screen.findbuf.empty())
64                         return true;
65
66                 found = reversed
67                         ? lw->ReverseFind(callback_fn, callback_data,
68                                           screen.findbuf.c_str(),
69                                           options.find_wrap,
70                                           options.bell_on_wrap)
71                         : lw->Find(callback_fn, callback_data,
72                                    screen.findbuf.c_str(),
73                                    options.find_wrap,
74                                    options.bell_on_wrap);
75                 if (!found) {
76                         screen_status_printf(_("Unable to find \'%s\'"),
77                                              screen.findbuf.c_str());
78                         screen_bell();
79                 }
80                 return true;
81         default:
82                 break;
83         }
84         return false;
85 }
86
87 /* query user for a string and jump to the entry
88  * which begins with this string while the users types */
89 void
90 screen_jump(ScreenManager &screen, ListWindow *lw,
91             list_window_callback_fn_t callback_fn, void *callback_data,
92             const ListRenderer *renderer)
93 {
94         constexpr size_t WRLN_MAX_LINE_SIZE = 1024;
95         int key = 65;
96
97         char buffer[WRLN_MAX_LINE_SIZE];
98         std::fill_n(buffer, WRLN_MAX_LINE_SIZE, 0);
99
100         /* In screen.findbuf is the whole string which is displayed in the status_window
101          * and search_str is the string the user entered (without the prompt) */
102         char *search_str = buffer + g_snprintf(buffer, WRLN_MAX_LINE_SIZE, "%s: ", JUMP_PROMPT);
103         char *iter = search_str;
104
105         while(1) {
106                 key = screen_getch(buffer);
107                 /* if backspace or delete was pressed, process instead of ending loop */
108                 if (key == KEY_BACKSPACE || key == KEY_DC) {
109                         int i;
110                         if (search_str <= g_utf8_find_prev_char(buffer, iter))
111                                 iter = g_utf8_find_prev_char(buffer, iter);
112                         for (i = 0; *(iter + i) != '\0'; i++)
113                                 *(iter + i) = '\0';
114                         continue;
115                 }
116                 /* if a control key was pressed, end loop */
117                 else if (g_ascii_iscntrl(key) || key == KEY_NPAGE || key == KEY_PPAGE) {
118                         break;
119                 }
120                 else {
121                         *iter = key;
122                         if (iter < buffer + WRLN_MAX_LINE_SIZE - 3)
123                                 ++iter;
124                 }
125                 lw->Jump(callback_fn, callback_data, search_str);
126
127                 /* repaint the list_window */
128                 if (renderer != nullptr)
129                         lw->Paint(*renderer);
130                 else
131                         lw->Paint(callback_fn, callback_data);
132                 wrefresh(lw->w);
133         }
134
135         screen.findbuf = search_str;
136
137         /* ncmpc should get the command */
138         keyboard_unread(key);
139 }