/* * freescope - Free source browser * Copyright (C) 2001 Olivier Deme * * 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ // FILE: cursui.cpp /************/ /* INCLUDES */ /************/ #include #include #include #include #include #include #include #include "cursui.h" #include "wndhelp.h" #include "defs.h" #include "fconfig.h" /***************/ /* DEFINITIONS */ /***************/ #ifdef erase // erase is defined in curses library... #undef erase #endif // erase /************************/ /* FUNCTION DEFINITIONS */ /************************/ /* * FUNCTION: CursUI::CursUI * * DESCRIPTION: Constructor. * The constructor initiates the curses interface and should * return only when the user decides to quit the application. * * IN: * IN-OUT: * RETURN CODE: */ CursUI::CursUI () : m_form_query(NULL), m_form_result(NULL), m_panel_user(NULL), m_panel_results(NULL), m_panel_status(NULL), m_results_per_page(30), m_mode(MODE_QUERY), m_next_completion(false), m_selecting_changes(false), m_all_selected(false) { } /* * FUNCTION: CursUI::do_init * * DESCRIPTION: This function starts the curses UI * * IN: * IN-OUT: * RETURN CODE: */ void CursUI::do_init () { if (!initscr()) throw DbUser_Exception("Couldn't initialize ncurses library"); try { SIGLINK->Register(SIGWINCH, CursUI::on_resize, this); SIGLINK->Register(SIGINT, CursUI::on_exit, this); } catch (SigLink_Exception& se) { throw(DbUser_Exception(se.what())); } // Initialization of query labels m_query_label[FIELD_LABEL_C_SYMBOL] = "Symbol: "; m_query_label[FIELD_LABEL_DEFINITION] = "Global definition: "; m_query_label[FIELD_LABEL_FUNC_CALLED] = "Functions called by: "; m_query_label[FIELD_LABEL_FUNC_CALLER] = "Functions calling: "; m_query_label[FIELD_LABEL_FIND_TEXT] = "Text: "; #ifdef HAVE_TEXT_CHANGE_CAPABILITY m_query_label[FIELD_LABEL_CHANGE_TEXT] = "Change text: "; #endif // HAVE_TEXT_CHANGE_CAPABILITY m_query_label[FIELD_LABEL_FIND_REGEXP] = "Regular exp: "; m_query_label[FIELD_LABEL_FIND_FILE] = "File: "; m_query_label[FIELD_LABEL_INCLUDE] = "File including: "; cbreak(); echo(); nonl(); on_resize(SIGWINCH, this); if (FCONFIG->get_update_on_startup() == true) update(); // Update the database set_status(COPYRIGHT_MSG); // Display the copyright message top_panel(m_panel_user); doupdate(); } /* * FUNCTION: CursUI::on_resize * * DESCRIPTION: This is a callback function hat should be called each time * the terminal is resized. * It recalculates the size of each window in the terminal * and reflects this modification. * * IN: signo The signal caught. * arg An optional argument * IN-OUT: * RETURN CODE: */ void CursUI::on_resize (int signo, void* arg) { CursUI* instance = (CursUI*)arg; WINDOW* wnd_user; WINDOW* wnd_results; WINDOW* wnd_status; // Update xterm size... endwin(); #ifndef HAVE_LIBNCURSES initscr(); #endif // HAVE_LIBNCURSES doupdate(); // Get screen size getmaxyx(stdscr, instance->m_wndHeight, instance->m_wndWidth); if (instance->m_wndHeight <= (FIELD_LABEL_INCLUDE - 3)) instance->terminate("Terminal too small. Exiting..."); if (instance->m_form_query != NULL) free_form(instance->m_form_query); if (instance->m_panel_user != NULL) { del_panel(instance->m_panel_user); del_panel(instance->m_panel_results); del_panel(instance->m_panel_status); delwin(panel_window(instance->m_panel_results)); delwin(panel_window(instance->m_panel_status)); } instance->m_maxResults = instance->m_wndHeight - FIELD_LABEL_INCLUDE - 2; // Create windows if ((wnd_results = newwin(instance->m_maxResults, instance->m_wndWidth, 0, 0)) == NULL) { throw DbUser_Exception("Failed to create new window"); } if ((wnd_user = newwin(FIELD_LABEL_INCLUDE + 1, instance->m_wndWidth, instance->m_wndHeight - FIELD_LABEL_INCLUDE - 1, 0)) == NULL) { throw DbUser_Exception("Failed to create new window"); } if ((wnd_status = newwin(1, instance->m_wndWidth, instance->m_wndHeight - FIELD_LABEL_INCLUDE - 2, 0)) == NULL) { throw DbUser_Exception("Failed to create new window"); } instance->m_panel_user = new_panel(wnd_user); instance->m_panel_results = new_panel(wnd_results); instance->m_panel_status = new_panel(wnd_status); keypad(wnd_user, TRUE); wattron(wnd_status, A_REVERSE | A_BOLD); instance->do_refresh(); instance->create_user_fields(); instance->create_result_fields(); instance->m_fmt_results.erase(instance->m_fmt_results.begin(), instance->m_fmt_results.end()); instance->m_curr_fmt_result = 0; instance->m_curr_result = instance->m_result.begin(); instance->m_first_results = true; instance->m_results_per_page = instance->m_wndHeight - FIELD_LABEL_INCLUDE - 2; if (instance->m_result.size() != 0) instance->show_next_results(); else { update_panels(); doupdate(); } instance->set_status("Screen resize completed"); if (instance->m_mode == MODE_SELECT) { #ifndef HAVE_LIBNCURSES noecho(); #endif // HAVE_LIBNCURSES instance->highlight(true); } } /* * FUNCTION: CursUI::do_loop * * DESCRIPTION: This function implements the main loop. * * IN: * IN-0UT: * RETURN CODE: */ void CursUI::do_loop () throw () { for (;;) { int key; key = wgetch(panel_window(m_panel_user)); do_key(key); } } /* * FUNCTION: CursUI::on_exit * * DESCRIPTION: This is a callback function hat should be called when the user * exits the application * It exits gracefully the curses mode. * If the interface is waiting for selection input, this function * cancels the selection. * * IN: signo The signal caught. * arg A pointer to the instance * IN-OUT: * RETURN CODE: */ void CursUI::on_exit (int signo, void* arg) { CursUI* instance = (CursUI*)arg; if (instance->m_selecting_changes == true) { instance->m_selecting_changes = false; instance->m_mode = MODE_QUERY; instance->m_all_selected = false; for (unsigned i = 0; i < instance->m_results_per_page; ++i) set_field_back(instance->m_field_res[i*RES_FIELDS], A_NORMAL); echo(); form_driver(instance->m_form_query, REQ_DEL_LINE); instance->highlight(false); top_panel(instance->m_panel_user); instance->set_status("Operation cancelled"); return; } delwin(panel_window(instance->m_panel_user)); delwin(panel_window(instance->m_panel_results)); delwin(panel_window(instance->m_panel_status)); endwin(); exit(0); } /* * FUNCTION: CursUI::do_refresh * * DESCRIPTION: This function refresh the whole screen * * IN: * IN-OUT: * RETURN CODE: */ void CursUI::do_refresh () throw() { clear(); wclear(panel_window(m_panel_user)); wclear(panel_window(m_panel_status)); wclear(panel_window(m_panel_results)); set_status("Ready"); update_panels(); doupdate(); } /* * FUNCTION: CursUI::DoUserFields * * DESCRIPTION: This function creates the form used by the user for making * queries in the database * * IN: * IN-OUT: * RETURN CODE: */ void CursUI::create_user_fields () { // Create fields m_field_query[FIELD_LABEL_C_SYMBOL] = new_field(1, m_query_label[FIELD_LABEL_C_SYMBOL].size(), FIELD_LABEL_C_SYMBOL, 0, 0, 0); set_field_buffer(m_field_query[FIELD_LABEL_C_SYMBOL], 0, (char*)m_query_label[FIELD_LABEL_C_SYMBOL].c_str()); field_opts_off(m_field_query[FIELD_LABEL_C_SYMBOL], O_ACTIVE); m_field_query[FIELD_LABEL_DEFINITION] = new_field(1, m_query_label[FIELD_LABEL_DEFINITION].size(), FIELD_LABEL_DEFINITION, 0, 0, 0); set_field_buffer(m_field_query[FIELD_LABEL_DEFINITION], 0, (char*)m_query_label[FIELD_LABEL_DEFINITION].c_str()); field_opts_off(m_field_query[FIELD_LABEL_DEFINITION], O_ACTIVE); m_field_query[FIELD_LABEL_FUNC_CALLED] = new_field(1, m_query_label[FIELD_LABEL_FUNC_CALLED].size(), FIELD_LABEL_FUNC_CALLED, 0, 0, 0); set_field_buffer(m_field_query[FIELD_LABEL_FUNC_CALLED], 0, (char*)m_query_label[FIELD_LABEL_FUNC_CALLED].c_str()); field_opts_off(m_field_query[FIELD_LABEL_FUNC_CALLED], O_ACTIVE); m_field_query[FIELD_LABEL_FUNC_CALLER] = new_field(1, m_query_label[FIELD_LABEL_FUNC_CALLER].size(), FIELD_LABEL_FUNC_CALLER, 0, 0, 0); set_field_buffer(m_field_query[FIELD_LABEL_FUNC_CALLER], 0, (char*)m_query_label[FIELD_LABEL_FUNC_CALLER].c_str()); field_opts_off(m_field_query[FIELD_LABEL_FUNC_CALLER], O_ACTIVE); m_field_query[FIELD_LABEL_FIND_TEXT] = new_field(1, m_query_label[FIELD_LABEL_FIND_TEXT].size(), FIELD_LABEL_FIND_TEXT, 0, 0, 0); set_field_buffer(m_field_query[FIELD_LABEL_FIND_TEXT], 0, (char*)m_query_label[FIELD_LABEL_FIND_TEXT].c_str()); field_opts_off(m_field_query[FIELD_LABEL_FIND_TEXT], O_ACTIVE); #ifdef HAVE_TEXT_CHANGE_CAPABILITY m_field_query[FIELD_LABEL_CHANGE_TEXT] = new_field(1, m_query_label[FIELD_LABEL_CHANGE_TEXT].size(), FIELD_LABEL_CHANGE_TEXT, 0, 0, 0); set_field_buffer(m_field_query[FIELD_LABEL_CHANGE_TEXT], 0, (char*)m_query_label[FIELD_LABEL_CHANGE_TEXT].c_str()); field_opts_off(m_field_query[FIELD_LABEL_CHANGE_TEXT], O_ACTIVE); #endif // HAVE_TEXT_CHANGE_CAPABILITY m_field_query[FIELD_LABEL_FIND_REGEXP] = new_field(1, m_query_label[FIELD_LABEL_FIND_REGEXP].size(), FIELD_LABEL_FIND_REGEXP, 0, 0, 0); set_field_buffer(m_field_query[FIELD_LABEL_FIND_REGEXP], 0, (char*)m_query_label[FIELD_LABEL_FIND_REGEXP].c_str()); field_opts_off(m_field_query[FIELD_LABEL_FIND_REGEXP], O_ACTIVE); m_field_query[FIELD_LABEL_FIND_FILE] = new_field(1, m_query_label[FIELD_LABEL_FIND_FILE].size(), FIELD_LABEL_FIND_FILE, 0, 0, 0); set_field_buffer(m_field_query[FIELD_LABEL_FIND_FILE], 0, (char*)m_query_label[FIELD_LABEL_FIND_FILE].c_str()); field_opts_off(m_field_query[FIELD_LABEL_FIND_FILE], O_ACTIVE); m_field_query[FIELD_LABEL_INCLUDE] = new_field(1, m_query_label[FIELD_LABEL_INCLUDE].size(), FIELD_LABEL_INCLUDE, 0, 0, 0); set_field_buffer(m_field_query[FIELD_LABEL_INCLUDE], 0, (char*)m_query_label[FIELD_LABEL_INCLUDE].c_str()); field_opts_off(m_field_query[FIELD_LABEL_INCLUDE], O_ACTIVE); m_field_query[FIELD_C_SYMBOL] = new_field(1, m_wndWidth - m_query_label[FIELD_LABEL_C_SYMBOL].size() - 1, FIELD_LABEL_C_SYMBOL, m_query_label[FIELD_LABEL_C_SYMBOL].size(), 0, 0); m_field_query[FIELD_DEFINITION] = new_field(1, m_wndWidth - m_query_label[FIELD_LABEL_DEFINITION].size() - 1, FIELD_LABEL_DEFINITION, m_query_label[FIELD_LABEL_DEFINITION].size(), 0, 0); m_field_query[FIELD_FUNC_CALLED] = new_field(1, m_wndWidth - m_query_label[FIELD_LABEL_FUNC_CALLED].size() - 1, FIELD_LABEL_FUNC_CALLED, m_query_label[FIELD_LABEL_FUNC_CALLED].size(), 0, 0); m_field_query[FIELD_FUNC_CALLER] = new_field(1, m_wndWidth - m_query_label[FIELD_LABEL_FUNC_CALLER].size() - 1, FIELD_LABEL_FUNC_CALLER, m_query_label[FIELD_LABEL_FUNC_CALLER].size(), 0, 0); m_field_query[FIELD_FIND_TEXT] = new_field(1, m_wndWidth - m_query_label[FIELD_LABEL_FIND_TEXT].size() - 1, FIELD_LABEL_FIND_TEXT, m_query_label[FIELD_LABEL_FIND_TEXT].size(), 0, 0); #ifdef HAVE_TEXT_CHANGE_CAPABILITY m_field_query[FIELD_CHANGE_TEXT] = new_field(1, m_wndWidth - m_query_label[FIELD_LABEL_CHANGE_TEXT].size() - 1, FIELD_LABEL_CHANGE_TEXT, m_query_label[FIELD_LABEL_CHANGE_TEXT].size(), 0, 0); #endif // HAVE_TEXT_CHANGE_CAPABILITY m_field_query[FIELD_FIND_REGEXP] = new_field(1, m_wndWidth - m_query_label[FIELD_LABEL_FIND_REGEXP].size() - 1, FIELD_LABEL_FIND_REGEXP, m_query_label[FIELD_LABEL_FIND_REGEXP].size(), 0, 0); m_field_query[FIELD_FIND_FILE] = new_field(1, m_wndWidth - m_query_label[FIELD_LABEL_FIND_FILE].size() - 1, FIELD_LABEL_FIND_FILE, m_query_label[FIELD_LABEL_FIND_FILE].size(), 0, 0); m_field_query[FIELD_INCLUDE] = new_field(1, m_wndWidth - m_query_label[FIELD_LABEL_INCLUDE].size() - 1, FIELD_LABEL_INCLUDE, m_query_label[FIELD_LABEL_INCLUDE].size(), 0, 0); m_field_query[FIELD_INCLUDE + 1] = NULL; m_form_query = new_form(m_field_query); set_form_win(m_form_query, panel_window(m_panel_user)); post_form(m_form_query); update_panels(); doupdate(); } /* * FUNCTION: CursUI::create_result_fields * * DESCRIPTION: This function creates all the fields required to display the * result * * IN: * IN-OUT: * RETURN CODE: */ void CursUI::create_result_fields () { unsigned int i; size_t max_file = 0; size_t max_scope = 0; size_t max_lineno = 0; if (m_result.size() == 0) return; if (m_form_result != NULL) { unpost_form(m_form_result); free_form(m_form_result); for (i = 0; i < m_maxResults ; i++) { free_field(m_field_res[i * RES_FIELDS + 0]); free_field(m_field_res[i * RES_FIELDS + 1]); free_field(m_field_res[i * RES_FIELDS + 2]); free_field(m_field_res[i * RES_FIELDS + 3]); free_field(m_field_res[i * RES_FIELDS + 4]); } } // Get longest fields for (unsigned int i = 0; (i < m_results_per_page) && (m_curr_fmt_result + i < m_result.size()); i++) { size_t tmp; tmp = m_fmt_results[m_curr_fmt_result + i].file_without_path.size(); if (tmp > max_file) max_file = tmp; tmp = m_fmt_results[m_curr_fmt_result + i].scope.size(); if (tmp > max_scope) max_scope = tmp; tmp = m_fmt_results[m_curr_fmt_result + i].lineno.size(); if (tmp > max_lineno) max_lineno = tmp; } // Give some space between each field max_file += 2; max_scope += 2; max_lineno += 2; // Create each field for each result for (i = 0; i < m_maxResults; i++) { m_field_res[i*RES_FIELDS+0] = new_field(1, 3, i, 0, 0, 0); if (m_selecting_changes == true && m_selection[m_curr_fmt_result + i] == true) { if (m_curr_fmt_result + i < m_result.size()) set_field_back(m_field_res[i*RES_FIELDS+0], A_BOLD | A_REVERSE); } set_field_fore(m_field_res[i*RES_FIELDS+0], A_BOLD); m_field_res[i*RES_FIELDS+1] = new_field(1, max_file, i, 3, 0, 0); m_field_res[i*RES_FIELDS+2] = new_field(1, max_scope, i, 3 + max_file, 0, 0); m_field_res[i*RES_FIELDS+3] = new_field(1, max_lineno, i, 3 + max_file + max_scope, 0, 0); m_field_res[i*RES_FIELDS+4] = new_field(1, m_wndWidth - 3 - max_file - max_scope - max_lineno, i, 3 + max_file + max_scope + max_lineno, 0, 0); } // for loop m_field_res[i*RES_FIELDS] = NULL; // Create form m_form_result = new_form(m_field_res); set_form_win(m_form_result, panel_window(m_panel_results)); post_form(m_form_result); } /* * FUNCTION: CursUI::do_key * * DESCRIPTION: This function processes an event associated with a key pressed * ny the user * * IN: key The key pressed * IN-OUT: * RETURN CODE: */ void CursUI::do_key (int key) { static bool escape_next_key = false; // Check if key is a digit key if ((key >= '0') && (key <= '9')) { string value; FIELD* field = current_field(m_form_query); // Get text entered by user form_driver(m_form_query, REQ_VALIDATION); value = field_buffer(field, 0); if (value.size() == 1) { form_driver(m_form_query, REQ_VALIDATION); form_driver(m_form_query, REQ_DEL_LINE); start_editor(key - '0'); } } switch (m_mode) { case MODE_QUERY: { int backspace; backspace = (int)erasechar(); // KEY_BACKSPACE is unreliable... if (escape_next_key == false) { if (key == backspace || key == KEY_BACKSPACE) { form_driver(m_form_query, REQ_CLR_EOF); form_driver(m_form_query, REQ_PREV_CHAR); form_driver(m_form_query, REQ_DEL_CHAR); } else if (key == FCONFIG->get_key_next_query() || key == KEY_DOWN) { form_driver(m_form_query, REQ_DEL_LINE); form_driver(m_form_query, REQ_NEXT_FIELD); } else if (key == FCONFIG->get_key_prev_query() || key == KEY_UP) { form_driver(m_form_query, REQ_DEL_LINE); form_driver(m_form_query, REQ_PREV_FIELD); } else if (key == FCONFIG->get_key_switch_mode()) { m_mode = MODE_SELECT; noecho(); form_driver(m_form_query, REQ_DEL_LINE); highlight(true); } else if (key == FCONFIG->get_key_next_completion()) { form_driver(m_form_query, REQ_CLR_EOF); m_next_completion = show_next_completion(); } else if (key == FCONFIG->get_key_prev_completion()) { form_driver(m_form_query, REQ_CLR_EOF); m_next_completion = show_prev_completion(); } else if (key == FCONFIG->get_key_rebuild_db()) { update(); form_driver(m_form_query, REQ_DEL_LINE); update_panels(); doupdate(); } else if (key == FCONFIG->get_key_help()) { form_driver(m_form_query, REQ_DEL_LINE); WndHelp help; } else if (key == FCONFIG->get_key_escape()) { escape_next_key = true; } else if (key == KEY_QUIT) { endwin(); exit(0); // Not very nice, but it works! } else if (key == '\n' || key == '\r') { do_request(); form_driver(m_form_query, REQ_DEL_LINE); } else if (key == KEY_RIGHT) { show_next_results(); form_driver(m_form_query, REQ_BEG_LINE); } else if (key == KEY_LEFT) { show_prev_results(); form_driver(m_form_query, REQ_BEG_LINE); } else if (key == FCONFIG->get_key_history_forward()) { if ((m_history.size() == 0) || (m_history_it == m_history.end())) { form_driver(m_form_query, REQ_DEL_LINE); } else { ++m_history_it; form_driver(m_form_query, REQ_DEL_LINE); show_history(); } } else if (key == FCONFIG->get_key_history_backward()) { if ((m_history.size() == 0) || (m_history_it == m_history.begin())) { form_driver(m_form_query, REQ_DEL_LINE); show_history(); break; } else { --m_history_it; form_driver(m_form_query, REQ_DEL_LINE); show_history(); } } else { form_driver(m_form_query, key); form_driver(m_form_query, REQ_DEL_CHAR); form_driver(m_form_query, REQ_CLR_EOF); m_next_completion = false; } } else { // Escape key has been pressed. escape_next_key = false; form_driver(m_form_query, key); form_driver(m_form_query, REQ_DEL_CHAR); form_driver(m_form_query, REQ_CLR_EOF); m_next_completion = false; } } break; case MODE_SELECT: { if (key == FCONFIG->get_key_next_selection() || key == KEY_DOWN) highlightNext(); else if (key == FCONFIG->get_key_prev_selection() || key == KEY_UP) highlightPrev(); else if (key == FCONFIG->get_key_next_page() || key == KEY_RIGHT) { highlight(false); show_next_results(); highlight(true); } else if (key == FCONFIG->get_key_prev_page() || key == KEY_LEFT) { highlight(false); show_prev_results(); highlight(true); } else if (key == FCONFIG->get_key_switch_mode() || key == KEY_TAB) { if (m_selecting_changes == true) { set_status("press ESCAPE to cancel all changes"); break; } m_mode = MODE_QUERY; echo(); form_driver(m_form_query, REQ_DEL_LINE); highlight(false); top_panel(m_panel_user); doupdate(); } else if (key == FCONFIG->get_key_rebuild_db()) { update(); set_status("Update completed"); } else if (key == FCONFIG->get_key_help()) { WndHelp help; } else if (key == ctrl('a')) { if (m_selecting_changes == true) { size_t i; int back; if (m_all_selected == false) { m_all_selected = true; back = A_REVERSE; } else { m_all_selected = false; back = A_NORMAL; } m_selection.assign(m_result.size(), m_all_selected); for(i = 0; i < m_results_per_page; ++i) { if (m_curr_fmt_result + i >= m_result.size()) break; set_field_back(m_field_res[i * 5], back); } /* * This is a hack. I still don't understand why it * doesn't work otherwise... */ form_driver(m_form_result, REQ_PREV_FIELD); update_panels(); doupdate(); form_driver(m_form_result, REQ_NEXT_FIELD); } } else if (key == 'q' || key == KEY_QUIT) { if (m_selecting_changes == true) apply_changed_text(); else { endwin(); exit(0); // Not very nice, but it works! } } else if (key == 's') { if (m_selecting_changes == true) { FIELD* field; int index; int back; field = current_field(m_form_result); index = m_curr_fmt_result + field_index(field) / 5; if (m_selection[index] == false) { m_selection[index] = true; back = A_BOLD | A_REVERSE; } else { m_selection[index] = false; back = A_NORMAL; } set_field_back(field, back); highlightNext(); } } else if (key == ' ' || key == '\r' || key == '\n') { start_editor(field_index(current_field(m_form_result))/5); break; } } break; } } /* * FUNCTION: CursUI::set_status * * DESCRIPTION: This function displays a message in the status window. * The message is centered justify * * IN: status The status to be displayed * center true if text is to be centered. * IN-OUT: * RETURN CODE: */ void CursUI::set_status (string status, bool center) { int pad; wdeleteln(panel_window(m_panel_status)); if (center == true) { // Calculate padding pad = (int)((m_wndWidth / 2) - (status.size() / 2)); for (int i = 0; i < pad; i++) { status.insert(0, " "); status.append(" "); } } else { pad = (int)(m_wndWidth - status.size()); for (int i = 0; i < pad; i++) status.append(" "); } // DISPLAY STATUS mvwprintw(panel_window(m_panel_status), 0, 0, (char*)status.c_str()); update_panels(); doupdate(); } /* * FUNCTION: CursUI::do_request * * DESCRIPTION: This function processes a search request * * IN: * IN-OUT: * RETURN CODE: */ void CursUI::do_request () { FIELD* field; string value; string tmp; size_t last; query_t query; // Clear previous results wclear(panel_window(m_panel_results)); // Get the active field. field = current_field(m_form_query); // Get text entered by user form_driver(m_form_query, REQ_VALIDATION); value = field_buffer(field, 0); // Check that field is not empty if (value[0] == ' ') return; set_status("Working..."); last = value.find_last_not_of(' '); value = value.substr(0, last+1); query.query = value; query.field = field; m_history.push_back(query); m_history_it = m_history.end(); // Query the database switch (field_index(field)) { case FIELD_C_SYMBOL: do_query(QUERY_SYMBOL, value); break; case FIELD_DEFINITION: do_query(QUERY_DEFINITION, value); break; case FIELD_FUNC_CALLED: do_query(QUERY_CALLED, value); break; case FIELD_FUNC_CALLER: do_query(QUERY_CALL, value); break; case FIELD_FIND_TEXT: do_query(QUERY_TEXT, value); break; case FIELD_FIND_REGEXP: do_query(QUERY_REGEXP, value); break; #ifdef HAVE_TEXT_CHANGE_CAPABILITY case FIELD_CHANGE_TEXT: tmp = value; do_query(QUERY_TEXT, tmp); break; #endif // HAVE_TEXT_CHANGE_CAPABILITY case FIELD_FIND_FILE: do_query(QUERY_FILE, value); break; case FIELD_INCLUDE: do_query(QUERY_INCLUDE, value); break; default: return; } #ifdef HAVE_TEXT_CHANGE_CAPABILITY if (field_index(field) != FIELD_CHANGE_TEXT) { #endif // HAVE_TEXT_CHANGE_CAPABILITY set_current_field(m_form_query, field); // If we have only one match, start the editor if (m_result.size() == 1 && field_index(field) != FIELD_INCLUDE && field_index(field) != FIELD_FUNC_CALLED) { start_editor(0); } if (m_result.size() != 0) { m_mode = MODE_SELECT; noecho(); form_driver(m_form_query, REQ_DEL_LINE); highlight(true); } #ifdef HAVE_TEXT_CHANGE_CAPABILITY } else { string status; char new_value[255]; if (m_result.size() == 0) return; m_selecting_changes = true; status = "Change \"" + value + "\" to: "; set_status(status, false); mvwgetstr(panel_window(m_panel_status), 0, status.size(), new_value); m_old_value = value; m_new_value = new_value; select_changes(); } #endif // HAVE_TEXT_CHANGE_CAPABILITY } /* * FUNCTION: CursUI::display_result * * DESCRIPTION: This function displays in the result screen the result * of a databse query * * IN: count The number of matches * IN-OUT: * RETURN CODE: */ void CursUI::display_result () { if (m_result.size() == 0) { set_status("No match found"); clear_results(); return; } // Clear formatted result list m_fmt_results.erase(m_fmt_results.begin(), m_fmt_results.end()); m_curr_result = m_result.begin(); m_curr_fmt_result = 0; m_first_results = true; // Display the first results show_next_results(); } /* * FUNCTION: CursUI::show_next_results * * DESCRIPTION: This function displays the next results available. * * IN: * IN-OUT: * RETURN CODE: */ void CursUI::show_next_results () { // Are there next results? if (m_result.size() == 0) return; if ((m_first_results == false) && (m_curr_fmt_result + m_results_per_page >= m_result.size())) { return; } wclear(panel_window(m_panel_results)); form_driver(m_form_result, REQ_FIRST_FIELD); if (m_first_results == false) m_curr_fmt_result += m_results_per_page; m_first_results = false; // Have we already shown these results? if (m_curr_fmt_result + m_results_per_page >= m_fmt_results.size()) { format_next_results(); } create_result_fields(); for (unsigned int i = 0; (i < m_results_per_page) && ((m_curr_fmt_result + i) < m_fmt_results.size()); ++i) { char resNbr[4]; result_elem_t elem = m_fmt_results[m_curr_fmt_result + i]; sprintf(resNbr, "%u", i + 1); if (m_selecting_changes == true && m_selection[m_curr_fmt_result + i] == true) { set_field_back(m_field_res[i*RES_FIELDS+0], A_BOLD | A_REVERSE); } set_field_buffer(m_field_res[i*RES_FIELDS+0], 0, resNbr); set_field_buffer(m_field_res[i*RES_FIELDS+1], 0, (char*)elem.file_without_path.c_str()); set_field_buffer(m_field_res[i*RES_FIELDS+2], 0, (char*)elem.scope.c_str()); set_field_buffer(m_field_res[i*RES_FIELDS+3], 0, (char*)elem.lineno.c_str()); set_field_buffer(m_field_res[i*RES_FIELDS+4], 0, (char*)elem.line.c_str()); } show_result_status(); // Refresh screen update_panels(); doupdate(); } /* * FUNCTION: CursUI::show_prev_results * * DESCRIPTION: This function displays the previous results available. * * IN: * IN-OUT: * RETURN CODE: */ void CursUI::show_prev_results () { // Are there previous results? if (m_curr_fmt_result == 0) return; wclear(panel_window(m_panel_results)); form_driver(m_form_result, REQ_FIRST_FIELD); m_curr_fmt_result -= m_results_per_page; create_result_fields(); for (unsigned int i = 0; (i < m_results_per_page) && ((m_curr_fmt_result + i) <= m_fmt_results.size()); ++i) { char resNbr[4]; result_elem_t elem = m_fmt_results[m_curr_fmt_result + i]; sprintf(resNbr, "%u", i + 1); if (m_selecting_changes == true && m_selection[m_curr_fmt_result + i] == true) { set_field_back(m_field_res[i*RES_FIELDS+0], A_BOLD | A_REVERSE); } set_field_buffer(m_field_res[i*RES_FIELDS+0], 0, resNbr); set_field_buffer(m_field_res[i*RES_FIELDS+1], 0, (char*)elem.file_without_path.c_str()); set_field_buffer(m_field_res[i*RES_FIELDS+2], 0, (char*)elem.scope.c_str()); set_field_buffer(m_field_res[i*RES_FIELDS+3], 0, (char*)elem.lineno.c_str()); set_field_buffer(m_field_res[i*RES_FIELDS+4], 0, (char*)elem.line.c_str()); } show_result_status(); } /* * FUNCTION: CursUI::clear_results * * DESCRIPTION: This function removed all results from the screen * * IN: * IN-OUT: * RETURN CODE: */ void CursUI::clear_results () { for (unsigned int i = 0; i < m_results_per_page; ++i) { set_field_buffer(m_field_res[i*RES_FIELDS+0], 0, ""); set_field_buffer(m_field_res[i*RES_FIELDS+0], 1, ""); set_field_buffer(m_field_res[i*RES_FIELDS+0], 2, ""); set_field_buffer(m_field_res[i*RES_FIELDS+0], 3, ""); set_field_buffer(m_field_res[i*RES_FIELDS+0], 4, ""); } // Refresh screen update_panels(); doupdate(); } /* * FUNCTION: CursUI::format_next_results * * DESCRIPTION: This function prepares the next set of results for proper * display in the result form. * * IN: * IN-OUT: * RETURN CODE: */ void CursUI::format_next_results () { int j; // Iterate the matches for (unsigned int i = 0; (i < m_results_per_page) && (m_curr_result != m_result.end()); ++i, ++m_curr_result) { result_elem_t fmt_result; fmt_result.filename = m_curr_result->file.c_str(); /* * If the line nbr for the match is 0, it means that the result * is not a location inside a file. Instead, the result is the file * itself. */ if (m_curr_result->lineno != 0) { char tmp[10]; sprintf(tmp, "%u", m_curr_result->lineno); fmt_result.lineno = tmp; fmt_result.scope = m_curr_result->scope; fmt_result.line = read_line(fmt_result.filename, m_curr_result->lineno); // Remove absolute path for (j = fmt_result.filename.size() - 1; j >= 0; --j) { if (fmt_result.filename.at(j) == '/') { fmt_result.file_without_path.assign (fmt_result.filename, j + 1, fmt_result.filename.size() - j -1); break; } } if (j < 0) fmt_result.file_without_path = fmt_result.filename; } else fmt_result.file_without_path = fmt_result.filename; // Add result in the list m_fmt_results.push_back(fmt_result); } } /* * FUNCTION: CursUI::start_editor * * DESCRIPTION: This function starts a text editor at the position given * by the result associated with the given index as * appearing on the screen. * * IN: index The selected result * IN-OUT: * RETURN CODE: */ void CursUI::start_editor (int index) { if ((index > (int) m_results_per_page) || (m_curr_fmt_result + index >= m_fmt_results.size())) { // Invalid selection return; } string command; char* editor; string lineno; if (m_fmt_results[m_curr_fmt_result + index].lineno.size() != 0) lineno = m_fmt_results[m_curr_fmt_result + index].lineno; else lineno = "1"; editor = getenv("VIEWER"); if ((editor == 0) || (strlen(editor) == 0)) { editor = getenv("EDITOR"); if ((editor == 0) || (strlen(editor) == 0)) editor = "vi"; } if (strcmp(editor, "elvis") == 0) { command = "elvis -c "; command += lineno; command += " "; command += m_fmt_results[m_curr_fmt_result + index].filename; } else { // Default editor is vi like command = editor; command += " +"; command += lineno; command += " "; command += m_fmt_results[m_curr_fmt_result + index].filename; } SIGLINK->Deregister(SIGWINCH, CursUI::on_resize); // Quit curses mode endwin(); // Start editor if (system(command.c_str()) == -1) set_status("Command \"" + command + "\" failed."); // Return to curses mode #ifndef HAVE_LIBNCURSES initscr(); #endif // HAVE_LIBNCURSES doupdate(); // Has the terminal been resize? int x, y; getmaxyx(stdscr, y, x); if (x != m_wndWidth || y != m_wndHeight) on_resize(SIGWINCH, this); else { update_panels(); doupdate(); keypad(panel_window(m_panel_user), TRUE); nonl(); cbreak(); noecho(); } SIGLINK->Register(SIGWINCH, CursUI::on_resize, this); } /* * FUNCTION: CursUI::highlight * * DESCRIPTION: This function highlights on or off the selected result. * * IN: highlight true if result needs to be highlighted * false otherwise. * IN-OUT: * RETURN CODE: */ void CursUI::highlight (bool highlight) { // Return if no result if (m_result.size() == 0) { m_mode = MODE_QUERY; return; } FIELD* field; chtype attr; if (highlight == true) attr = A_REVERSE | A_BOLD; else attr = A_NORMAL; // Get the active field. field = current_field(m_form_result); // highlight on or off selection. for (int i = 0; i < 5; i++) { FIELD* field2; int index; index = field_index(field); field2 = current_field(m_form_result); if (i == 0 && m_selecting_changes == true && (m_selection[m_curr_fmt_result + index / 5] == false)) { set_field_back(field2, A_BOLD); } else set_field_back(field2, attr); form_driver(m_form_result, REQ_NEXT_FIELD); } set_current_field(m_form_result, field); // Refresh screen update_panels(); doupdate(); } /* * FUNCTION: CursUI::highlightNext * * DESCRIPTION: This function highlights the next result * * IN: * IN-OUT: * RETURN CODE: */ void CursUI::highlightNext () { FIELD* field; FIELD* field2; int index; // Is there a next result? field2 = current_field(m_form_result); index = field_index(field2) + 5; if (((m_curr_fmt_result%m_results_per_page)+(index/5) >= m_results_per_page) || (m_curr_fmt_result+(index / 5) >= m_result.size())) { // No more result on this page /* * This is a hack. I still don't understand why it doesn't work * otherwise... */ if (m_selecting_changes == true) { form_driver(m_form_result, REQ_PREV_FIELD); update_panels(); doupdate(); form_driver(m_form_result, REQ_NEXT_FIELD); } return; } // Go to next result for (int i = 0; i < 5; i++) { field2 = current_field(m_form_result); if (m_selecting_changes == false || i != 0 || (m_selecting_changes == true && m_selection[m_curr_fmt_result + (index - 1) / 5] == false)) { set_field_back(field2, A_NORMAL); } form_driver(m_form_result, REQ_NEXT_FIELD); } // Get the active field. field = current_field(m_form_result); for (int i = 0; i < 5; i++) { field2 = current_field(m_form_result); if (i == 0 && m_selecting_changes == true && (m_selection[m_curr_fmt_result + index / 5] == false)) { set_field_back(field2, A_BOLD); } else set_field_back(field2, A_BOLD | A_REVERSE); form_driver(m_form_result, REQ_NEXT_FIELD); } set_current_field(m_form_result, field); // Refresh screen update_panels(); doupdate(); } /* * FUNCTION: CursUI::highlightPrev * * DESCRIPTION: This function highlights the previous result * * IN: * IN-OUT: * RETURN CODE: */ void CursUI::highlightPrev () { FIELD* field; FIELD* field2; int index; // Get the active field. field = current_field(m_form_result); index = field_index(field); // Is there a previous result? if (index == 0) return; // Go to previous result for (int i = 0; i < 5; i++) { field2 = current_field(m_form_result); if ((m_selecting_changes == false || i != 0) || (m_selecting_changes == true && m_selection[m_curr_fmt_result + (index / 5)] == false)) { set_field_back(field2, A_NORMAL); } form_driver(m_form_result, REQ_NEXT_FIELD); } set_current_field(m_form_result, field); for (int i = 0; i < 5; i++) form_driver(m_form_result, REQ_PREV_FIELD); field = current_field(m_form_result); for (int i = 0; i < 5; i++) { field2 = current_field(m_form_result); if (i == 0 && m_selecting_changes == true && (m_selection[m_curr_fmt_result + (index - 1) / 5] == false)) { set_field_back(field2, A_BOLD); } else set_field_back(field2, A_BOLD | A_REVERSE); form_driver(m_form_result, REQ_NEXT_FIELD); } set_current_field(m_form_result, field); // Refresh screen update_panels(); doupdate(); } /* * FUNCTION: CursUI::show_result_status * * DESCRIPTION: This function refresh the status bar with the message: * "Match x -> y out of z" * * IN: * IN-OUT: * RETURN CODE: */ void CursUI::show_result_status () { char msg[80]; unsigned int y; y = m_curr_fmt_result + m_results_per_page; if (y >= m_result.size()) y = m_result.size(); sprintf(msg, "Match %u -> %u out of %u", m_curr_fmt_result + 1, y, m_result.size()); set_status(msg); } /* * FUNCTION: CursUI::show_history * * DESCRIPTION: This function displays a query from the history * * IN: * IN-OUT: * RETURN CODE: */ void CursUI::show_history () { if ((m_history.size() == 0) || (m_history_it == m_history.end())) return; set_current_field(m_form_query, m_history_it->field); set_field_buffer(m_history_it->field, 0, (char*)m_history_it->query.c_str()); update_panels(); doupdate(); } /* * FUNCTION: CursUI::terminate * * DESCRIPTION: This function is called when a fatal error occurs. * It terminates the application and displays a message. * * IN: reason The reason for termination * IN-OUT: * RETURN CODE: */ void CursUI::terminate (const string& reason) { endwin(); if (reason.size() != 0) cerr << reason << endl; exit(-1); } /* * FUNCTION: CursUI::show_next_completion * * DESCRIPTION: This function shows the next possible completion. * * IN: * IN-OUT: * RETURN CODE: true if this completion worked. false otherwise. */ bool CursUI::show_next_completion () { FIELD* field; string completion; int total; int index; char status[80]; field = current_field(m_form_query); // Get text entered by user if (m_next_completion == false) { form_driver(m_form_query, REQ_VALIDATION); m_stub = field_buffer(field, 0); if (m_stub.size() == 0) return false; m_stub.erase(m_stub.find(' ')); if (m_stub.size() == 0) return false; set_status("Working..."); } // Get completion completion = next_completion(m_stub, index, total); if (completion.size() == 0) { set_status("No completion"); return false; } sprintf(status, "Completion %d out of %d", index, total); set_status(status); // Show the completion set_field_buffer(field, 0, (char*)completion.c_str()); update_panels(); doupdate(); return true; } /* * FUNCTION: CursUI::show_prev_completion * * DESCRIPTION: This function shows the previous possible completion. * * IN: * IN-OUT: * RETURN CODE: true if this completion worked. false otherwise. */ bool CursUI::show_prev_completion () { FIELD* field; string completion; int total; int index; char status[80]; field = current_field(m_form_query); // Get text entered by user if (m_next_completion == false) { form_driver(m_form_query, REQ_VALIDATION); m_stub = field_buffer(field, 0); if (m_stub.size() == 0) return false; m_stub.erase(m_stub.find(' ')); if (m_stub.size() == 0) return false; set_status("Working..."); } // Get completion completion = prev_completion(m_stub, index, total); if (completion.size() == 0) { set_status("No completion"); return false; } sprintf(status, "Completion %d out of %d", index, total); set_status(status); // Show the completion set_field_buffer(field, 0, (char*)completion.c_str()); update_panels(); doupdate(); return true; } /* * FUNCTION: CursUI::select_changes * * DESCRIPTION: This function queries the user for selecting matches which * he wants to apply substitution to. * * IN: * IN-OUT: * OUT: * RETURN CODE: * SIDE EFFECT: */ void CursUI::select_changes () { m_selection.erase(m_selection.begin(), m_selection.end()); m_selection.resize(m_result.size()); m_selection.assign(m_result.size(), false); set_status(m_old_value + " -> " + m_new_value + " (use 's' to select)"); m_mode = MODE_SELECT; noecho(); form_driver(m_form_query, REQ_DEL_LINE); highlight(true); } /* * FUNCTION: CursUI::apply_changed_text * * DESCRIPTION: This function applies the modification of text for the matches * that have been selected by the user. To do so, we write to a * file an 'ed' script that is executed upon closure. * * IN: * IN-OUT: * OUT: * RETURN CODE: * SIDE EFFECT: */ void CursUI::apply_changed_text () { ofstream file; char script[] = "/tmp/freescopeXXXXXX"; string cmd("sh "); string prev_file; string buffer; list::iterator it; size_t i; string::size_type pos = 0; char c; set_status("Confirm changes? [y/n]: "); do { c = wgetch(panel_window(m_panel_status)); } while (c != 'y' && c != 'Y' && c != 'n' && c != 'N'); if (c == 'n' || c == 'N') { m_selecting_changes = false; m_mode = MODE_QUERY; m_all_selected = false; for (unsigned i = 0; i < m_results_per_page; ++i) set_field_back(m_field_res[i*RES_FIELDS], A_NORMAL); echo(); form_driver(m_form_query, REQ_DEL_LINE); highlight(false); top_panel(m_panel_user); set_status("Operation cancelled"); return; } // Get a unique file name mkstemp(script); file.open(script, ios::out); if (file == 0) { set_status("Couldn't create temporary file!"); return; } // Escape special characters while ((pos = m_old_value.find_first_of("/\\[.^*", pos)) != string::npos) { m_old_value.insert(pos++, 1, '\\'); pos += 2; } pos = 0; while ((pos = m_new_value.find_first_of("/\\&", pos)) != string::npos) { m_new_value.insert(pos++, 1, '\\'); pos += 2; } buffer += "ed - <<\\!\n"; it = m_result.begin(); buffer += "e "; buffer += (*it).file; buffer += "\n"; for (i = 0, prev_file = (*it).file; i < m_result.size(); ++i, ++it) { char l[20]; if (m_selection[i] == false) continue; if ((*it).file != prev_file) { buffer += "w\n"; buffer += "e "; buffer += (*it).file; buffer += "\n"; } sprintf(l, "%u", (*it).lineno); buffer += l; buffer += "s/"; buffer += m_old_value; buffer += "/"; buffer += m_new_value; buffer += "/g\n"; prev_file = (*it).file; if (buffer.size() > 512) { file << buffer; buffer.erase(); } } file << buffer; file << "w\nq\n!\n" << endl; file.close(); chmod(script, 0766); cmd += script; set_status("Working..."); system(cmd.c_str()); remove(script); m_selecting_changes = false; m_mode = MODE_QUERY; m_all_selected = false; for (i = 0; i < m_results_per_page; ++i) set_field_back(m_field_res[i*RES_FIELDS], A_NORMAL); // display_result(); m_resFile = NULL; display_result(); show_result_status(); echo(); form_driver(m_form_query, REQ_DEL_LINE); highlight(false); top_panel(m_panel_user); update_panels(); doupdate(); }