/* fieldlistview.h
 * This file belongs to Worker, a file manager for UN*X/X11.
 * Copyright (C) 2002-2025 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 NEWLISTVIEW_H
#define NEWLISTVIEW_H

#include "aguixdefs.h"
#include "util.h"
#include "guielement.h"
#include "fieldlvrowdata.h"
#include <string>
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/X.h>
#include <set>
#include <functional>
#include "pan_scrolling.hh"

class AGUIX;
class AGUIXFont;

class FieldListView:public GUIElement
{
public:
  FieldListView( AGUIX *caguix, int cx, int cy, int cwidth, int cheight, int cdata );
  virtual ~FieldListView();
  FieldListView( const FieldListView &other );
  FieldListView &operator=( const FieldListView &other );

  void setNrOfFields( int nv );
  int getNrOfFields() const;
  void setText( int row, int field, std::string text, bool update_simple_set = false );
  std::string getText( int row, int field ) const;
  void trimElements();
  void setFieldWidth( int field, int width );
  void setFieldWidthQ( int field, int width );
  void setSize( int rows ); // create/delete needed rows
  int getUsedWidth( int field );
  int addRow();
  int deleteRow( int row );
  int insertRow( int row );
  void swapRows( int row1, int row2 );
  void setStrikeOut( int row, int field, bool nv );
  bool getStrikeOut( int row, int field ) const;
  void setStrikeOutStart( int row, int field, size_t nv );
  size_t getStrikeOutStart( int row, int field ) const;

  void setXOffset( int nv, bool no_redraw = false );
  int getMaxTextWidth();
  void setYOffset( int nv, bool no_redraw = false );

  typedef enum { ALIGN_LEFT, ALIGN_RIGHT } align_t;

  void setFieldAlign( int field, align_t nv );
  void setFieldAlignQ( int field, align_t nv );

  void setFieldTextQ( int field, const std::string nv );
  void setFieldText( int field, const std::string nv );
  std::string getFieldText( int field ) const;

  // true to merge with following field text
  void setFieldTextMergedQ( int field, bool nv );
  void setFieldTextMerged( int field, bool nv );
  void setFieldTextResizeable( int field, bool nv );

  virtual void redraw();
  void redraw( int element );
  typedef enum { CC_NORMAL = 0, CC_SELECT = 1, CC_ACTIVE = 2, CC_SELACT = 3 } colorclass_t;
  void setFG( int row, colorclass_t cc, int col );
  int getFG( int row, colorclass_t cc ) const;
  void setBG( int row, colorclass_t cc, int col );
  int getBG( int row, colorclass_t cc ) const;
  void setSelect( int row, bool nv );
  void setSelectQ( int row, bool nv );
  bool getSelect( int row ) const;
  void setActiveRow( int nv );
  void setActiveRowQ( int nv );
  int getActiveRow() const;

  void setVisMark( int row, bool nv );
  void setVisMarkQ( int row, bool nv );
  bool getVisMark( int row ) const;

  virtual void resize( int w, int h );
  int getData() const;
  void setData( int );
  virtual void flush();
  virtual bool handleMessage( XEvent *E, Message *msg );
  int getXOffset() const;
  int getYOffset() const;
  int getElements() const;
  int getMaxDisplayV() const;
  void setHBarState( int state );
  void setVBarState( int state );
  int getHBarState() const;
  int getVBarState() const;
  void setVBarWidth( int new_width );
  int getVBarWidth() const;
  void setHBarHeight( int new_height );
  int getHBarHeight() const;
  void scrollV( int delta, bool no_redraw = false );
  void scrollV1( int dir, bool no_redraw = false );
  void setSelectHandler( void (*nsh)( FieldListView*, int element ) );
  void setSelectHandler( std::function< void( FieldListView*, int element ) > cb );
  void showActive();
  void centerActive();
  int setFont ( const char * );
  virtual const char *getType() const;
  virtual bool isType( const char *type ) const;
  virtual bool isParent( Window ) const;
  int getRowHeight() const;
  int getMBG() const;
  void setMBG( int color );
  
  int maximizeX();
  int getMaximumWidth();
  int maximizeY();
  int getMaximumHeight();

  void showRow( int row, bool redraw = true);
  
  void setData( int row, int ndata );
  int getData( int row, int fallback_value ) const;
  void setDataExt( int row, FieldLVRowData *ndata );
  FieldLVRowData *getDataExt( int row ) const;
  
  bool isValidRow( int row ) const;

  typedef enum { PRECOLOR_NONE, PRECOLOR_NORMAL, PRECOLOR_ONLYSELECT, PRECOLOR_ONLYACTIVE, PRECOLOR_NOTSELORACT } precolor_t;
  void setPreColors( int row, precolor_t mode );
  void setDisplayFocus( bool nv );
  bool getDisplayFocus() const;

  void cond_redraw();
  bool isRowVisible( int row ) const;

  void setShowHeader( bool nv );
  bool getShowHeader() const;
  int getHeaderHeight() const;
  void redrawHeader();
  void setHeaderFG( int col );
  void setHeaderBG( int col );
  int getHeaderFG() const;
  int getHeaderBG() const;
  int getHScrollStep() const;

  int getFieldWidth( int field ) const;
  int getEffectiveFieldWidth( int field );

  typedef enum {
    SELECT_BUTTON,
    ACTIVATE_BUTTON,
    SCROLL_BUTTON,
    ENTRY_PRESSED_MSG_BUTTON
  } buttonfunction_t;
  static int changeButtonFunction( buttonfunction_t bf, int button, int mod = 0 );

  typedef enum { MOUSE_SELECT_NORMAL, MOUSE_SELECT_ALT } mouse_select_t;
  static int changeSelectMode( mouse_select_t nm );
    static int changeHScrollMode( bool nv );
    static void changeDelayedEntryPressedEvent( bool nv );
  
  class ColorDef
  {
  public:
    ColorDef();
    ~ColorDef();

    void setFG( colorclass_t cc, int col );
    int getFG( colorclass_t cc ) const;
    void setBG( colorclass_t cc, int col );
    int getBG( colorclass_t cc ) const;
  private:
    int fg[4], bg[4];
  };
  void setColor( int row, const ColorDef &col );
  ColorDef getColor( int row ) const;

  void simpleRedrawUpdatedRows();
  void timeLimitedRedraw();

  void setGlobalFieldSpace( int spacing );
  int getFieldSpace( int field ) const;
  void setFieldSpace( int field, int spacing );

  void setHighlightSegments( int row, int field, const std::vector< size_t > &segments );
  const std::vector< size_t > *getHighlightSegments( int row, int field ) const;
  void clearHighlightSegments();

  void setConsiderHeaderWidthForDynamicWidth( bool nv );
  bool getConsiderHeaderWidthForDynamicWidth() const;

  void setDefaultColorMode( precolor_t mode );

  struct on_demand_data {
      bool done = false;  // indicates that the caller is done with ondemand data

      std::string text; // the text for the given row x column
      bool text_set = false;

      std::vector< size_t > segments; // highlight segments
      bool segments_set = false;

      bool strike_out = false;
      bool strike_out_set = false;

      size_t strike_out_start = 0;
      bool strike_out_start_set = false;
  };
  
  void setOnDemandCallback( std::function< void( FieldListView*,
                                                 int row,
                                                 int field,
                                                 struct on_demand_data &data ) > cb );

  void clearOnDemandDataAvailableFlag();
  void clearOnDemandDataAvailableFlag( int row );

  typedef enum {
      HEADER_SORT_HINT_NONE,
      HEADER_SORT_HINT_UP,
      HEADER_SORT_HINT_DOWN
  } header_sort_hint_t;

  void setSortHint( int field, header_sort_hint_t hint );
  void unsetSortHint();

    int getRowYPosition( int row );

    void setShowSelectionInVBar( bool nv );

    void setHideEmptyField( int field, bool v );
protected:
  int fields;
  int *fieldwidth;
  int *used_width;
  int *maxrow;

  align_t *fieldalign;
  std::string *fieldtext;
  bool *fieldtextmerged;
  bool *fieldtextResizeable;

  std::vector< int > m_header_text_width;
  std::vector< int > m_field_spacing;
    std::vector< bool > m_hide_empty_field;

  int fieldtextclicked;

  class Content
  {
  public:
    Content();
    ~Content();
    Content( const Content &other );
    Content &operator=( const Content &other );
    void setText( int field, std::string text );
    void trim( int newfields );
    std::string getText( int field ) const;
    void setFG( colorclass_t cc, int col );
    int getFG( colorclass_t cc ) const;
    void setBG( colorclass_t cc, int col );
    int getBG( colorclass_t cc ) const;
    void setSelect( bool nv );
    bool getSelect() const;
    void setDataExt( FieldLVRowData *ndata );
    FieldLVRowData *getDataExt() const;
    void setData( int ndata );
    int getData() const;
    void setMark( bool nv );
    bool getMark() const;

    void setStrikeOut( int field, bool nv );
    bool getStrikeOut( int field ) const;

    void setStrikeOutStart( int field, size_t nv );
    size_t getStrikeOutStart( int field ) const;

    void setHighlightSegments( int field, const std::vector< size_t > &segments );
    const std::vector< size_t > *getHighlightSegments( int field ) const;
    void clearHighlightSegments();

    bool getOnDemandDataAvailable() const
    {
        return m_ondemand_data_available;
    }

    void setOnDemandDataAvailable( bool nv )
    {
        m_ondemand_data_available = nv;
    }
  protected:
    int cur_fields;
    std::string **texts;
    std::vector< bool > m_strike_out;
    std::vector< size_t > m_strike_out_start;
    std::vector< std::vector< size_t > > *m_highlight_segments;

    int fg[4], bg[4];
    bool select;
    int data;
    FieldLVRowData *dataExt;
    bool mark;

    bool m_ondemand_data_available;
  };
  Content **elementarray;
  int arraysize;
  int elements;

  void increaseArray( int newsize );
  void checkShrink();

  int xoffset, yoffset, maxdisplayv;

  void redrawContent();
  void redrawLine( int element );
  int activerow;

  int data;
  static const char *type;
  int hbar;
  int hbar_height;
  int hbar_dir;
  bool hbar_pressed;
  int vbar;
  int vbar_width;
  int vbar_dir;
  bool vbar_pressed;
  void mredraw();
  void hbarredraw();
  void vbarredraw();
  Window hbarwin,vbarwin;
  void clearNonElements();
  void handleExpose( Window msgwin, int ex, int ey, int ew, int eh );
  virtual void handleSelect( Message *msg );
  virtual void handleMiddle( Message *msg );
  void handleThird( Message *msg );
  void handleHBar( Message *msg );
  void handleVBar( Message *msg );
  bool handleKeys( Message *msg );
  void handleHeader( Message *msg );
  void handleEntryPressed( Message *msg );

  void (*selecthandler)( FieldListView*,int element );
  std::function< void( FieldListView*, int element ) > m_select_handler;
  void setupBars();
  virtual void runSelectHandler( int element );
  void recalcMaxValues();

  int getHeaderTextWidth( int field );
  
  AGUIXFont *font;
  struct timeval lastwheel;
  int mbg;

  int elementHeight, elementWidth, ox, oy;
  void resetWin( bool doRedraw );
  int getRowForMouseY( int my ) const;

  Pixmap buffer;
  void drawBuffer();
  void drawBuffer( int dx, int dy, int dw, int dh );
  void drawBuffer( int element );

  bool displayFocus;
  int getInnerWidth() const;
  int getInnerHeight() const;

  virtual void doCreateStuff();
  virtual void doDestroyStuff();

  void sendScrollEvent( int type );

  bool cond_changed;
  bool showHeader;
  int headerCols[2];

  int getFieldForPoint( int mx, int my, int *start_x = NULL, int *end_x = NULL );

  typedef enum { VBAR_IDLE, VBAR_SCROLL, VBAR_SCROLL_UP, VBAR_SCROLL_DOWN } vbarMode_t;
  vbarMode_t vbarMode;
  int grabs;
  int scrollDelta;
  typedef enum { HBAR_IDLE, HBAR_SCROLL, HBAR_SCROLL_LEFT, HBAR_SCROLL_RIGHT } hbarMode_t;
  hbarMode_t hbarMode;
  typedef enum { THIRD_IDLE, THIRD_SCROLL } thirdMode_t;
  thirdMode_t thirdMode;
  int scrollXDir, scrollYDir;
  void updateScrollValues( int mx, int my );
  int selectRowsRange( int startRow, int endRow, bool state );
  int singleSelectRow( int row, bool state );

  typedef enum { SELECT_IDLE, SELECT_HOLD, SELECT_SCROLL_UP, SELECT_SCROLL_DOWN, SELECT_IGNORE } selectMode_t;
  selectMode_t selectMode;
  int clickLastSelectedRow, clickFirstSelectedRow;
  bool clickState;
  int clickSelected;
  typedef enum { MIDDLE_IDLE, MIDDLE_HOLD, MIDDLE_SCROLL_UP, MIDDLE_SCROLL_DOWN, MIDDLE_IGNORE } middleMode_t;
  middleMode_t middleMode;

  int _last_single_select_row;
  Time _last_single_select_time;
  int _last_single_middle_row;
  Time _last_single_middle_time;

  typedef enum { HEADER_IDLE, HEADER_CLICK, HEADER_DRAG } headerMode_t;
  headerMode_t headerMode;

  void selectModeIgnore();
  void middleModeIgnore();

  void prepareBG( bool force = false );

  Pixmap _header_pixmap;
  int _header_pixmap_w, _header_pixmap_h;
  void freeHeaderPixmap();
  int checkHeaderPixmap( int pw, int ph );
  int getBorderWidth() const;
  int drag_base_x;
  int drag_base_w;
  Time last_drag_time;
  int last_drag_clicked;
  int drag_field;
  bool header_cursor_set;  // true if cursor for header dragging is set

  int findPreviousRealField( int f );
  int getLastField( int *start_x, int *end_x );
  int findResizeableField( int f );

  static unsigned int _select_button, _select_buttonmask;
  static unsigned int _activate_button, _activate_buttonmask, _activate_mod;
  static unsigned int _scroll_button, _scroll_buttonmask, _scroll_mod;
  static unsigned int _entry_pressed_msg_button, _entry_pressed_msg_buttonmask, _entry_pressed_msg_mod;
  static mouse_select_t _select_mode;
    static unsigned int s_hscroll_left_button;
    static unsigned int s_hscroll_right_button;
    static bool s_enable_delayed_entry_pressed_event;

  unsigned int getCurrentMouseButtonState( Window twin );
  
  bool checkForValidButtonMod( unsigned int button,
                               unsigned int keystate,
                               unsigned int matching_mod );

  void initStaticDefaultColors();

  std::set<int> m_updated_rows;
  bool m_complete_update;
  struct timeval m_last_update_time;

  int m_global_field_spacing;

  bool m_consider_header_for_dynamic_width;

  precolor_t m_default_color_mode;

  static ColorDef s_default_element_colors;

  std::function< void( FieldListView*,
                       int row,
                       int field,
                       struct on_demand_data &data ) > m_ondemand_callback;

  void updateOnDemandData();

  struct header_sort_hint {
      header_sort_hint() : field( -1 ),
                           hint( HEADER_SORT_HINT_NONE )
      {}
      int field;
      header_sort_hint_t hint;
  } sort_hint;

    Pixmap m_vbar_selectbuffer = 0;
    int m_vbar_selectbuffer_w = -1;
    int m_vbar_selectbuffer_h = -1;
    std::vector< bool > m_vbar_select_dirty;
    std::vector< int > m_vbar_select_count;
    bool m_vbar_select_some_dirty = false;
    bool m_show_selection_in_vbar = false;
    bool m_vbar_rebuild_required = false;

    int m_header_border_leftright = 3;
    int m_header_border_topbottom = 2;

    pan_info m_pan_info;

    Time m_middle_first_timer_event = 0;
    bool m_middle_delayed_entry_pressed_disabled = false;

    void drawSelectStateVBar();
    void freeVBarSelectBuffer();
    int checkVBarSelectBuffer( int tw, int th );
    void updateSelectState( int element, bool only_selected );
};

#endif
