/* popupmenu.hh
 * This file belongs to Worker, a file manager for UN*X/X11.
 * Copyright (C) 2007-2023 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
 */

#ifndef POPUPMENU_HH
#define POPUPMENU_HH

#include "aguix.h"
#include "popupwindow.hh"
#include "bevelbox.h"

#include <list>
#include <vector>
#include <string>

class PopUpMenu : private PopUpWindow {
public:
    typedef enum { HEADER, NORMAL, SUBMENU, HLINE, GREYED_OUT, EDITABLE } entry_type_t;

    class PopUpEntry {
    public:
        PopUpEntry();
        
        std::string name;
        std::string help;
        std::function< void( const std::string &help ) > help_cb;
        entry_type_t type;
        int id;
        std::list<PopUpEntry> submenu;
        int fg, bg;

        bool highlight_on_show;
    };

    PopUpMenu( AGUIX *parent, const std::list<PopUpEntry> &entries, int group_id = -1 );
    virtual ~PopUpMenu();
    PopUpMenu( const PopUpMenu &other );
    PopUpMenu &operator=( const PopUpMenu &other );

    void show();

    typedef enum {
        POPUP_UNDER_MOUSE,
        POPUP_IN_POSITION,
        POPUP_CENTER_IN_POSITION,
        POPUP_CENTER_FIRST_AROUND_POS
    } popup_show_t;
    void show( int x, int y, popup_show_t show_type );
    void hide();
    int create();
    void redraw();
    bool handleMessage( XEvent *,Message *msg );

    int getBorderWidth() const;

    using PopUpWindow::getWidth;
    using PopUpWindow::getHeight;
    using PopUpWindow::setXFocus;
    void checkAndGrabKeyboard();

    int getHighlightEntry() const;
    bool getKeyboardFocus() const;

    void highlightOnShowEntry( int default_entry = -1 );

    void setHelpCB( std::function< void( const std::string &help ) > help_cb );

    void setFocusOutIgnoreTime( uint64_t ms );
protected:
    void setParentMenu( PopUpMenu *parent );
    void setKeyboardFocus( bool nv );
    void setHighlightEntry( int entry, int forward_direction = 1 );
    void sendMessage( const PopUpMenu *submenu, const std::list<int> &entries, std::unique_ptr<AGMessage> agmsg );

    typedef enum {
        BOTTOM_UP_RUN,
        TOP_DOWN_RUN
    } change_focus_run_t;
    void changeFocusTo( PopUpMenu *owner, change_focus_run_t mode = BOTTOM_UP_RUN );
private:
    class InternPopUpEntry {
    public:
        InternPopUpEntry();
        ~InternPopUpEntry();

        std::string name;
        std::string help;
        entry_type_t type;
        int id;
        PopUpMenu *submenu;
        
        int y, height;
        int fg, bg;

        std::string editable_content;

        bool highlight_on_show;
    };

    void showEntry( int entry );
    void enableTimer();
    void disableTimer();
    
    void activateEntry( int entry );

    void redrawEntry( int entry, int top_y );
    int getEntryHeight( int entry );
    void calcEntryDimensions();
    int getEntryForYPos( int y );
    
    void setDimensions();
    int forwardToVisibleEntry( int pos, int direction );
    void updateInfixFilter();
    bool isVisibleEntry( int pos ) const;
    void addStringToFilter( const char *str );
    void removeLastInfixChar();
    int checkForVisibleEntries( const std::string &filter, bool prefer_prefix );
    void sendClickMessage( int entry );
    void sendEditedMessage( int entry );
    
    std::vector<InternPopUpEntry> m_entries;
    int m_highlighted_entry;
    int m_counter;
    int m_last_motion_x, m_last_motion_y;
    bool m_ignore_non_vertical_motion;
    bool m_open_after_timeout;

    bool m_keyboard_focus;
    
    PopUpMenu *m_parent_menu;

    int m_grabbed_keyboard;
    int m_grabbed_pointer;

    int m_border_width;

    bool m_ignore_button_release;

    std::string m_infix_filter;

    int m_edit_entry;

    std::function< void( const std::string &help ) > m_help_cb;

    uint64_t m_focus_out_ignore_time_ms = 0;
    uint64_t m_show_time_ms = 0;
};

#endif
