/********************************************************************************
*                                                                               *
*                      G r a d i e n t B a r   W i d g e t                      *
*                                                                               *
*********************************************************************************
* Copyright (C) 2002,2025 by Jeroen van der Zijp.   All Rights Reserved.        *
*********************************************************************************
* This library is free software; you can redistribute it and/or modify          *
* it under the terms of the GNU Lesser General Public License as published by   *
* the Free Software Foundation; either version 3 of the License, or             *
* (at your option) any later version.                                           *
*                                                                               *
* This library 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 Lesser General Public License for more details.                           *
*                                                                               *
* You should have received a copy of the GNU Lesser General Public License      *
* along with this program.  If not, see <http://www.gnu.org/licenses/>          *
********************************************************************************/
#ifndef FXGRADIENTBAR_H
#define FXGRADIENTBAR_H

#ifndef FXFRAME_H
#include "FXFrame.h"
#endif

namespace FX {


/// Gradient bar orientation
enum {
  GRADIENTBAR_HORIZONTAL      = 0,                          /// Gradient bar shown horizontally
  GRADIENTBAR_VERTICAL        = 0x00008000,                 /// Gradient bar shown vertically
  GRADIENTBAR_NO_CONTROLS     = 0,                          /// No controls shown
  GRADIENTBAR_CONTROLS_TOP    = 0x00010000,                 /// Controls on top
  GRADIENTBAR_CONTROLS_BOTTOM = 0x00020000,                 /// Controls on bottom
  GRADIENTBAR_CONTROLS_LEFT   = GRADIENTBAR_CONTROLS_TOP,   /// Controls on left
  GRADIENTBAR_CONTROLS_RIGHT  = GRADIENTBAR_CONTROLS_BOTTOM /// Controls on right
  };


/// Blend modes
enum {
  GRADIENT_BLEND_LINEAR,                /// Linear blend
  GRADIENT_BLEND_POWER,                 /// Power law blend
  GRADIENT_BLEND_SINE,                  /// Sine blend
  GRADIENT_BLEND_INCREASING,            /// Quadratic increasing blend
  GRADIENT_BLEND_DECREASING             /// Quadratic decreasing blend
  };


// Gradient segment
struct FXGradient {
  FXdouble lower;                     /// Lower value
  FXdouble middle;                    /// Middle value
  FXdouble upper;                     /// Upper value
  FXColor  lowerColor;                /// Lower color
  FXColor  upperColor;                /// Upper color
  FXuchar  blend;                     /// Blend method
  };


class FXImage;


/**
* The gradient bar is a control that is used to edit color gradient,
* such as used in texture mapping and shape filling.
*/
class FXAPI FXGradientBar : public FXFrame {
  FXDECLARE(FXGradientBar)
protected:
  FXImage     *bar;                     // Image containing colors
  FXGradient  *seg;                     // Segments
  FXint        nsegs;                   // Number of segments
  FXint        sellower;                // Lower selected segment
  FXint        selupper;                // Upper selected segment
  FXint        dropped;                 // Dropped segment
  FXint        current;                 // Current segment
  FXint        anchor;                  // Anchor segment
  FXint        grip;                    // Grip being dragged, if any
  FXint        where;                   // Where dropped in segment
  FXint        barsize;                 // Bar size
  FXint        controlsize;             // Size of control
  FXColor      selectColor;             // Select color
  FXString     tip;                     // Tooltip value
  FXString     help;                    // Help value
  FXint        offset;                  // Offset
protected:
  FXGradientBar();
  void updatebar();
  FXdouble getValue(FXint x,FXint y) const;
  FXint getSegmentLowerPos(FXint sg) const;
  FXint getSegmentUpperPos(FXint sg) const;
  FXint getSegmentMiddlePos(FXint sg) const;
  void drawUpArrow(FXDCWindow& dc,FXint x,FXint y,FXColor clr);
  void drawDnArrow(FXDCWindow& dc,FXint x,FXint y,FXColor clr);
  void drawRtArrow(FXDCWindow& dc,FXint x,FXint y,FXColor clr);
  void drawLtArrow(FXDCWindow& dc,FXint x,FXint y,FXColor clr);
  void drawBottomArrows(FXDCWindow& dc,FXint x,FXint y,FXint w,FXint h);
  void drawTopArrows(FXDCWindow& dc,FXint x,FXint y,FXint w,FXint h);
  void drawLeftArrows(FXDCWindow& dc,FXint x,FXint y,FXint w,FXint h);
  void drawRightArrows(FXDCWindow& dc,FXint x,FXint y,FXint w,FXint h);
  static FXdouble blendlinear(FXdouble middle,FXdouble pos);
  static FXdouble blendpower(FXdouble middle,FXdouble pos);
  static FXdouble blendsine(FXdouble middle,FXdouble pos);
  static FXdouble blendincreasing(FXdouble middle,FXdouble pos);
  static FXdouble blenddecreasing(FXdouble middle,FXdouble pos);
  static const FXGradient defaultGradient[3];
private:
  FXGradientBar(const FXGradientBar&);
  FXGradientBar &operator=(const FXGradientBar&);
public:
  enum {
    GRIP_NONE,
    GRIP_LOWER,
    GRIP_SEG_LOWER,
    GRIP_MIDDLE,
    GRIP_SEG_UPPER,
    GRIP_UPPER
    };
public:
  long onPaint(FXObject*,FXSelector,void*);
  long onMotion(FXObject*,FXSelector,void*);
  long onLeftBtnPress(FXObject*,FXSelector,void*);
  long onLeftBtnRelease(FXObject*,FXSelector,void*);
  long onRightBtnPress(FXObject*,FXSelector,void*);
  long onRightBtnRelease(FXObject*,FXSelector,void*);
  long onDNDEnter(FXObject*,FXSelector,void*);
  long onDNDLeave(FXObject*,FXSelector,void*);
  long onDNDMotion(FXObject*,FXSelector,void*);
  long onDNDDrop(FXObject*,FXSelector,void*);
  long onCmdBlending(FXObject*,FXSelector,void*);
  long onUpdBlending(FXObject*,FXSelector,void*);
  long onUpdSegColor(FXObject*,FXSelector,void*);
  long onCmdSegColor(FXObject*,FXSelector,void*);
  long onUpdRecenter(FXObject*,FXSelector,void*);
  long onCmdRecenter(FXObject*,FXSelector,void*);
  long onUpdSplit(FXObject*,FXSelector,void*);
  long onCmdSplit(FXObject*,FXSelector,void*);
  long onUpdMerge(FXObject*,FXSelector,void*);
  long onCmdMerge(FXObject*,FXSelector,void*);
  long onUpdUniform(FXObject*,FXSelector,void*);
  long onCmdUniform(FXObject*,FXSelector,void*);
  long onCmdReset(FXObject*,FXSelector,void*);
  long onCmdSetHelp(FXObject*,FXSelector,void*);
  long onCmdGetHelp(FXObject*,FXSelector,void*);
  long onCmdSetTip(FXObject*,FXSelector,void*);
  long onCmdGetTip(FXObject*,FXSelector,void*);
  long onQueryHelp(FXObject*,FXSelector,void*);
  long onQueryTip(FXObject*,FXSelector,void*);
public:
  enum{
    ID_LOWER_COLOR=FXFrame::ID_LAST,
    ID_UPPER_COLOR,
    ID_BLEND_LINEAR,
    ID_BLEND_POWER,
    ID_BLEND_SINE,
    ID_BLEND_INCREASING,
    ID_BLEND_DECREASING,
    ID_RECENTER,
    ID_SPLIT,
    ID_MERGE,
    ID_UNIFORM,
    ID_RESET,
    ID_LAST
    };
public:

  /// Construct a gradient bar
  FXGradientBar(FXComposite* p,FXObject* tgt=nullptr,FXSelector sel=0,FXuint opts=FRAME_NORMAL,FXint x=0,FXint y=0,FXint w=0,FXint h=0,FXint pl=DEFAULT_PAD,FXint pr=DEFAULT_PAD,FXint pt=DEFAULT_PAD,FXint pb=DEFAULT_PAD);

  /// Create server-side resources
  virtual void create();

  /// Perform layout
  virtual void layout();

  /// Return default width
  virtual FXint getDefaultWidth();

  /// Return default height
  virtual FXint getDefaultHeight();

  /**
   * Obtain segment containing location x, y.
   * Returns -1 if no matching segment was found.
   */
  FXint getSegment(FXint x,FXint y) const;

  /**
   * Get the grip in segment sg which is closest to location (x, y),
   * one of GRIP_LOWER, GRIP_SEG_LOWER, GRIP_MIDDLE, GRIP_SEG_UPPER,
   * GRIP_UPPER or GRIP_NONE.
   */
  FXint getGrip(FXint sg,FXint x,FXint y) const;

  /// Return the number of segments
  FXint getNumSegments() const { return nsegs; }

  /**
  * Reset gradients to default color ramp.
  */
  void resetGradients();

  /**
   * Replace the current gradient segments.
   * The gradient bar makes a copy of the input segments array.
   */
  void setGradients(const FXGradient *segments,FXint nsegments);

  /**
  * Return a copy of the gradient segments.
  * The array of segments is allocated using allocElms() and should be freed
  * by the caller using freeElms().
  */
  void getGradients(FXGradient*& segments,FXint& nsegments) const;

  /// Change current segment
  void setCurrentSegment(FXint index,FXbool notify=false);

  /// Return current segment, or -1 if there is no current segment
  FXint getCurrentSegment() const { return current; }

  /// Change anchor segment
  void setAnchorSegment(FXint index);

  /// Return anchor segment, or -1 if there is no anchor segment
  FXint getAnchorSegment() const { return anchor; }

  /// Select segment(s)
  FXbool selectSegments(FXint fm,FXint to,FXbool notify=false);

  /// Deselect all segments
  FXbool deselectSegments(FXbool notify=false);

  /// Returns true if the specified segment is selected
  FXbool isSegmentSelected(FXint s) const;

  /// Set lower color of a segment
  void setSegmentLowerColor(FXint s,FXColor clr,FXbool notify=false);

  /// Set upper color of a segment
  void setSegmentUpperColor(FXint s,FXColor clr,FXbool notify=false);

  /// Get lower color of a segment
  FXColor getSegmentLowerColor(FXint s) const;

  /// Get upper color of a segment
  FXColor getSegmentUpperColor(FXint s) const;

  /// Move lower point of segment sg
  void moveSegmentLower(FXint sg,FXdouble val,FXbool notify=false);

  /// Move middle point of segment sg
  void moveSegmentMiddle(FXint sg,FXdouble val,FXbool notify=false);

  /// Move upper point of segment sg
  void moveSegmentUpper(FXint sg,FXdouble val,FXbool notify=false);

  /// Move segments sglo to sghi to new position val
  void moveSegments(FXint sglo,FXint sghi,FXdouble val,FXbool notify=false);

  /// Get lower value of segment sg
  FXdouble getSegmentLower(FXint sg) const;

  /// Get middle value of segment sg
  FXdouble getSegmentMiddle(FXint sg) const;

  /// Get upper value of segment sg
  FXdouble getSegmentUpper(FXint sg) const;

  /**
   * Get gradient ramp.
   * The ramp argument should be an array of size nramp, which will be
   * filled with the appropriate color values.
   */
  void gradient(FXColor *ramp,FXint nramp) const;

  /**
   * Get blend mode of segment, one of GRADIENT_BLEND_LINEAR,
   * GRADIENT_BLEND_POWER, GRADIENT_BLEND_SINE, GRADIENT_BLEND_INCREASING,
   * or GRADIENT_BLEND_DECREASING.
   */
  FXuint getSegmentBlend(FXint s) const;

  /// Split segment at the midpoint
  void splitSegments(FXint sglo,FXint sghi,FXbool notify=false);

  /// Merge segments
  void mergeSegments(FXint sglo,FXint sghi,FXbool notify=false);

  /// Make segments uniformly distributed
  void uniformSegments(FXint sglo,FXint sghi,FXbool notify=false);

  /// Change blend mode of segment
  void blendSegments(FXint sglo,FXint sghi,FXuint blend=GRADIENT_BLEND_LINEAR,FXbool notify=false);

  /// Change control size
  void setControlSize(FXint cs);

  /// Get control size
  FXint getControlSize() const { return controlsize; }

  /// Change bar size
  void setBarSize(FXint bs);

  /// Get bar size
  FXint getBarSize() const { return barsize; }

  /// Get the gradient bar style
  FXuint getBarStyle() const;

  /// Change the gradient bar style
  void setBarStyle(FXuint style);

  /// Set color
  void setSelectColor(FXColor clr);

  /// Get color
  FXColor getSelectColor() const { return selectColor; }

  /// Set status line help text for this gradient bar
  void setHelpText(const FXString& text){ help=text; }

  /// Get status line help text for this gradient bar
  const FXString& getHelpText() const { return help; }

  /// Set tool tip message for this gradient bar
  void setTipText(const FXString& text){ tip=text; }

  /// Get tool tip message for this gradient bar
  const FXString& getTipText() const { return tip; }

  /// Save gradient bar to a stream
  virtual void save(FXStream& store) const;

  /// Load gradient bar from a stream
  virtual void load(FXStream& store);

  /// Destructor
  virtual ~FXGradientBar();
  };


}

#endif
