/*
* 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 <signal.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fstream>
#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<match_t>::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();
}
syntax highlighted by Code2HTML, v. 0.9.1