/* request.cc
 * This file belongs to Worker, a file manager for UN*X/X11.
 * Copyright (C) 2001-2026 Ralf Hoffmann.
 * You can contact me at: ralf@boomerangsworld.de
 *   or http://www.boomerangsworld.de/worker
 *
 * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "request.h"
#include "aguix.h"
#include "awindow.h"
#include "button.h"
#include "text.h"
#include "message.h"
#include "stringgadget.h"
#include "bevelbox.h"
#include "acontainer.h"
#include "acontainerbb.h"
#include "choosebutton.h"
#include "textview.h"

#define ACCEPT_ENTER_FOR_CHOOSE 1

Requester::Requester(AGUIX *parent)
{
  aguix=parent;
  transientawin = NULL;
}

Requester::Requester( class AGUIX *parent, class AWindow *twin )
{
  aguix = parent;
  transientawin = twin;
}

Requester::~Requester()
{
}

int Requester::request(const char *title,const char *text,const char *buttons)
{
  return request( title, text, buttons, REQUEST_NONE );
}

int Requester::request(const char *title,const char *text,const char *buttons, request_flags_t flags)
{
  return request( title, text, buttons, NULL, flags );
}

int Requester::request(const char *title,const char *text,const char *buttons, const AWindow *twin, request_flags_t flags)
{
  int i;
  Button **buts;
  int nbuttons;
  char **buttonss;

  nbuttons=createLines(buttons,&buttonss);
  
  if ( twin == NULL && transientawin == NULL ) {
    twin = aguix->getTransientWindow();
  }
  
  AWindow *win = new AWindow( aguix, 10, 10, 10, 10, title, AWindow::AWINDOW_DIALOG );
  win->create();
  if ( twin != NULL ) {
    win->setTransientForAWindow( twin );
  } else if ( transientawin != NULL ) {
    win->setTransientForAWindow( transientawin );
  }

  AContainer *ac1 = win->setContainer( new AContainer( win, 1, 2 ), true );
  ac1->setMinSpace( 0 );
  ac1->setMaxSpace( 0 );
  ac1->setBorderWidth( 5 );

  AContainerBB *ac1_texts = dynamic_cast<AContainerBB*>( ac1->add( new AContainerBB( win, 1, 1 ), 0, 0 ) );
  ac1_texts->setBorderWidth( 5 );
  win->addMultiLineText( text, *ac1_texts, 0, 0, NULL, NULL );

  AContainer *ac1_buttons = NULL;

  if ( nbuttons == 1 ) {
    ac1_buttons = ac1->add( new AContainer( win, 3, 1 ), 0, 1 );
    ac1_buttons->setMinSpace( 0 );
    ac1_buttons->setMaxSpace( 0 );
  } else {
    ac1_buttons = ac1->add( new AContainer( win, nbuttons, 1 ), 0, 1 );
    ac1_buttons->setMinSpace( 5 );
    ac1_buttons->setMaxSpace( -1 );
  }
  ac1_buttons->setBorderWidth( 0 );

  buts = (Button**)_allocsafe( nbuttons * sizeof( Button* ) );

  if ( nbuttons == 1 ) {
    buts[0] = ac1_buttons->addWidget( new Button( aguix, 0, 0, buttonss[0], 0 ),
                                      1, 0, AContainer::CO_FIX );
  } else {
    for ( i = 0; i < nbuttons; i++ ) {
      buts[i] = ac1_buttons->addWidget( new Button( aguix, 0, 0, buttonss[i], i ),
                                        i, 0, AContainer::CO_FIX );
    }
  }

  for ( i = 0; i < nbuttons; i++ ) {
    buts[i]->setAcceptFocus( true );
  }
  buts[0]->takeFocus();
  win->setDoTabCycling( true );
  win->contMaximize( true, true );
  win->useStippleBackground();
  win->centerScreen();
  win->show();
  AGMessage *msg;
  int endmode=-1;
  for(;endmode==-1;) {
    msg=aguix->WaitMessage(win);
    if(msg!=NULL) {
      switch(msg->type) {
        case AG_CLOSEWINDOW:
          if ( msg->closewindow.window == win->getWindow() ) {
	    if ( ( flags & REQUEST_CANCELWITHLEFT ) != 0 ) {
	      endmode = 0;
	    } else {
	      endmode = nbuttons - 1;
	    }
	  }
          break;
        case AG_BUTTONCLICKED:
	  for(i=0;i<nbuttons;i++) {
	    if(msg->button.button==buts[i]) {
	      endmode=i;
	      break;
	    }
	  }
          break;
	case AG_KEYPRESSED:
	  if ( win->isParent( msg->key.window, false ) == true ) {
	    switch(msg->key.key) {
	      case XK_KP_Enter:
	      case XK_Return:
#ifdef ACCEPT_ENTER_FOR_CHOOSE
		for ( i = 0; i < nbuttons; i++ ) {
		  if ( buts[i]->getHasFocus() == true ) {
		    endmode = i;
		    break;
		  }
		}
#else
		if ( buts[0]->hasFocus() == true ) endmode = 0;
#endif
		break;
	      case XK_Escape:
		if ( ( flags & REQUEST_CANCELWITHLEFT ) != 0 ) {
		  endmode = 0;
		} else {
		  endmode = nbuttons - 1;
		}
		break;
	      case XK_F1:
	        endmode=0;
	        break;
	      case XK_F2:
	        endmode=1;
		if(endmode>=nbuttons) endmode = -1;
	        break;
	      case XK_F3:
	        endmode=2;
		if(endmode>=nbuttons) endmode = -1;
	        break;
	      case XK_F4:
	        endmode=3;
		if(endmode>=nbuttons) endmode = -1;
	        break;
	      case XK_F5:
	        endmode=4;
		if(endmode>=nbuttons) endmode = -1;
	        break;
	      case XK_F6:
	        endmode=5;
		if(endmode>=nbuttons) endmode = -1;
	        break;
	      case XK_F7:
	        endmode=6;
		if(endmode>=nbuttons) endmode = -1;
	        break;
	      case XK_F8:
	        endmode=7;
		if(endmode>=nbuttons) endmode = -1;
	        break;
	      case XK_F9:
	        endmode=8;
		if(endmode>=nbuttons) endmode = -1;
	        break;
	      case XK_F10:
	        endmode=9;
		if(endmode>=nbuttons) endmode = -1;
	        break;
	      case XK_Left:
		win->prevFocus();
		break;
  	      case XK_Right:
		win->nextFocus();
		break;
	    }
	  }
	  break;
      }
      aguix->ReplyMessage(msg);
    }
  }
  delete win;
  for(i=0;i<nbuttons;i++) _freesafe(buttonss[i]);
  _freesafe(buttonss);
  _freesafe(buts);
  return endmode;
}

int Requester::request( const char *title,
                        const char *text,
                        const std::vector< std::string > &button_rows,
                        const AWindow *twin,
                        request_flags_t flags)
{
    std::vector< std::vector< std::pair< std::string, int > > > buttons;
    int max_buttons_per_row = 1;

    for ( const auto &button_row_string : button_rows ) {
        char **buttonss;
        int nbuttons = createLines( button_row_string.c_str(),
                                    &buttonss );

        if ( nbuttons > max_buttons_per_row ) {
            max_buttons_per_row = nbuttons;
        }

        std::vector< std::pair< std::string, int > > button_row;
        for ( int i = 0; i < nbuttons; i++ ) {
            button_row.push_back( { buttonss[i], -1 } );
            _freesafe( buttonss[i] );
        }
        _freesafe( buttonss );

        buttons.push_back( button_row );
    }

    return request( title, text, buttons, twin, flags );
}

static std::pair< int, int > find_button( const std::vector< std::vector< std::pair< Button *, int > > > &buttons,
                                          Button *b )
{
    std::pair< int, int > res = { -1, -1 };

    for ( size_t row = 0; row < buttons.size(); row++ ) {
        for ( size_t col = 0; col < buttons[row].size(); col++ ) {
            if ( buttons[row][col].first == b ) {
                res = { row, col };
                break;
            }
        }
    }

    return res;
}

static int get_end_mode( const std::vector< std::vector< std::pair< Button *, int > > > &buttons,
                                           size_t col )
{
    for ( const auto &row : buttons ) {
        if ( col >= row.size() ) continue;
        if ( row.at( col ).first == NULL ) continue;
        return row.at( col ).second;
    }
    return -1;
}

int Requester::request( const char *title,
                        const char *text,
                        const std::vector< std::vector< std::pair< std::string, int > > > &button_rows,
                        const AWindow *twin,
                        request_flags_t flags)
{
    std::vector< std::vector< std::pair< Button *, int > > > buttons;
    int max_buttons_per_row = 1;

    buttons.resize( button_rows.size() );

    for ( size_t row = 0; row < button_rows.size(); row++ ) {
        size_t nbuttons = button_rows[row].size();
        if ( (int)nbuttons > max_buttons_per_row ) {
            max_buttons_per_row = nbuttons;
        }
    }

    for ( size_t row = 0; row < button_rows.size(); row++ ) {
        buttons[row].resize( max_buttons_per_row );
    }
  
    if ( twin == NULL && transientawin == NULL ) {
        twin = aguix->getTransientWindow();
    }
  
    AWindow *win = new AWindow( aguix, 10, 10, 10, 10, title, AWindow::AWINDOW_DIALOG );
    win->create();
    if ( twin != NULL ) {
        win->setTransientForAWindow( twin );
    } else if ( transientawin != NULL ) {
        win->setTransientForAWindow( transientawin );
    }

    AContainer *ac1 = win->setContainer( new AContainer( win, 1, 2 ), true );
    ac1->setMinSpace( 0 );
    ac1->setMaxSpace( 0 );
    ac1->setBorderWidth( 5 );

    AContainerBB *ac1_texts = dynamic_cast<AContainerBB*>( ac1->add( new AContainerBB( win, 1, 1 ), 0, 0 ) );
    ac1_texts->setBorderWidth( 5 );
    win->addMultiLineText( text, *ac1_texts, 0, 0, NULL, NULL );

    AContainer *ac1_buttons = NULL;

    if ( max_buttons_per_row == 1 ) {
        ac1_buttons = ac1->add( new AContainer( win, 3, buttons.size() ), 0, 1 );
        ac1_buttons->setMinSpace( 0 );
        ac1_buttons->setMaxSpace( 0 );
    } else {
        ac1_buttons = ac1->add( new AContainer( win, max_buttons_per_row, buttons.size() ), 0, 1 );
        ac1_buttons->setMinSpace( 5 );
        ac1_buttons->setMaxSpace( -1 );
    }
    ac1_buttons->setBorderWidth( 0 );

    size_t row_nr = 0;
    bool focus_set = false;
    for ( auto &row : buttons ) {
        if ( max_buttons_per_row == 1 ) {
            if ( ! button_rows.at( row_nr ).at( 0 ).first.empty() ) {
                row.at( 0 ).first = ac1_buttons->addWidget( new Button( aguix, 0, 0, button_rows.at( row_nr ).at( 0 ).first.c_str(), 0 ),
                                                            1, row_nr, AContainer::CO_FIX );
                row.at( 0 ).first->setAcceptFocus( true );
                if ( ! focus_set ) {
                    row.at( 0 ).first->takeFocus();
                    focus_set = true;
                }

                row.at( 0 ).second = button_rows.at( row_nr ).at( 0 ).second;
                if ( row.at( 0 ).second < 0 ) row.at( 0 ).second = row_nr;
            }
        } else {
            size_t col_nr = 0;
            for ( auto &col : row ) {
                if ( col_nr >= button_rows.at( row_nr ).size() ) continue;

                if ( ! button_rows.at( row_nr ).at( col_nr ).first.empty() ) {
                    col.first = ac1_buttons->addWidget( new Button( aguix, 0, 0, button_rows.at( row_nr ).at( col_nr ).first.c_str(), row_nr * max_buttons_per_row + col_nr ),
                                                        col_nr, row_nr, AContainer::CO_FIX );
                    col.first->setAcceptFocus( true );
                    if ( ! focus_set ) {
                        col.first->takeFocus();
                        focus_set = true;
                    }

                    col.second = button_rows.at( row_nr ).at( col_nr ).second;
                    if ( col.second < 0 ) col.second = row_nr * max_buttons_per_row + col_nr;
                }

                col_nr++;
            }
        }

        row_nr++;
    }

    win->setDoTabCycling( true );
    win->contMaximize( true, true );
    win->useStippleBackground();
    win->centerScreen();
    win->show();
    AGMessage *msg;
    int endmode=-1;
    for(;endmode==-1;) {
        msg=aguix->WaitMessage(win);
        if(msg!=NULL) {
            switch(msg->type) {
                case AG_CLOSEWINDOW:
                    if ( msg->closewindow.window == win->getWindow() ) {
                        if ( ( flags & REQUEST_CANCELWITHLEFT ) != 0 ) {
                            endmode = get_end_mode( buttons, 0 );
                        } else {
                            endmode = get_end_mode( buttons, max_buttons_per_row - 1 );
                        }
                    }
                    break;
                case AG_BUTTONCLICKED: {
                    auto res = find_button( buttons, msg->button.button );
                    if ( res.first >= 0 ) {
                        endmode = buttons.at( res.first ).at( res.second ).second;
                        break;
                    }
                    break;
                }
                case AG_KEYPRESSED:
                    if ( win->isParent( msg->key.window, false ) == true ) {
                        switch(msg->key.key) {
                            case XK_KP_Enter:
                            case XK_Return: {
#ifdef ACCEPT_ENTER_FOR_CHOOSE
                                for ( size_t r = 0; r < buttons.size(); r++ ) {
                                    for ( size_t c = 0; c < (size_t)max_buttons_per_row; c++ ) {
                                        if ( c >= buttons[r].size() ) continue;
                                        if ( buttons[r][c].first == nullptr ) continue;
                                        if ( buttons[r][c].first->getHasFocus() == true ) {
                                            endmode = buttons[r][c].second;
                                            break;
                                        }
                                    }
                                }
#else
                                if ( buttons.size() > 0 &&
                                     buttons[0].size() > 0 &&
                                     buttons[0][0]->hasFocus() == true ) {
                                    endmode = buttons[0][0].second;
                                }
#endif
                            }
                                break;
                            case XK_Escape:
                                if ( ( flags & REQUEST_CANCELWITHLEFT ) != 0 ) {
                                    endmode = get_end_mode( buttons, 0 );
                                } else {
                                    endmode = get_end_mode( buttons, max_buttons_per_row - 1 );
                                }
                                break;
                            case XK_F1:
                                endmode = get_end_mode( buttons, 0 );
                                break;
                            case XK_F2:
                                endmode = get_end_mode( buttons, 1 );
                                break;
                            case XK_F3:
                                endmode = get_end_mode( buttons, 2 );
                                break;
                            case XK_F4:
                                endmode = get_end_mode( buttons, 3 );
                                break;
                            case XK_F5:
                                endmode = get_end_mode( buttons, 4 );
                                break;
                            case XK_F6:
                                endmode = get_end_mode( buttons, 5 );
                                break;
                            case XK_F7:
                                endmode = get_end_mode( buttons, 6 );
                                break;
                            case XK_F8:
                                endmode = get_end_mode( buttons, 7 );
                                break;
                            case XK_F9:
                                endmode = get_end_mode( buttons, 8 );
                                break;
                            case XK_F10:
                                endmode = get_end_mode( buttons, 9 );
                                break;
                            case XK_Left:
                                win->prevFocus();
                                break;
                            case XK_Right:
                                win->nextFocus();
                                break;
                        }
                    }
                    break;
            }
            aguix->ReplyMessage(msg);
        }
    }
    delete win;
    return endmode;
}

int Requester::string_request( const char *title, const char *text, const char *default_str, const char *buttons, char **return_str )
{
  return string_request( title, text, default_str, buttons, return_str, REQUEST_NONE );
}

int Requester::string_request( const char *title, const char *text, const char *default_str, const char *buttons, char **return_str, request_flags_t flags)
{
  return string_request( title, text, default_str, buttons, return_str, NULL, flags );
}

int Requester::string_request( const char *title, const char *text, const char *default_str, const char *buttons,char **return_str, const AWindow *twin, request_flags_t flags)
{
    const request_options options;

    return string_request( title, text, default_str,
                           buttons, return_str, twin, flags, options );
}

int Requester::string_request( const char *title, const char *text, const char *default_str, const char *buttons,char **return_str, const AWindow *twin, request_flags_t flags,
                               const struct request_options &options )
{
    return string_request( title, text, default_str,
                           buttons, return_str, twin, flags,
                           options,
                           "", nullptr );
}

int Requester::string_request( const char *title, const char *text, const char *default_str, const char *buttons,
                               char **return_str, const AWindow *twin, request_flags_t flags,
                               const struct request_options &options,
                               const std::string &help_text,
                               const std::function< std::string ( const std::string &current_input_before_cursor,
                                                                  const std::string &current_input_after_cursor ) > help_callback )
{
  Button **buts;
  int nbuttons;
  char **buttonss;
  StringGadget *sg;
  int i;
  
  nbuttons=createLines(buttons,&buttonss);

  if ( twin == NULL && transientawin == NULL ) {
    twin = aguix->getTransientWindow();
  }
  
  AWindow *win = new AWindow( aguix, 10, 10, 10, 10, title, AWindow::AWINDOW_DIALOG );
  win->create();
  if ( twin != NULL ) {
    win->setTransientForAWindow( twin );
  } else if ( transientawin != NULL ) {
    win->setTransientForAWindow( transientawin );
  }

  AContainer *ac1 = win->setContainer( new AContainer( win, 1, 4 + ( help_callback ? 1 : 0 ) ), true );
  ac1->setMinSpace( 0 );
  ac1->setMaxSpace( 0 );
  ac1->setBorderWidth( 5 );

  AContainerBB *ac1_texts = dynamic_cast<AContainerBB*>( ac1->add( new AContainerBB( win, 1, 1 ), 0, 0 ) );
  ac1_texts->setBorderWidth( 5 );
  win->addMultiLineText( text, *ac1_texts, 0, 0, NULL, NULL );

  sg = ac1->addWidget( new StringGadget( aguix, 0, 0, 100, default_str, 0 ),
                       0, 1, AContainer::CO_INCW );
  sg->setAcceptFocus( true );

  AContainerBB *ac1_help = nullptr;
  Text *help_text_w = nullptr;

  if ( help_callback ) {
      ac1_help = dynamic_cast<AContainerBB*>( ac1->add( new AContainerBB( win, 2, 1 ), 0, 2 ) );
      ac1_help->setBorderWidth( 5 );
      ac1_help->setMinSpace( 5 );
      ac1_help->setMaxSpace( 5 );
      ac1_help->addWidget( new Text( aguix, 0, 0, help_text.c_str(), 1, 0 ),
                           0, 0, AContainer::CO_FIX );
      help_text_w = ac1_help->addWidget( new Text( aguix, 0, 0, help_callback( "", "" ).c_str(), 1, 0 ),
                                       1, 0, AContainer::CO_INCW );
  }

  ac1->setMinHeight( 5, 0, 2 + ( help_callback ? 1 : 0 ) );

  AContainer *ac1_buttons = NULL;

  if ( nbuttons == 1 ) {
    ac1_buttons = ac1->add( new AContainer( win, 3, 1 ), 0, 3 + ( help_callback ? 1 : 0 ) );
    ac1_buttons->setMinSpace( 0 );
    ac1_buttons->setMaxSpace( 0 );
  } else {
    ac1_buttons = ac1->add( new AContainer( win, nbuttons, 1 ), 0, 3 + ( help_callback ? 1 : 0 ) );
    ac1_buttons->setMinSpace( 5 );
    ac1_buttons->setMaxSpace( -1 );
  }
  ac1_buttons->setBorderWidth( 0 );

  buts = (Button**)_allocsafe( nbuttons * sizeof( Button* ) );

  if ( nbuttons == 1 ) {
    buts[0] = ac1_buttons->addWidget( new Button( aguix, 0, 0, buttonss[0], 0 ),
                                      1, 0, AContainer::CO_FIX );
  } else {
    for ( i = 0; i < nbuttons; i++ ) {
      buts[i] = ac1_buttons->addWidget( new Button( aguix, 0, 0, buttonss[i], i ),
                                        i, 0, AContainer::CO_FIX );
    }
  }

  for ( i = 0; i < nbuttons; i++ ) {
    buts[i]->setAcceptFocus( true );
  }
 
  sg->setXOffset( 0 );
  if ( default_str != NULL ) {
    if ( flags == REQUEST_CURSORLEFT ) {
      sg->setCursor( 0 );
    } else if ( flags == REQUEST_CURSORRIGHT ) {
      sg->setCursor( (int)strlen( default_str ) );
    } else if ( flags == REQUEST_SELECTALL ) {
      sg->selectAll();
    }
  }

  if ( options.select_characters < 0 ) {
      sg->selectAll();
  } else if ( options.select_characters > 0 ) {
      sg->setSelection( 0, options.select_characters - 1, false );
  }

  sg->takeFocus();
  win->useStippleBackground();
  win->setDoTabCycling( true );
  win->contMaximize( true );
  win->centerScreen();
  win->show();
  AGMessage *msg;
  int endmode=-1;
  for(;endmode==-1;) {
    msg=aguix->WaitMessage(win);
    if(msg!=NULL) {
      switch(msg->type) {
        case AG_CLOSEWINDOW:
          if ( msg->closewindow.window == win->getWindow() ) {
	    if ( ( flags & REQUEST_CANCELWITHLEFT ) != 0 ) {
	      endmode = 0;
	    } else {
	      endmode = nbuttons - 1;
	    }
	  }
          break;
        case AG_BUTTONCLICKED:
	  for(i=0;i<nbuttons;i++) {
	    if(msg->button.button==buts[i]) {
	      endmode=i;
	      break;
	    }
	  }
          break;
	case AG_STRINGGADGET_OK:
	  if(msg->stringgadget.sg==sg) endmode=0;
	  break;
	case AG_STRINGGADGET_CANCEL:
	  if ( msg->stringgadget.sg == sg ) {
	    if ( ( flags & REQUEST_CANCELWITHLEFT ) != 0 ) {
	      endmode = 0;
	    } else {
	      endmode = nbuttons - 1;
	    }
	  }
	  break;
    case AG_STRINGGADGET_CONTENTCHANGE:
    case AG_STRINGGADGET_CURSORCHANGE:
        if ( help_callback ) {
            auto current_input = sg->getText();
            int cursor = sg->getCursor();
            std::string before( current_input, cursor );
            auto res = help_callback( before, "" );
            help_text_w->setText( res.c_str() );

            ac1_help->readLimits();
            win->contMaximize( true, false, true );
        }
        break;
	case AG_KEYPRESSED:
	  if ( win->isParent( msg->key.window, false ) == true ) {
	    // key release only accept when not first key we get
	    // means a key is pressed when we started and if we use this
	    // key it could confuse the user
	    switch(msg->key.key) {
	      case XK_KP_Enter:
	      case XK_Return:
#ifdef ACCEPT_ENTER_FOR_CHOOSE
		for ( i = 0; i < nbuttons; i++ ) {
		  if ( buts[i]->getHasFocus() == true ) {
		    endmode = i;
		    break;
		  }
		}
#else
		if ( buts[0]->hasFocus() == true ) endmode = 0;
#endif
		break;
	      case XK_Escape:
		if ( ( flags & REQUEST_CANCELWITHLEFT ) != 0 ) {
		  endmode = 0;
		} else {
		  endmode = nbuttons - 1;
		}
		break;
	      case XK_F1:
	        endmode=0;
	        break;
	      case XK_F2:
	        endmode=1;
		if(endmode>=nbuttons) endmode = -1;
	        break;
	      case XK_F3:
	        endmode=2;
		if(endmode>=nbuttons) endmode = -1;
	        break;
	      case XK_F4:
	        endmode=3;
		if(endmode>=nbuttons) endmode = -1;
	        break;
	      case XK_F5:
	        endmode=4;
		if(endmode>=nbuttons) endmode = -1;
	        break;
	      case XK_F6:
	        endmode=5;
		if(endmode>=nbuttons) endmode = -1;
	        break;
	      case XK_F7:
	        endmode=6;
		if(endmode>=nbuttons) endmode = -1;
	        break;
	      case XK_F8:
	        endmode=7;
		if(endmode>=nbuttons) endmode = -1;
	        break;
	      case XK_F9:
	        endmode=8;
		if(endmode>=nbuttons) endmode = -1;
	        break;
	      case XK_F10:
	        endmode=9;
		if(endmode>=nbuttons) endmode = -1;
	        break;
	      case XK_Left:
		win->prevFocus();
		break;
  	      case XK_Right:
		win->nextFocus();
		break;
	    }
	  }
	  break;
      }
      aguix->ReplyMessage(msg);
    }
  }
  if ( return_str != NULL ) {
    *return_str = dupstring( sg->getText() );
  }
  delete win;
  for(i=0;i<nbuttons;i++) _freesafe(buttonss[i]);
  _freesafe(buttonss);
  _freesafe(buts);
  return endmode;
}

int Requester::request_choose( const char *title, const char *text, const char *choose_text, bool &choose_var, const char *buttons )
{
  return request_choose( title, text, choose_text, choose_var, buttons, REQUEST_NONE );
}

int Requester::request_choose( const char *title, const char *text, const char *choose_text, bool &choose_var, const char *buttons, request_flags_t flags )
{
  return request_choose( title, text, choose_text, choose_var, buttons, NULL, flags );
}

int Requester::request_choose( const char *title,
			       const char *text,
			       const char *choose_text,
			       bool &choose_var,
			       const char *buttons,
			       const AWindow *twin,
			       request_flags_t flags )
{
  int i;
  Button **buts;
  int nbuttons;
  char **buttonss;
  int lines;
  char **liness;

  nbuttons = createLines( buttons, &buttonss );

  if ( twin == NULL && transientawin == NULL ) {
    twin = aguix->getTransientWindow();
  }
  
  AWindow *win = new AWindow( aguix, 10, 10, 10, 10, title, AWindow::AWINDOW_DIALOG );
  win->create();
  if ( twin != NULL ) {
    win->setTransientForAWindow( twin );
  } else if ( transientawin != NULL ) {
    win->setTransientForAWindow( transientawin );
  }

  AContainer *ac1 = win->setContainer( new AContainer( win, 1, 3 ), true );
  ac1->setMinSpace( 5 );
  ac1->setMaxSpace( 5 );

  lines = createLines( text, &liness );

  AContainerBB *ac1_1 = static_cast<AContainerBB*>( ac1->add( new AContainerBB( win, 1, lines ), 0, 0 ) );
  ac1_1->setMinSpace( 5 );
  ac1_1->setMaxSpace( 5 );

  for ( i = 0; i < lines; i++ ) {
    ac1_1->addWidget( new Text( aguix, 0, 0, liness[i] ),
		0, i, AContainer::CO_INCWNR );
  }
  
  for ( i = 0; i < lines; i++ ) _freesafe( liness[i] );
  _freesafe( liness );

  AContainer *ac1_2 = ac1->add( new AContainer( win, 3, 1 ), 0, 1 );
  ac1_2->setMinSpace( 5 );
  ac1_2->setMaxSpace( -1 );
  ac1_2->setBorderWidth( 0 );
  ChooseButton *cb1 = ac1_2->addWidget( new ChooseButton( aguix, 0,
                                                          0, choose_var,
                                                          choose_text, LABEL_RIGHT, 1 ),
                                        1, 0, AContainer::CO_FIXNR );
  AContainer *ac1_3 = ac1->add( new AContainer( win, nbuttons, 1 ), 0, 2 );
  ac1_3->setMinSpace( 5 );
  ac1_3->setMaxSpace( -1 );
  ac1_3->setBorderWidth( 0 );

  buts = (Button**)_allocsafe( nbuttons * sizeof( Button* ) );
  for ( i = 0; i < nbuttons; i++ ) {
    buts[i] = ac1_3->addWidget( new Button( aguix, 0, 0, buttonss[i], i ),
                                i, 0, AContainer::CO_FIX );
  }
  for ( i = 0; i < nbuttons; i++ ) {
    buts[i]->setAcceptFocus( true );
  }
  buts[0]->takeFocus();

  win->setDoTabCycling( true );
  win->contMaximize( true, true );

  win->useStippleBackground();
  win->centerScreen();
  win->show();

  AGMessage *msg;
  int endmode = -1;
  for( ; endmode == -1; ) {
    msg = aguix->WaitMessage( win );
    if ( msg != NULL ) {
      switch ( msg->type ) {
        case AG_CLOSEWINDOW:
          if ( msg->closewindow.window == win->getWindow() ) {
	    if ( ( flags & REQUEST_CANCELWITHLEFT ) != 0 ) {
	      endmode = 0;
	    } else {
	      endmode = nbuttons - 1;
	    }
	  }
          break;
        case AG_BUTTONCLICKED:
	  for ( i = 0; i < nbuttons; i++ ) {
	    if ( msg->button.button == buts[i] ) {
	      endmode = i;
	      break;
	    }
	  }
          break;
	case AG_KEYPRESSED:
	  if ( win->isParent( msg->key.window, false ) == true ) {
	    switch(msg->key.key) {
	      case XK_KP_Enter:
	      case XK_Return:
#ifdef ACCEPT_ENTER_FOR_CHOOSE
		for ( i = 0; i < nbuttons; i++ ) {
		  if ( buts[i]->getHasFocus() == true ) {
		    endmode = i;
		    break;
		  }
		}
#else
		if ( buts[0]->hasFocus() == true ) endmode = 0;
#endif
		break;
	      case XK_Escape:
		if ( ( flags & REQUEST_CANCELWITHLEFT ) != 0 ) {
		  endmode = 0;
		} else {
		  endmode = nbuttons - 1;
		}
		break;
	      case XK_F1:
	        endmode = 0;
	        break;
	      case XK_F2:
	        endmode = 1;
		if ( endmode >= nbuttons ) endmode = -1;
	        break;
	      case XK_F3:
	        endmode = 2;
		if ( endmode >= nbuttons ) endmode = -1;
	        break;
	      case XK_F4:
	        endmode = 3;
		if ( endmode >= nbuttons ) endmode = -1;
	        break;
	      case XK_F5:
	        endmode = 4;
		if ( endmode >= nbuttons ) endmode = -1;
	        break;
	      case XK_F6:
	        endmode = 5;
		if ( endmode >= nbuttons ) endmode = -1;
	        break;
	      case XK_F7:
	        endmode = 6;
		if ( endmode >= nbuttons ) endmode = -1;
	        break;
	      case XK_F8:
	        endmode = 7;
		if ( endmode >= nbuttons ) endmode = -1;
	        break;
	      case XK_F9:
	        endmode = 8;
		if ( endmode >= nbuttons ) endmode = -1;
	        break;
	      case XK_F10:
	        endmode = 9;
		if ( endmode >= nbuttons ) endmode = -1;
	        break;
	      case XK_Left:
		win->prevFocus();
		break;
  	      case XK_Right:
		win->nextFocus();
		break;
	    }
	  }
	  break;
      }
      aguix->ReplyMessage( msg );
    }
  }

  choose_var = cb1->getState();

  delete win;
  for ( i = 0; i < nbuttons; i++ ) _freesafe( buttonss[i] );
  _freesafe( buttonss );
  _freesafe( buts );
  return endmode;
}


int Requester::request( const char *title, std::shared_ptr< TextStorage > ts, const char *buttons )
{
  return request( title, ts, buttons, REQUEST_NONE );
}

int Requester::request( const char *title, std::shared_ptr< TextStorage > ts, const char *buttons, request_flags_t flags )
{
  return request( title, ts, buttons, NULL, flags );
}

int Requester::request( const char *title, std::shared_ptr< TextStorage > ts, const char *buttons, const AWindow *twin, request_flags_t flags )
{
  int i;
  Button **buts;
  int nbuttons;
  char **buttonss;

  nbuttons = createLines( buttons, &buttonss );

  if ( twin == NULL && transientawin == NULL ) {
    twin = aguix->getTransientWindow();
  }
  
  AWindow *win = new AWindow( aguix, 10, 10, 10, 10, title, AWindow::AWINDOW_DIALOG );
  win->create();
  if ( twin != NULL ) {
    win->setTransientForAWindow( twin );
  } else if ( transientawin != NULL ) {
    win->setTransientForAWindow( transientawin );
  }

  AContainer *ac1 = win->setContainer( new AContainer( win, 1, 2 ), true );
  ac1->setMinSpace( 5 );
  ac1->setMaxSpace( 5 );

  TextView *tv = new TextView( aguix, 0, 0, 100, 100, "", ts );
  tv->setDisplayFocus( true );
  ac1->addWidget( tv, 0, 0, AContainer::CO_MIN );
  tv->create();
  tv->show();

  int rw;

  aguix->getLargestDimensionOfCurrentScreen( NULL, NULL, &rw, NULL );

  tv->maximizeX( rw * 60 / 100 );
  tv->setLineWrap( true );
  tv->maximizeYLines( 15 );
  ac1->readLimits();

  if ( nbuttons == 1 ) {
    AContainer *ac1_3 = ac1->add( new AContainer( win, 3, 1 ), 0, 1 );
    ac1_3->setMinSpace( 0 );
    ac1_3->setMaxSpace( -1 );
    ac1_3->setBorderWidth( 0 );
    
    buts = (Button**)_allocsafe( nbuttons * sizeof( Button* ) );
    buts[0] = ac1_3->addWidget( new Button( aguix, 0, 0, buttonss[0], 0 ),
                                1, 0, AContainer::CO_FIX );
  } else {
    AContainer *ac1_3 = ac1->add( new AContainer( win, nbuttons, 1 ), 0, 1 );
    ac1_3->setMinSpace( 5 );
    ac1_3->setMaxSpace( -1 );
    ac1_3->setBorderWidth( 0 );
    
    buts = (Button**)_allocsafe( nbuttons * sizeof( Button* ) );
    for ( i = 0; i < nbuttons; i++ ) {
      buts[i] = ac1_3->addWidget( new Button( aguix, 0, 0, buttonss[i], i ),
                                  i, 0, AContainer::CO_FIX );
    }
  }
  for ( i = 0; i < nbuttons; i++ ) {
    buts[i]->setAcceptFocus( true );
  }
  //  buts[0]->takeFocus();
  win->applyFocus( tv );

  win->setDoTabCycling( true );
  win->contMaximize( true );

  win->useStippleBackground();
  win->centerScreen();
  win->show();

  AGMessage *msg;
  int endmode = -1;
  for( ; endmode == -1; ) {
    msg = aguix->WaitMessage( win );
    if ( msg != NULL ) {
      switch ( msg->type ) {
        case AG_CLOSEWINDOW:
          if ( msg->closewindow.window == win->getWindow() ) {
	    if ( ( flags & REQUEST_CANCELWITHLEFT ) != 0 ) {
	      endmode = 0;
	    } else {
	      endmode = nbuttons - 1;
	    }
	  }
          break;
        case AG_BUTTONCLICKED:
	  for ( i = 0; i < nbuttons; i++ ) {
	    if ( msg->button.button == buts[i] ) {
	      endmode = i;
	      break;
	    }
	  }
          break;
	case AG_KEYPRESSED:
	  if ( win->isParent( msg->key.window, false ) == true ) {
	    switch(msg->key.key) {
	      case XK_KP_Enter:
	      case XK_Return:
#ifdef ACCEPT_ENTER_FOR_CHOOSE
		for ( i = 0; i < nbuttons; i++ ) {
		  if ( buts[i]->getHasFocus() == true ) {
		    endmode = i;
		    break;
		  }
		}
#else
		if ( buts[0]->hasFocus() == true ) endmode = 0;
#endif
		break;
	      case XK_Escape:
		if ( ( flags & REQUEST_CANCELWITHLEFT ) != 0 ) {
		  endmode = 0;
		} else {
		  endmode = nbuttons - 1;
		}
		break;
	      case XK_F1:
	        endmode = 0;
	        break;
	      case XK_F2:
	        endmode = 1;
		if ( endmode >= nbuttons ) endmode = -1;
	        break;
	      case XK_F3:
	        endmode = 2;
		if ( endmode >= nbuttons ) endmode = -1;
	        break;
	      case XK_F4:
	        endmode = 3;
		if ( endmode >= nbuttons ) endmode = -1;
	        break;
	      case XK_F5:
	        endmode = 4;
		if ( endmode >= nbuttons ) endmode = -1;
	        break;
	      case XK_F6:
	        endmode = 5;
		if ( endmode >= nbuttons ) endmode = -1;
	        break;
	      case XK_F7:
	        endmode = 6;
		if ( endmode >= nbuttons ) endmode = -1;
	        break;
	      case XK_F8:
	        endmode = 7;
		if ( endmode >= nbuttons ) endmode = -1;
	        break;
	      case XK_F9:
	        endmode = 8;
		if ( endmode >= nbuttons ) endmode = -1;
	        break;
	      case XK_F10:
	        endmode = 9;
		if ( endmode >= nbuttons ) endmode = -1;
	        break;
	    }
	  }
	  break;
      }
      aguix->ReplyMessage( msg );
    }
  }

  delete win;
  for ( i = 0; i < nbuttons; i++ ) _freesafe( buttonss[i] );
  _freesafe( buttonss );
  _freesafe( buts );
  return endmode;
}

int Requester::string_request_choose( const char *title,
                                      const char *text,
                                      const char *default_str,
                                      const char *choose_text,
                                      bool &choose_var,
                                      const char *buttons,
                                      char **return_str )
{
    return string_request_choose( title, text, default_str, choose_text,
                                  choose_var, buttons, return_str,
                                  REQUEST_NONE);
}

int Requester::string_request_choose( const char *title,
                                      const char *text,
                                      const char *default_str,
                                      const char *choose_text,
                                      bool &choose_var,
                                      const char *buttons,
                                      char **return_str,
                                      request_flags_t flags )
{
    return string_request_choose( title, text, default_str, choose_text,
                                  choose_var, buttons, return_str, NULL, flags );
}

int Requester::string_request_choose( const char *title,
                                      const char *text,
                                      const char *default_str,
                                      const char *choose_text,
                                      bool &choose_var,
                                      const char *buttons,
                                      char **return_str,
                                      const AWindow *twin,
                                      request_flags_t flags )
{
    Button **buts;
    int nbuttons;
    char **buttonss;
    StringGadget *sg;
    int i;
  
    nbuttons=createLines(buttons,&buttonss);

    if ( twin == NULL && transientawin == NULL ) {
        twin = aguix->getTransientWindow();
    }
  
    AWindow *win = new AWindow( aguix, 10, 10, 10, 10, title, AWindow::AWINDOW_DIALOG );
    win->create();
    if ( twin != NULL ) {
        win->setTransientForAWindow( twin );
    } else if ( transientawin != NULL ) {
        win->setTransientForAWindow( transientawin );
    }

    AContainer *ac1 = win->setContainer( new AContainer( win, 1, 5 ), true );
    ac1->setMinSpace( 0 );
    ac1->setMaxSpace( 0 );
    ac1->setBorderWidth( 5 );

    AContainerBB *ac1_texts = dynamic_cast<AContainerBB*>( ac1->add( new AContainerBB( win, 1, 1 ), 0, 0 ) );
    ac1_texts->setBorderWidth( 5 );
    win->addMultiLineText( text, *ac1_texts, 0, 0, NULL, NULL );

    sg = ac1->addWidget( new StringGadget( aguix, 0, 0, 100, default_str, 0 ),
                         0, 1, AContainer::CO_INCW );
    sg->setAcceptFocus( true );

    ac1->setMinHeight( 5, 0, 2 );

    AContainer *ac1_2 = ac1->add( new AContainer( win, 1, 1 ), 0, 2 );
    ac1_2->setMinSpace( 5 );
    ac1_2->setMaxSpace( -1 );
    ac1_2->setBorderWidth( 5 );

    ChooseButton *cb1 = ac1_2->addWidget( new ChooseButton( aguix, 0,
                                                            0, choose_var,
                                                            choose_text, LABEL_RIGHT, 1 ),
                                          0, 0, AContainer::CO_INCWNR );

    AContainer *ac1_buttons = NULL;

    if ( nbuttons == 1 ) {
        ac1_buttons = ac1->add( new AContainer( win, 3, 1 ), 0, 4 );
        ac1_buttons->setMinSpace( 0 );
        ac1_buttons->setMaxSpace( 0 );
    } else {
        ac1_buttons = ac1->add( new AContainer( win, nbuttons, 1 ), 0, 4 );
        ac1_buttons->setMinSpace( 5 );
        ac1_buttons->setMaxSpace( -1 );
    }
    ac1_buttons->setBorderWidth( 0 );

    buts = (Button**)_allocsafe( nbuttons * sizeof( Button* ) );

    if ( nbuttons == 1 ) {
        buts[0] = ac1_buttons->addWidget( new Button( aguix, 0, 0, buttonss[0], 0 ),
                                          1, 0, AContainer::CO_FIX );
    } else {
        for ( i = 0; i < nbuttons; i++ ) {
            buts[i] = ac1_buttons->addWidget( new Button( aguix, 0, 0, buttonss[i], i ),
                                              i, 0, AContainer::CO_FIX );
        }
    }

    for ( i = 0; i < nbuttons; i++ ) {
        buts[i]->setAcceptFocus( true );
    }
 
    sg->setXOffset( 0 );
    if ( default_str != NULL ) {
        if ( flags == REQUEST_CURSORLEFT ) {
            sg->setCursor( 0 );
        } else if ( flags == REQUEST_CURSORRIGHT ) {
            sg->setCursor( (int)strlen( default_str ) );
        } else if ( flags == REQUEST_SELECTALL ) {
            sg->selectAll();
        }
    }

    sg->takeFocus();
    win->useStippleBackground();
    win->setDoTabCycling( true );
    win->contMaximize( true );
    win->centerScreen();
    win->show();
    AGMessage *msg;
    int endmode=-1;
    for(;endmode==-1;) {
        msg=aguix->WaitMessage(win);
        if(msg!=NULL) {
            switch(msg->type) {
                case AG_CLOSEWINDOW:
                    if ( msg->closewindow.window == win->getWindow() ) {
                        if ( ( flags & REQUEST_CANCELWITHLEFT ) != 0 ) {
                            endmode = 0;
                        } else {
                            endmode = nbuttons - 1;
                        }
                    }
                    break;
                case AG_BUTTONCLICKED:
                    for(i=0;i<nbuttons;i++) {
                        if(msg->button.button==buts[i]) {
                            endmode=i;
                            break;
                        }
                    }
                    break;
                case AG_STRINGGADGET_OK:
                    if(msg->stringgadget.sg==sg) endmode=0;
                    break;
                case AG_STRINGGADGET_CANCEL:
                    if ( msg->stringgadget.sg == sg ) {
                        if ( ( flags & REQUEST_CANCELWITHLEFT ) != 0 ) {
                            endmode = 0;
                        } else {
                            endmode = nbuttons - 1;
                        }
                    }
                    break;
                case AG_KEYPRESSED:
                    if ( win->isParent( msg->key.window, false ) == true ) {
                        // key release only accept when not first key we get
                        // means a key is pressed when we started and if we use this
                        // key it could confuse the user
                        switch(msg->key.key) {
                            case XK_KP_Enter:
                            case XK_Return:
#ifdef ACCEPT_ENTER_FOR_CHOOSE
                                for ( i = 0; i < nbuttons; i++ ) {
                                    if ( buts[i]->getHasFocus() == true ) {
                                        endmode = i;
                                        break;
                                    }
                                }
#else
                                if ( buts[0]->hasFocus() == true ) endmode = 0;
#endif
                                break;
                            case XK_Escape:
                                if ( ( flags & REQUEST_CANCELWITHLEFT ) != 0 ) {
                                    endmode = 0;
                                } else {
                                    endmode = nbuttons - 1;
                                }
                                break;
                            case XK_F1:
                                endmode=0;
                                break;
                            case XK_F2:
                                endmode=1;
                                if(endmode>=nbuttons) endmode = -1;
                                break;
                            case XK_F3:
                                endmode=2;
                                if(endmode>=nbuttons) endmode = -1;
                                break;
                            case XK_F4:
                                endmode=3;
                                if(endmode>=nbuttons) endmode = -1;
                                break;
                            case XK_F5:
                                endmode=4;
                                if(endmode>=nbuttons) endmode = -1;
                                break;
                            case XK_F6:
                                endmode=5;
                                if(endmode>=nbuttons) endmode = -1;
                                break;
                            case XK_F7:
                                endmode=6;
                                if(endmode>=nbuttons) endmode = -1;
                                break;
                            case XK_F8:
                                endmode=7;
                                if(endmode>=nbuttons) endmode = -1;
                                break;
                            case XK_F9:
                                endmode=8;
                                if(endmode>=nbuttons) endmode = -1;
                                break;
                            case XK_F10:
                                endmode=9;
                                if(endmode>=nbuttons) endmode = -1;
                                break;
                            case XK_Left:
                                win->prevFocus();
                                break;
                            case XK_Right:
                                win->nextFocus();
                                break;
                        }
                    }
                    break;
            }
            aguix->ReplyMessage(msg);
        }
    }
    if ( return_str != NULL ) {
        *return_str = dupstring( sg->getText() );
    }

    choose_var = cb1->getState();

    delete win;
    for(i=0;i<nbuttons;i++) _freesafe(buttonss[i]);
    _freesafe(buttonss);
    _freesafe(buts);
    return endmode;
}

