/*
 * 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