/* * Copyright 2008-2013 Various Authors * Copyright 2004 Timo Hirvonen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "search_mode.h" #include "cmdline.h" #include "history.h" #include "ui_curses.h" #include "search.h" #include "xmalloc.h" #include "xstrjoin.h" #include "misc.h" #include "lib.h" #include "options.h" #include "command_mode.h" #include "keys.h" #include /* this is set in ui_curses.c */ enum search_direction search_direction = SEARCH_FORWARD; /* current search string, this is set _only_ when user presses enter * this string is used when 'n' or 'N' is pressed * incremental search does not use this, it uses cmdline.line directly */ char *search_str = NULL; int search_restricted = 0; static struct history search_history; static char *search_history_filename; static char *history_search_text = NULL; static void update_search_line(const char *text, int restricted) { int len = strlen(text); char ch = search_direction == SEARCH_FORWARD ? '/' : '?'; char *buf, *ptr; buf = xnew(char, len + 2); ptr = buf; if (restricted) *ptr++ = ch; memcpy(ptr, text, len + 1); cmdline_set_text(buf); free(buf); } static int search_line_empty(void) { char ch; if (cmdline.clen == 0) return 1; if (cmdline.clen > 1) return 0; ch = search_direction == SEARCH_FORWARD ? '/' : '?'; return cmdline.line[0] == ch; } static void parse_line(const char **text, int *restricted) { char ch = search_direction == SEARCH_FORWARD ? '/' : '?'; int r = 0; if (cmdline.line[0] == ch) { /* //WORDS or ??WORDS */ r = 1; } *text = cmdline.line + r; *restricted = r; } static void reset_history_search(void) { history_reset_search(&search_history); free(history_search_text); history_search_text = NULL; } static void backspace(void) { if (cmdline.clen > 0) { cmdline_backspace(); } else { input_mode = NORMAL_MODE; } } static void delete(void) { /* save old value */ int restricted = search_restricted; const char *text; cmdline_delete_ch(); parse_line(&text, &search_restricted); if (text[0]) search(searchable, text, search_direction, 0); /* restore old value */ search_restricted = restricted; } void search_text(const char *text, int restricted, int beginning) { if (text[0] == 0) { /* cmdline is "/", "?", "//" or "??" */ if (search_str) { /* use old search string */ search_restricted = restricted; if (!search_next(searchable, search_str, search_direction)) search_not_found(); } } else { /* set new search string and add it to the history */ free(search_str); search_str = xstrdup(text); history_add_line(&search_history, text); /* search not yet done if up or down arrow was pressed */ search_restricted = restricted; if (!search(searchable, search_str, search_direction, beginning)) search_not_found(); } } void search_mode_ch(uchar ch) { const char *text; int restricted; switch (ch) { case 0x01: // ^A cmdline_move_home(); break; case 0x02: // ^B cmdline_move_left(); break; case 0x04: // ^D delete(); break; case 0x05: // ^E cmdline_move_end(); break; case 0x06: // ^F cmdline_move_right(); break; case 0x03: // ^C case 0x07: // ^G case 0x1B: // ESC parse_line(&text, &restricted); if (text[0]) { history_add_line(&search_history, text); cmdline_clear(); } input_mode = NORMAL_MODE; break; case 0x0A: parse_line(&text, &restricted); search_text(text, restricted, 0); cmdline_clear(); input_mode = NORMAL_MODE; break; case 0x0B: cmdline_clear_end(); break; case 0x10: // ^P search_mode_key(KEY_UP); return; case 0xE: // ^N search_mode_key(KEY_DOWN); return; case 0x15: cmdline_backspace_to_bol(); break; case 0x17: // ^W cmdline_backward_delete_word(cmdline_word_delimiters); break; case 0x08: // ^H case 127: backspace(); break; default: if (ch < 0x20) { return; } else { /* start from beginning if this is first char */ int beginning = search_resets_position && search_line_empty(); /* save old value * * don't set search_{str,restricted} here because * search can be cancelled by pressing ESC */ restricted = search_restricted; cmdline_insert_ch(ch); parse_line(&text, &search_restricted); search(searchable, text, search_direction, beginning); /* restore old value */ search_restricted = restricted; } break; } reset_history_search(); } void search_mode_escape(int c) { switch (c) { case 98: cmdline_backward_word(cmdline_filename_delimiters); break; case 100: cmdline_delete_word(cmdline_filename_delimiters); break; case 102: cmdline_forward_word(cmdline_filename_delimiters); break; case 127: case KEY_BACKSPACE: cmdline_backward_delete_word(cmdline_filename_delimiters); break; } reset_history_search(); } void search_mode_key(int key) { const char *text; int restricted; switch (key) { case KEY_DC: delete(); break; case KEY_BACKSPACE: backspace(); break; case KEY_LEFT: cmdline_move_left(); return; case KEY_RIGHT: cmdline_move_right(); return; case KEY_HOME: cmdline_move_home(); return; case KEY_END: cmdline_move_end(); return; case KEY_UP: parse_line(&text, &restricted); if (history_search_text == NULL) history_search_text = xstrdup(text); text = history_search_forward(&search_history, history_search_text); if (text) update_search_line(text, restricted); return; case KEY_DOWN: if (history_search_text) { parse_line(&text, &restricted); text = history_search_backward(&search_history, history_search_text); if (text) { update_search_line(text, restricted); } else { update_search_line(history_search_text, restricted); } } return; default: return; } reset_history_search(); } void search_mode_mouse(MEVENT *event) { if ((event->bstate & BUTTON1_PRESSED) || (event->bstate & BUTTON3_PRESSED)) { const char *text; int restricted; if (event->y <= window_get_nr_rows(current_win()) + 2) { parse_line(&text, &restricted); if (text[0]) { history_add_line(&search_history, text); cmdline_clear(); } input_mode = NORMAL_MODE; normal_mode_mouse(event); return; } if (event->x == 0) return; int i = event->x > cmdline.clen ? cmdline.clen : event->x - 1; while (i < cmdline.cpos) cmdline_move_left(); while (i > cmdline.cpos) cmdline_move_right(); } else if (event->bstate & BUTTON4_PRESSED) { search_mode_key(KEY_UP); } else if (event->bstate & BUTTON5_PRESSED) { search_mode_key(KEY_DOWN); } } void search_mode_init(void) { search_history_filename = xstrjoin(cmus_config_dir, "/search-history"); history_load(&search_history, search_history_filename, 100); } void search_mode_exit(void) { history_save(&search_history); history_free(&search_history); free(search_history_filename); }