text: // DOM-IGNORE-BEGIN
  /*******************************************************************************
  * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries.
  *
  * Subject to your compliance with these terms, you may use Microchip software
  * and any derivatives exclusively with Microchip products. It is your
  * responsibility to comply with third party license terms applicable to your
  * use of third party software (including open source software) that may
  * accompany Microchip software.
  *
  * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER
  * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED
  * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A
  * PARTICULAR PURPOSE.
  *
  * IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE,
  * INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND
  * WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS
  * BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE
  * FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN
  * ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
  * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.
  *******************************************************************************/
  // DOM-IGNORE-END
  
  
  #include "gfx/legato/widget/bargraph/legato_widget_bar_graph.h"
  
  #if LE_BARGRAPH_WIDGET_ENABLED
  
  #define MAX_TICK_LABEL_DIGITS 10
  #define MAX_TICK_LABEL_VALUE 999999999
  #define LABEL_OFFSET_MIN_PIX 5
  
  #include <stdio.h>
  #include <string.h>
  #include <gfx/legato/legato.h>
  
  #include "gfx/legato/common/legato_error.h"
  #include "gfx/legato/common/legato_utils.h"
  #include "gfx/legato/core/legato_state.h"
  #include "gfx/legato/memory/legato_memory.h"
  #include "gfx/legato/renderer/legato_renderer.h"
  #include "gfx/legato/string/legato_stringutils.h"
  #include "gfx/legato/widget/legato_widget.h"
  
  #define DEFAULT_WIDTH           101
  #define DEFAULT_HEIGHT          101
  
  #define DEFAULT_TICK_LENGTH        5
  #define DEFAULT_TICK_INTERVAL      10
  #define DEFAULT_SUBTICK_INTERVAL   5
  #define DEFAULT_MAX_VALUE          100
  #define DEFAULT_MIN_VALUE          0
  
  
  static
  #if LE_DYNAMIC_VTABLES == 0
  const
  #endif
  leBarGraphWidgetVTable barGraphWidgetVTable;
  
  void _leBarGraphWidget_Constructor(leBarGraphWidget* _this)
  {
      LE_ASSERT_THIS();
      
      /* call base class constructor */
      leWidget_Constructor((leWidget*)_this);
      
      _this->widget.fn = (void*)&barGraphWidgetVTable;
      _this->fn = &barGraphWidgetVTable;
  
      _this->widget.type = LE_WIDGET_BAR_GRAPH;
  
      _this->widget.rect.width = DEFAULT_WIDTH;
      _this->widget.rect.height = DEFAULT_HEIGHT;
  
      _this->widget.style.borderType = LE_WIDGET_BORDER_NONE;
      _this->widget.style.backgroundType = LE_WIDGET_BACKGROUND_NONE;
      
      _this->tickLength = DEFAULT_TICK_LENGTH;
      _this->fillGraphArea = LE_TRUE;
      _this->stacked = LE_FALSE;
      _this->maxValue = DEFAULT_MAX_VALUE;
      _this->minValue = DEFAULT_MIN_VALUE;
      _this->tickInterval = DEFAULT_TICK_INTERVAL;
      _this->subtickInterval = DEFAULT_SUBTICK_INTERVAL;
      
      _this->valueGridLinesVisible = LE_TRUE;
      _this->valueAxisLabelsVisible = LE_TRUE;
      _this->valueAxisTicksVisible = LE_TRUE;
      _this->valueAxisSubticksVisible = LE_TRUE;
      _this->valueAxisTicksPosition = BAR_GRAPH_TICK_CENTER;
      _this->valueAxisSubticksPosition = BAR_GRAPH_TICK_CENTER;
      
      _this->categAxisLabelsVisible = LE_TRUE;
      _this->categAxisTicksVisible = LE_TRUE;
      _this->categAxisTicksPosition = BAR_GRAPH_TICK_CENTER;
      
      leArray_Create(&_this->dataSeriesArray);
      leArray_Create(&_this->categories);
  
      _this->ticksLabelFont = NULL;
  }
  
  void _leWidget_Destructor(leWidget* wgt);
  
  void _leBarGraphWidget_Destructor(leBarGraphWidget* _this)
  {
      _this->fn->clearData(_this);
  
      _this->ticksLabelFont = NULL;
  
      _leWidget_Destructor((leWidget*)_this);
  }
  
  leBarGraphWidget* leBarGraphWidget_New()
  {
      leBarGraphWidget* wgt = NULL;
  
      wgt = LE_MALLOC(sizeof(leBarGraphWidget));
  
      if(wgt == NULL)
          return NULL;
      
      _leBarGraphWidget_Constructor(wgt);
  
      return wgt;
  }
  
  //Gets the maximum draw rectangle for the category labels
  static void getCategoryLabelMaxDrawRect(const leBarGraphWidget* _this,
                                          leRect* rect)
  {
      uint32_t categoryIndex;
      leRect textRect;
      leBarGraphCategory* category;
      
      *rect = leRect_Zero;
      
      for (categoryIndex = 0; 
          (categoryIndex < _this->categories.size); 
           categoryIndex++)
      {
          category = (leBarGraphCategory*)leArray_Get(&_this->categories, categoryIndex);
          
          if(category == NULL || category->str == NULL)
              return;
          
          category->str->fn->getRect(category->str, &textRect);
          
          if(textRect.height > rect->height)
          {
              rect->height = textRect.height;
          }
                      
          if(textRect.width > rect->width)
          {
              rect->width = textRect.width;
          }
      }
  }
  
  //Gets the superset (largest) draw rectangle for the labels. 
  static void getValueLabelMaxDrawRect(const leBarGraphWidget* _this,
                                       leRect * rect)
  {
      leRect minLabelRect;
      leRect maxLabelRect;
      char strbuff[MAX_TICK_LABEL_DIGITS];
  
      *rect = leRect_Zero;
      
      if(_this->ticksLabelFont == NULL)
          return;
      
      //Calculate the offset for the value labels
      if(_this->valueAxisLabelsVisible)
      {
          minLabelRect = leRect_Zero;
          maxLabelRect = leRect_Zero;
  
          if(_this->minValue < 0)
          {
              //Protect from overflow
              if(_this->minValue > -MAX_TICK_LABEL_VALUE)
              {
                  sprintf(strbuff, "%d", (int)_this->minValue);
              }
              else
              {
                  sprintf(strbuff, "---");
              }
  
              leStringUtils_GetRectCStr(strbuff,
                                        _this->ticksLabelFont,
                                        &minLabelRect);
          }
  
          //Protect from overflow
          if(_this->maxValue < MAX_TICK_LABEL_VALUE) 
          {
              sprintf(strbuff, "%d", (int)_this->maxValue);
          }
          else 
          {
              sprintf(strbuff, "---");
          }
  
          leStringUtils_GetRectCStr(strbuff,
                                    _this->ticksLabelFont,
                                    &maxLabelRect);
  
          rect->width = (maxLabelRect.width > minLabelRect.width) ?
                        (maxLabelRect.width) :
                        (minLabelRect.width);
  
          rect->height = (maxLabelRect.height > minLabelRect.height) ? 
                         (maxLabelRect.height) :
                         (minLabelRect.height);
      }
  }
  
  //Gets the rectangle of the graph area (without labels or ticks)
  void _leBarGraphWidget_GetGraphRect(const leBarGraphWidget* _this,
                                      leRect* graphRect)
  {
      leRect categoryLabelMaxRect;
      leRect valueLabelMaxRect;
  
      graphRect->x = _this->widget.rect.x;
      graphRect->y = _this->widget.rect.y;
      graphRect->width = _this->widget.rect.width;
      graphRect->height = _this->widget.rect.height;
  
      graphRect->x += _this->widget.margin.left;
      graphRect->y += _this->widget.margin.top;
      graphRect->width -= _this->widget.margin.left + _this->widget.margin.right;
      graphRect->height -= _this->widget.margin.top + _this->widget.margin.bottom;
  
      // adjust for value tick position
      if(_this->valueAxisTicksVisible == LE_TRUE)
      {
          switch(_this->valueAxisTicksPosition)
          {
              case BAR_GRAPH_TICK_OUT:
              {
                  graphRect->x += _this->tickLength;
                  graphRect->width -= _this->tickLength;
  
                  break;
              }
              case BAR_GRAPH_TICK_CENTER:
              {
                  graphRect->x += _this->tickLength / 2;
                  graphRect->width -= _this->tickLength / 2;
  
                  break;
              }
              default: { }
          }
      }
  
      // adjust for value labels
      if(_this->valueAxisLabelsVisible == LE_TRUE && _this->ticksLabelFont != NULL)
      {
          getValueLabelMaxDrawRect(_this, &valueLabelMaxRect);
  
          //graphRect.adjust(0, valueLabelMaxRect.height / 2 + LABEL_OFFSET_MIN_PIX, 0, 0);
  
          if(valueLabelMaxRect.width < LABEL_OFFSET_MIN_PIX)
          {
              valueLabelMaxRect.width = LABEL_OFFSET_MIN_PIX;
          }
  
          if(valueLabelMaxRect.height < LABEL_OFFSET_MIN_PIX)
          {
              valueLabelMaxRect.height = LABEL_OFFSET_MIN_PIX;
          }
  
          graphRect->x += valueLabelMaxRect.width;
          graphRect->width -= valueLabelMaxRect.width;
  
          graphRect->y += valueLabelMaxRect.height;
          graphRect->height -= valueLabelMaxRect.height;
      }
  
      if(_this->categories.size > 0)
      {
          // adjust for category ticks
          if(_this->categAxisTicksVisible == LE_TRUE)
          {
              switch(_this->categAxisTicksPosition)
              {
                  case BAR_GRAPH_TICK_OUT:
                  {
                      graphRect->height -= _this->tickLength;
  
                      break;
                  }
                  case BAR_GRAPH_TICK_CENTER:
                  {
                      graphRect->height -= _this->tickLength / 2;
  
                      break;
                  }
                  default: { }
              }
          }
  
          if(_this->categAxisLabelsVisible == LE_TRUE)
          {
              getCategoryLabelMaxDrawRect(_this, &categoryLabelMaxRect);
  
              graphRect->height -= categoryLabelMaxRect.height;
          }
      }
  }
  
  leResult _leBarGraphWidget_GetBarRect(const leBarGraphWidget* _this,
                                        uint32_t seriesID,
                                        uint32_t categoryID,
                                        leRect * rect)
  {
      leRect graphRect;
      
      LE_ASSERT_THIS();
      
      if(rect == NULL)
          return LE_FAILURE;
      
      _leBarGraphWidget_GetGraphRect(_this, &graphRect);
      
      rect->height = graphRect.height;
      rect->y = graphRect.y;
  
      if(_this->stacked == LE_TRUE)
      {
          rect->width = (graphRect.width / _this->categories.size) / 3;
          
          rect->x = graphRect.x + 
                ((categoryID * graphRect.width) / _this->categories.size) + 
                (graphRect.width / (_this->categories.size * 2)) - 
                (rect->width/2);
      }
      else
      {
          rect->width = (graphRect.width / 
                            (_this->categories.size * (_this->dataSeriesArray.size + 2)));
          
          rect->x = graphRect.x + 
                (categoryID * graphRect.width / _this->categories.size) + 
                seriesID * rect->width + rect->width;
  
      }
      
      return LE_SUCCESS;
  }
  
  static uint32_t getTickLength(const leBarGraphWidget* _this)
  {
      LE_ASSERT_THIS();
      
      return _this->tickLength;
  }
  
  static leResult setTickLength(leBarGraphWidget* _this,
                                uint32_t length)
  {
      LE_ASSERT_THIS();
      
      if(_this->tickLength == length)
          return LE_SUCCESS;
          
      _this->tickLength = length;
      
      _this->fn->invalidate(_this);
          
      return LE_SUCCESS;
  }
  
  static leBool getFillGraphArea(const leBarGraphWidget* _this)
  {
      LE_ASSERT_THIS();
      
      return _this->fillGraphArea;
  }
  
  static leResult setFillGraphArea(leBarGraphWidget* _this,
                                   leBool fill)
  {
      LE_ASSERT_THIS();
      
      if(_this->fillGraphArea == fill)
          return LE_SUCCESS;
          
      _this->fillGraphArea = fill;
      
      _this->fn->invalidate(_this);
          
      return LE_SUCCESS;
  }
  
  static leBool getGridLinesVisible(const leBarGraphWidget* _this,
                                    leBarGraphValueAxis axis)
  {
      LE_ASSERT_THIS();
  
      (void)axis; // void
      
      return _this->valueGridLinesVisible;
  }
  
  static leResult setGridLinesVisible(leBarGraphWidget* _this,
                                      leBarGraphValueAxis axis,
                                      leBool vis)
  {
      LE_ASSERT_THIS();
  
      (void)axis; // void
      
      if(_this->valueGridLinesVisible == vis)
          return LE_SUCCESS;
          
      _this->valueGridLinesVisible = vis;
      
      _this->fn->invalidate(_this);
          
      return LE_SUCCESS;
  }
  
  static leBool getStacked(const leBarGraphWidget* _this)
  {
      LE_ASSERT_THIS();
      
      return _this->stacked;
  }
  
  static leResult setStacked(leBarGraphWidget* _this,
                             leBool stacked)
  {   
      LE_ASSERT_THIS();
      
      if(_this->stacked == stacked)
          return LE_SUCCESS;
          
      _this->stacked = stacked;
      
      _this->fn->invalidate(_this);
          
      return LE_SUCCESS;
  }
  
  static uint32_t getMaxValue(const leBarGraphWidget* _this,
                              leBarGraphValueAxis axis)
  {   
      LE_ASSERT_THIS();
  
      (void)axis; // void
  
      return _this->maxValue;
  }
  
  static leResult setMaxValue(leBarGraphWidget* _this,
                              leBarGraphValueAxis axis,
                              int32_t value)
  {   
      LE_ASSERT_THIS();
  
      (void)axis; // void
      
      if(value <= _this->minValue)
          return LE_FAILURE;
      
      if(_this->maxValue == value)
          return LE_SUCCESS;
          
      _this->maxValue = value;
      
      _this->fn->invalidate(_this);
          
      return LE_SUCCESS;
  }
  
  static uint32_t getMinValue(const leBarGraphWidget* _this,
                              leBarGraphValueAxis axis)
  {   
      LE_ASSERT_THIS();
  
      (void)axis; // void
      
      return _this->minValue;
  }
  
  static leResult setMinValue(leBarGraphWidget* _this,
                              leBarGraphValueAxis axis,
                              int32_t value)
  {   
      LE_ASSERT_THIS();
  
      (void)axis; // void
      
      if(value >= _this->maxValue)
          return LE_FAILURE;
  
      if(_this->minValue == value)
          return LE_SUCCESS;
          
      _this->minValue = value;
      
      _this->fn->invalidate(_this);
          
      return LE_SUCCESS;
  }
  
  static leBool getValueAxisLabelsVisible(const leBarGraphWidget* _this,
                                          leBarGraphValueAxis axis)
  {
      LE_ASSERT_THIS();
  
      (void)axis; // void
      
      return _this->valueAxisLabelsVisible;
  }
  
  static leResult setValueAxisLabelsVisible(leBarGraphWidget* _this,
                                            leBarGraphValueAxis axis,
                                            leBool vis)
  {
      LE_ASSERT_THIS();
  
      (void)axis; // void
      
      if(_this->valueAxisLabelsVisible == vis)
          return LE_SUCCESS;
          
      _this->valueAxisLabelsVisible = vis;
      
      _this->fn->invalidate(_this);
          
      return LE_SUCCESS;
  }
  
  static leBool getValueAxisTicksVisible(const leBarGraphWidget* _this,
                                         leBarGraphValueAxis axis)
  {
      LE_ASSERT_THIS();
  
      (void)axis; // void
      
      return _this->valueAxisTicksVisible;
  }
  
  static leResult setValueAxisTicksVisible(leBarGraphWidget* _this,
                                           leBarGraphValueAxis axis,
                                           leBool vis)
  {
      LE_ASSERT_THIS();
  
      (void)axis; // void
      
      if(_this->valueAxisTicksVisible == vis)
          return LE_SUCCESS;
          
      _this->valueAxisTicksVisible = vis;
      
      _this->fn->invalidate(_this);
          
      return LE_SUCCESS;
  }
  
  static uint32_t getValueAxisTicksInterval(const leBarGraphWidget* _this,
                                            leBarGraphValueAxis axis)
  {
      LE_ASSERT_THIS();
  
      (void)axis; // void
      
      return _this->tickInterval;
  }
  
  static leResult setValueAxisTicksInterval(leBarGraphWidget* _this,
                                            leBarGraphValueAxis axis,
                                            uint32_t itv)
  {
      LE_ASSERT_THIS();
  
      (void)axis; // void
      
      if(_this->tickInterval == itv)
          return LE_SUCCESS;
          
      _this->tickInterval = itv;
      
      _this->fn->invalidate(_this);
          
      return LE_SUCCESS;
  }
  
  
  static leBarGraphTickPosition getValueAxisTicksPosition(const leBarGraphWidget* _this,
                                                          leBarGraphValueAxis axis)
  {
      LE_ASSERT_THIS();
  
      (void)axis; // void
      
      return _this->valueAxisTicksPosition;
  }
  
  static leResult setValueAxisTicksPosition(leBarGraphWidget* _this,
                                            leBarGraphValueAxis axis,
                                            leBarGraphTickPosition pos)
  {
      LE_ASSERT_THIS();
  
      (void)axis; // void
      
      if(_this->valueAxisTicksPosition == pos)
          return LE_SUCCESS;
          
      _this->valueAxisTicksPosition = pos;
      
      _this->fn->invalidate(_this);
          
      return LE_SUCCESS; 
  }
  
  static leBool getValueAxisSubticksVisible(const leBarGraphWidget* _this,
                                            leBarGraphValueAxis axis)
  {
      LE_ASSERT_THIS();
  
      (void)axis; // void
      
      return _this->valueAxisSubticksVisible;
  }
  
  static leResult setValueAxisSubticksVisible(leBarGraphWidget* _this,
                                              leBarGraphValueAxis axis,
                                              leBool vis)
  {
      LE_ASSERT_THIS();
  
      (void)axis; // void
      
      if(_this->valueAxisSubticksVisible == vis)
          return LE_SUCCESS;
          
      _this->valueAxisSubticksVisible = vis;
      
      _this->fn->invalidate(_this);
          
      return LE_SUCCESS;
  }
  
  static uint32_t getValueAxisSubticksInterval(const leBarGraphWidget* _this,
                                               leBarGraphValueAxis axis)
  {
      LE_ASSERT_THIS();
  
      (void)axis; // void
      
      return _this->subtickInterval;
  }
  
  static leResult setValueAxisSubticksInterval(leBarGraphWidget* _this,
                                               leBarGraphValueAxis axis,
                                               uint32_t itv)
  {
      LE_ASSERT_THIS();
  
      (void)axis; // void
      
      if(_this->subtickInterval == itv)
          return LE_SUCCESS;
          
      _this->subtickInterval = itv;
      
      _this->fn->invalidate(_this);
          
      return LE_SUCCESS;
  }
  
  static leBarGraphTickPosition getValueAxisSubticksPosition(const leBarGraphWidget* _this,
                                                             leBarGraphValueAxis axis)
  {
      LE_ASSERT_THIS();
  
      (void)axis; // void
  
      return _this->valueAxisSubticksPosition;
  }
  
  static leResult setValueAxisSubticksPosition(leBarGraphWidget* _this,
                                               leBarGraphValueAxis axis,
                                               leBarGraphTickPosition pos)
  {
      LE_ASSERT_THIS();
  
      (void)axis; // void
      
      if(_this->valueAxisSubticksPosition == pos)
          return LE_SUCCESS;
          
      _this->valueAxisSubticksPosition = pos;
      
      _this->fn->invalidate(_this);
          
      return LE_SUCCESS;     
  }
  
  static leBool getCategoryAxisTicksVisible(const leBarGraphWidget* _this)
  {
      LE_ASSERT_THIS();
      
      return _this->categAxisTicksVisible;
  }
  
  static leResult setCategoryAxisTicksVisible(leBarGraphWidget* _this,
                                              leBool vis)
  {
      LE_ASSERT_THIS();
      
      if(_this->categAxisTicksVisible == vis)
          return LE_SUCCESS;
          
      _this->categAxisTicksVisible = vis;
      
      _this->fn->invalidate(_this);
          
      return LE_SUCCESS;
  }
  
  static leBarGraphTickPosition getCategoryAxisTicksPosition(const leBarGraphWidget* _this)
  {
      LE_ASSERT_THIS();
      
      return _this->categAxisTicksPosition;
  }
  
  static leResult setCategoryAxisTicksPosition(leBarGraphWidget* _this,
                                               leBarGraphTickPosition pos)
  {
      LE_ASSERT_THIS();
      
      if(_this->categAxisTicksPosition == pos)
          return LE_SUCCESS;
          
      _this->categAxisTicksPosition = pos;
      
      _this->fn->invalidate(_this);
          
      return LE_SUCCESS;      
  }
  
  static leBool getCategoryAxisLabelsVisible(const leBarGraphWidget* _this)
  {
      LE_ASSERT_THIS();
      
      return _this->categAxisLabelsVisible;
  }
  
  static leResult setCategoryAxisLabelsVisible(leBarGraphWidget* _this,
                                               leBool vis)
  {
      LE_ASSERT_THIS();
      
      if(_this->categAxisLabelsVisible == vis)
          return LE_SUCCESS;
          
      _this->categAxisLabelsVisible = vis;
      
      _this->fn->invalidate(_this);
          
      return LE_SUCCESS;
  }
  
  static leFont* getTicksLabelFont(leBarGraphWidget* _this)
  {
      LE_ASSERT_THIS();
      
      return (leFont*)_this->ticksLabelFont;
  }
  
  static leResult setTicksLabelFont(leBarGraphWidget* _this, 
                                    const leFont* font)
  {
      LE_ASSERT_THIS();
  
      _this->ticksLabelFont = font;
      
      _this->fn->invalidate(_this);
          
      return LE_SUCCESS;
  }
  
  static leResult addSeries(leBarGraphWidget* _this,
                            uint32_t* seriesID)
  {
      leBarGraphDataSeries * series;
      
      LE_ASSERT_THIS();
      
      series = LE_MALLOC(sizeof(leBarGraphDataSeries));
      
      if(series == NULL)
          return LE_FAILURE;
  
      leArray_Create(&series->data);
      
      series->axis = BAR_GRAPH_AXIS_0;
      series->scheme = leGetDefaultScheme();
      
      leArray_PushBack(&_this->dataSeriesArray, series);
      
      if(seriesID != NULL)
      {
          *seriesID = _this->dataSeriesArray.size;
      }
      
      _this->fn->invalidate(_this);
      
      return LE_SUCCESS;
  }
  
  static leResult addDataToSeries(leBarGraphWidget* _this,
                                  uint32_t seriesID,
                                  int32_t value,
                                  uint32_t* index)
  {
      leBarGraphDataSeries* series;
      int32_t* data;
      
      LE_ASSERT_THIS();
      
      if(seriesID >= _this->dataSeriesArray.size)
          return LE_FAILURE;
      
      series = leArray_Get(&_this->dataSeriesArray, seriesID);
      
      if(series == NULL)
          return LE_FAILURE;
      
      data = LE_MALLOC(sizeof(data));
      
      if(data == NULL)
          return LE_FAILURE;
      
      *data = value;
      
      leArray_PushBack(&series->data, (void*)data);
  
      if(index != NULL)
      {
         *index = series->data.size - 1;
      }
      
      _this->fn->invalidate(_this);
      
      return LE_SUCCESS;
  }
  
  static leResult setDataInSeries(leBarGraphWidget* _this, 
                                  uint32_t seriesID,
                                  uint32_t index,
                                  int32_t value)
  {
      leBarGraphDataSeries* series;
      int32_t* data;
      leRect damagedRect;
      
      LE_ASSERT_THIS();
      
      if(seriesID >= _this->dataSeriesArray.size)
          return LE_FAILURE;
      
      series = leArray_Get(&_this->dataSeriesArray, seriesID);
      
      if(series == NULL)
          return LE_FAILURE;
  
      if(index >= series->data.size)
          return LE_FAILURE;
  
      data = leArray_Get(&series->data, index);
      
      if(data == NULL)
          return LE_FAILURE;
      
      if(value == *data)
          return LE_SUCCESS;
      
      *data = value;
  
      _leBarGraphWidget_GetBarRect(_this,
                                   seriesID,
                                   index,
                                   &damagedRect);
  
  
      _leWidget_DamageArea((leWidget*)_this, &damagedRect);
      
      return LE_SUCCESS;
  }
  
  static leScheme* getSeriesScheme(const leBarGraphWidget* _this,
                                   uint32_t seriesID)
  {
      leBarGraphDataSeries* series;
      
      LE_ASSERT_THIS();
      
      if(seriesID >= _this->dataSeriesArray.size)
          return NULL;
      
      series = leArray_Get(&_this->dataSeriesArray, seriesID);
      
      if(series == NULL)
          return NULL;
      
      return (leScheme*)series->scheme;
  }
  
  static leResult setSeriesScheme(leBarGraphWidget* _this,
                                  uint32_t seriesID,
                                  const leScheme* scheme)
  {
      leBarGraphDataSeries* series;
      
      LE_ASSERT_THIS();
      
      if(scheme == NULL)
          return LE_FAILURE;
      
      if(seriesID >= _this->dataSeriesArray.size)
          return LE_FAILURE;
      
      series = leArray_Get(&_this->dataSeriesArray, seriesID);
      
      if(series == NULL)
          return LE_FAILURE;
      
      series->scheme = scheme;
      
      _this->fn->invalidate(_this);
          
      return LE_SUCCESS;
  }
  
  static leResult addCategory(leBarGraphWidget* _this,
                              uint32_t* id)
  {
      leBarGraphCategory* cat;
  
      LE_ASSERT_THIS();
  
      cat = LE_MALLOC(sizeof(leBarGraphCategory));
  
      if(cat == NULL)
          return LE_FAILURE;
  
      cat->str = NULL;
      cat->drawX = 0;
  
      leArray_PushBack(&_this->categories, cat);
  
      if(id != NULL)
      {
          *id = _this->categories.size;
      }
      
      _this->fn->invalidate(_this);
      
      return LE_SUCCESS;
  }
  
  static leString* getCategoryString(const leBarGraphWidget* _this,
                                     uint32_t categoryID)
  {
      leBarGraphCategory* cat;
      
      LE_ASSERT_THIS();
      
      if(categoryID >= _this->categories.size)
          return NULL;
      
      cat = leArray_Get(&_this->categories, categoryID);
      
      if(cat == NULL)
          return NULL;
          
      return (leString*)cat->str;
  }
  
  static leResult setCategoryString(leBarGraphWidget* _this,
                                    uint32_t categoryID,
                                    const leString* str)
  {
      leBarGraphCategory* cat;
  
      if(categoryID >= _this->categories.size)
          return LE_FAILURE;
      
      LE_ASSERT_THIS();
  
      cat = leArray_Get(&_this->categories, categoryID);
  
      cat->str = str;
      
      _this->fn->invalidate(_this);
      
      return LE_SUCCESS;
  }
  
  static leResult clearData(leBarGraphWidget* _this)
  {
      leBarGraphCategory* cat;
      leBarGraphDataSeries* seriesArray;
  
      uint32_t i;
      
      LE_ASSERT_THIS();
      
      // free the category array
      for(i = 0; i < _this->categories.size; i++)
      {
          cat = leArray_Get(&_this->categories, i);
  
          if(cat != NULL)
          {
              LE_FREE(cat);
          }
      }
  
      leArray_Clear(&_this->categories);
      
      // free category string data
      for (i = 0; i < _this->dataSeriesArray.size; i++)
      {
          seriesArray = leArray_Get(&_this->dataSeriesArray, i);
          
          if(seriesArray != NULL)
          {
              // destroy the data list
              leArray_Clear(&seriesArray->data);
          }    
          
          // destroy the pointer list
          LE_FREE(seriesArray);
      }
      
      leArray_Clear(&_this->dataSeriesArray);
      
      _this->fn->invalidate(_this);
      
      return LE_SUCCESS;
  }
  
  static void handleLanguageChangeEvent(leBarGraphWidget* _this)
  {
      LE_ASSERT_THIS();
  
      _this->fn->invalidate(_this);
  }
  
  void _leBarGraphWidget_Paint(leBarGraphWidget* _this);
  
  #if LE_DYNAMIC_VTABLES == 1
  void _leWidget_FillVTable(leWidgetVTable* tbl);
  
  void _leBarGraphWidget_GenerateVTable()
  {
      _leWidget_FillVTable((void*)&barGraphWidgetVTable);
      
      /* overrides from base class */
      barGraphWidgetVTable._destructor = _leBarGraphWidget_Destructor;
      barGraphWidgetVTable._paint = _leBarGraphWidget_Paint;
      barGraphWidgetVTable.languageChangeEvent = handleLanguageChangeEvent;
      
      /* member functions */
      barGraphWidgetVTable.getTickLength = getTickLength;
      barGraphWidgetVTable.setTickLength = setTickLength;
      barGraphWidgetVTable.getMinValue = getMinValue;
      barGraphWidgetVTable.setMinValue = setMinValue;
      barGraphWidgetVTable.getMaxValue = getMaxValue;
      barGraphWidgetVTable.setMaxValue = setMaxValue;
      barGraphWidgetVTable.getValueAxisLabelsVisible = getValueAxisLabelsVisible;
      barGraphWidgetVTable.setValueAxisLabelsVisible = setValueAxisLabelsVisible;
      barGraphWidgetVTable.getValueAxisTicksVisible = getValueAxisTicksVisible;
      barGraphWidgetVTable.setValueAxisTicksVisible = setValueAxisTicksVisible;
      barGraphWidgetVTable.getValueAxisTicksInterval = getValueAxisTicksInterval;
      barGraphWidgetVTable.setValueAxisTicksInterval = setValueAxisTicksInterval;
      barGraphWidgetVTable.getValueAxisTicksPosition = getValueAxisTicksPosition;
      barGraphWidgetVTable.setValueAxisTicksPosition = setValueAxisTicksPosition;
      barGraphWidgetVTable.getValueAxisSubticksVisible = getValueAxisSubticksVisible;
      barGraphWidgetVTable.setValueAxisSubticksVisible = setValueAxisSubticksVisible;
      barGraphWidgetVTable.getValueAxisSubticksInterval = getValueAxisSubticksInterval;
      barGraphWidgetVTable.setValueAxisSubticksInterval = setValueAxisSubticksInterval;
      barGraphWidgetVTable.getValueAxisSubticksPosition = getValueAxisSubticksPosition;
      barGraphWidgetVTable.setValueAxisSubticksPosition = setValueAxisSubticksPosition;
      barGraphWidgetVTable.getCategoryAxisTicksVisible = getCategoryAxisTicksVisible;
      barGraphWidgetVTable.setCategoryAxisTicksVisible = setCategoryAxisTicksVisible;
      barGraphWidgetVTable.getCategoryAxisTicksPosition = getCategoryAxisTicksPosition;
      barGraphWidgetVTable.setCategoryAxisTicksPosition = setCategoryAxisTicksPosition;
      barGraphWidgetVTable.getCategoryAxisLabelsVisible = getCategoryAxisLabelsVisible;
      barGraphWidgetVTable.setCategoryAxisLabelsVisible = setCategoryAxisLabelsVisible;
      barGraphWidgetVTable.addSeries = addSeries;
      barGraphWidgetVTable.getSeriesScheme = getSeriesScheme;
      barGraphWidgetVTable.setSeriesScheme = setSeriesScheme;
      barGraphWidgetVTable.addDataToSeries = addDataToSeries;
      barGraphWidgetVTable.setDataInSeries = setDataInSeries;
      barGraphWidgetVTable.clearData = clearData;
      barGraphWidgetVTable.getTicksLabelFont = getTicksLabelFont;
      barGraphWidgetVTable.setTicksLabelFont = setTicksLabelFont;
      barGraphWidgetVTable.getGridLinesVisible = getGridLinesVisible;
      barGraphWidgetVTable.setGridLinesVisible = setGridLinesVisible;
      barGraphWidgetVTable.addCategory = addCategory;
      barGraphWidgetVTable.getCategoryString = getCategoryString;
      barGraphWidgetVTable.setCategoryString = setCategoryString;
      barGraphWidgetVTable.getStacked = getStacked;
      barGraphWidgetVTable.setStacked = setStacked;
      barGraphWidgetVTable.getFillGraphArea = getFillGraphArea;
      barGraphWidgetVTable.setFillGraphArea = setFillGraphArea;
  }
  
  void _leBarGraphWidget_FillVTable(leBarGraphWidgetVTable* tbl)
  {
      *tbl = barGraphWidgetVTable;
  }
  #else
  static const leBarGraphWidgetVTable barGraphWidgetVTable =
  {
      // base class
      .getType = (void*)_leWidget_GetType,
      .getX = (void*)_leWidget_GetX,
      .setX = (void*)_leWidget_SetX,
      .getY = (void*)_leWidget_GetY,
      .setY = (void*)_leWidget_SetY,
      .setPosition = (void*)_leWidget_SetPosition,
      .translate = (void*)_leWidget_Translate,
      .getWidth = (void*)_leWidget_GetWidth,
      .setWidth = (void*)_leWidget_SetWidth,
      .getHeight = (void*)_leWidget_GetHeight,
      .setHeight = (void*)_leWidget_SetHeight,
      .setSize = (void*)_leWidget_SetSize,
      .resize = (void*)_leWidget_Resize,
      .getAlphaEnabled = (void*)_leWidget_GetAlphaEnabled,
      .getCumulativeAlphaEnabled = (void*)_leWidget_GetCumulativeAlphaEnabled,
      .setAlphaEnabled = (void*)_leWidget_SetAlphaEnabled,
      .getAlphaAmount = (void*)_leWidget_GetAlphaAmount,
      .getCumulativeAlphaAmount = (void*)_leWidget_GetCumulativeAlphaAmount,
      .setAlphaAmount = (void*)_leWidget_SetAlphaAmount,
      .isOpaque = (void*)_leWidget_IsOpaque,
      .getEnabled = (void*)_leWidget_GetEnabled,
      .setEnabled = (void*)_leWidget_SetEnabled,
      .getVisible = (void*)_leWidget_GetVisible,
      .setVisible = (void*)_leWidget_SetVisible,
      .localRect = (void*)_leWidget_LocalRect,
      .rectToParent = (void*)_leWidget_RectToParentSpace,
      .rectToScreen = (void*)_leWidget_RectToScreenSpace,
      .addChild = (void*)_leWidget_AddChild,
      .removeChild = (void*)_leWidget_RemoveChild,
      .removeAllChildren = (void*)_leWidget_RemoveAllChildren,
      .getRootWidget = (void*)_leWidget_GetRootWidget,
      .setParent = (void*)_leWidget_SetParent,
      .getChildCount = (void*)_leWidget_GetChildCount,
      .getChildAtIndex = (void*)_leWidget_GetChildAtIndex,
      .getIndexOfChild = (void*)_leWidget_GetIndexOfChild,
      .containsDescendant = (void*)_leWidget_ContainsDescendant,
      .getScheme = (void*)_leWidget_GetScheme,
      .setScheme = (void*)_leWidget_SetScheme,
      .getBorderType = (void*)_leWidget_GetBorderType,
      .setBorderType = (void*)_leWidget_SetBorderType,
      .getBackgroundType = (void*)_leWidget_GetBackgroundType,
      .setBackgroundType = (void*)_leWidget_SetBackgroundType,
      .getHAlignment = (void*)_leWidget_GetHAlignment,
      .setHAlignment = (void*)_leWidget_SetHAlignment,
      .getVAlignment = (void*)_leWidget_GetVAlignment,
      .setVAlignment = (void*)_leWidget_SetVAlignment,
      .getMargins = (void*)_leWidget_GetMargins,
      .setMargins = (void*)_leWidget_SetMargins,
      .getCornerRadius = (void*)_leWidget_GetCornerRadius,
      .setCornerRadius = (void*)_leWidget_SetCornerRadius,
      .hasFocus = (void*)_leWidget_HasFocus,
      .setFocus = (void*)_leWidget_SetFocus,
      .invalidate = (void*)_leWidget_Invalidate,
      .invalidateContents = (void*)_leWidget_InvalidateContents,
      .installEventFilter = (void*)_leWidget_InstallEventFilter,
      .removeEventFilter = (void*)_leWidget_RemoveEventFilter,
  
      .update = (void*)_leWidget_Update,
  
      .touchDownEvent = (void*)_leWidget_TouchDownEvent,
      .touchUpEvent = (void*)_leWidget_TouchUpEvent,
      .touchMoveEvent = (void*)_leWidget_TouchMoveEvent,
      .moveEvent = (void*)_leWidget_MoveEvent,
      .resizeEvent = (void*)_leWidget_ResizeEvent,
      .focusLostEvent = (void*)_leWidget_FocusLostEvent,
      .focusGainedEvent = (void*)_leWidget_FocusGainedEvent,
  
      ._handleEvent = (void*)_leWidget_HandleEvent,
      ._validateChildren = (void*)_leWidget_ValidateChildren,
      ._increaseDirtyState = (void*)_leWidget_IncreaseDirtyState,
      ._setDirtyState = (void*)_leWidget_SetDirtyState,
      ._clearDirtyState = (void*)_leWidget_ClearDirtyState,
      ._invalidateBorderAreas = (void*)_leWidget_InvalidateBorderAreas,
      ._damageArea = (void*)_leWidget_DamageArea,
  
      /* overrides from base class */
      ._destructor = _leBarGraphWidget_Destructor,
      ._paint = _leBarGraphWidget_Paint,
      .languageChangeEvent = handleLanguageChangeEvent,
  
      /* member functions */
      .getTickLength = getTickLength,
      .setTickLength = setTickLength,
      .getMinValue = getMinValue,
      .setMinValue = setMinValue,
      .getMaxValue = getMaxValue,
      .setMaxValue = setMaxValue,
      .getValueAxisLabelsVisible = getValueAxisLabelsVisible,
      .setValueAxisLabelsVisible = setValueAxisLabelsVisible,
      .getValueAxisTicksVisible = getValueAxisTicksVisible,
      .setValueAxisTicksVisible = setValueAxisTicksVisible,
      .getValueAxisTicksInterval = getValueAxisTicksInterval,
      .setValueAxisTicksInterval = setValueAxisTicksInterval,
      .getValueAxisTicksPosition = getValueAxisTicksPosition,
      .setValueAxisTicksPosition = setValueAxisTicksPosition,
      .getValueAxisSubticksVisible = getValueAxisSubticksVisible,
      .setValueAxisSubticksVisible = setValueAxisSubticksVisible,
      .getValueAxisSubticksInterval = getValueAxisSubticksInterval,
      .setValueAxisSubticksInterval = setValueAxisSubticksInterval,
      .getValueAxisSubticksPosition = getValueAxisSubticksPosition,
      .setValueAxisSubticksPosition = setValueAxisSubticksPosition,
      .getCategoryAxisTicksVisible = getCategoryAxisTicksVisible,
      .setCategoryAxisTicksVisible = setCategoryAxisTicksVisible,
      .getCategoryAxisTicksPosition = getCategoryAxisTicksPosition,
      .setCategoryAxisTicksPosition = setCategoryAxisTicksPosition,
      .getCategoryAxisLabelsVisible = getCategoryAxisLabelsVisible,
      .setCategoryAxisLabelsVisible = setCategoryAxisLabelsVisible,
      .addSeries = addSeries,
      .getSeriesScheme = getSeriesScheme,
      .setSeriesScheme = setSeriesScheme,
      .addDataToSeries = addDataToSeries,
      .setDataInSeries = setDataInSeries,
      .clearData = clearData,
      .getTicksLabelFont = getTicksLabelFont,
      .setTicksLabelFont = setTicksLabelFont,
      .getGridLinesVisible = getGridLinesVisible,
      .setGridLinesVisible = setGridLinesVisible,
      .addCategory = addCategory,
      .getCategoryString = getCategoryString,
      .setCategoryString = setCategoryString,
      .getStacked = getStacked,
      .setStacked = setStacked,
      .getFillGraphArea = getFillGraphArea,
      .setFillGraphArea = setFillGraphArea,
  };
  #endif
  
  #endif // LE_BARGRAPH_WIDGET_ENABLED
  