<commit_before>/**
 * @file lv_ddlist.c
 *
 */

/*********************
 *      INCLUDES
 *********************/
#include "lv_dropdown.h"
#if LV_USE_DROPDOWN != 0

#include "../lv_core/lv_debug.h"
#include "../lv_draw/lv_draw.h"
#include "../lv_core/lv_group.h"
#include "../lv_core/lv_indev.h"
#include "../lv_themes/lv_theme.h"
#include "../lv_font/lv_symbol_def.h"
#include "../lv_misc/lv_anim.h"
#include "../lv_misc/lv_math.h"
#include <string.h>

/*********************
 *      DEFINES
 *********************/
#define LV_OBJX_NAME "lv_dropdown"

#if LV_USE_ANIMATION == 0
    #undef LV_DROPDOWN_DEF_ANIM_TIME
    #define LV_DROPDOWN_DEF_ANIM_TIME 0 /*No animation*/
#endif

#define LV_DROPDOWN_PR_NONE 0xFFFF

/**********************
 *      TYPEDEFS
 **********************/
typedef struct {
    lv_page_ext_t page;
    lv_obj_t * ddlist;      /*Pointer to the ddlist where the page belongs*/
} lv_dropdown_page_ext_t;

/**********************
 *  STATIC PROTOTYPES
 **********************/
static lv_design_res_t lv_dropdown_design(lv_obj_t * ddlist, const lv_area_t * clip_area, lv_design_mode_t mode);
static lv_design_res_t lv_dropdown_page_design(lv_obj_t * ddlist, const lv_area_t * clip_area, lv_design_mode_t mode);
static lv_res_t lv_dropdown_signal(lv_obj_t * ddlist, lv_signal_t sign, void * param);
static lv_res_t lv_dropdown_page_signal(lv_obj_t * page, lv_signal_t sign, void * param);
static lv_res_t lv_dropdown_page_scrl_signal(lv_obj_t * scrl, lv_signal_t sign, void * param);
static lv_style_list_t * lv_dropdown_get_style(lv_obj_t * ddlist, uint8_t part);
static void draw_box(lv_obj_t * ddlist, const lv_area_t * clip_area, uint16_t id, lv_state_t state);
static void draw_box_label(lv_obj_t * ddlist, const lv_area_t * clip_area, uint16_t id, lv_state_t state);
static lv_res_t page_release_handler(lv_obj_t * page);
static void page_press_handler(lv_obj_t * page);
static uint16_t get_id_on_point(lv_obj_t * ddlist, lv_coord_t x, lv_coord_t y);
static void pos_selected(lv_obj_t * ddlist);
static lv_obj_t * get_label(const lv_obj_t * ddlist);
#if LV_USE_ANIMATION
    static void list_anim(void * p, lv_anim_value_t v);
    static void close_anim_ready(lv_anim_t * a);
#endif

/**********************
 *  STATIC VARIABLES
 **********************/
static lv_signal_cb_t ancestor_signal;
static lv_signal_cb_t ancestor_page_signal;
static lv_signal_cb_t ancestor_page_scrl_signal;
static lv_design_cb_t ancestor_design;
static lv_design_cb_t ancestor_page_design;

/**********************
 *      MACROS
 **********************/

/**********************
 *   GLOBAL FUNCTIONS
 **********************/

/**
 * Create a drop down list objects
 * @param par pointer to an object, it will be the parent of the new drop down list
 * @param copy pointer to a drop down list object, if not NULL then the new object will be copied
 * from it
 * @return pointer to the created drop down list
 */
lv_obj_t * lv_dropdown_create(lv_obj_t * par, const lv_obj_t * copy)
{
    LV_LOG_TRACE("drop down list create started");

    /*Create the ancestor drop down list*/
    lv_obj_t * ddlist = lv_btn_create(par, copy);
    LV_ASSERT_MEM(ddlist);
    if(ddlist == NULL) return NULL;

    if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_cb(ddlist);
    if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_cb(ddlist);

    /*Allocate the drop down list type specific extended data*/
    lv_dropdown_ext_t * ext = lv_obj_allocate_ext_attr(ddlist, sizeof(lv_dropdown_ext_t));
    LV_ASSERT_MEM(ext);
    if(ext == NULL) {
        lv_obj_del(ddlist);
        return NULL;
    }

    /*Initialize the allocated 'ext' */
    ext->page          = NULL;
    ext->options     = NULL;
    ext->symbol         = LV_SYMBOL_DOWN;
    ext->text         = "Select";
    ext->show_selected   = 1;
    ext->sel_opt_id      = 0;
    ext->sel_opt_id_orig = 0;
    ext->pr_opt_id = LV_DROPDOWN_PR_NONE;
    ext->option_cnt      = 0;
    ext->dir = LV_DROPDOWN_DIR_DOWN;
    ext->max_height = LV_DPI * 2;
    ext->anim_time = LV_DROPDOWN_DEF_ANIM_TIME;
    lv_style_list_init(&ext->style_page);
    lv_style_list_init(&ext->style_scrlbar);
    lv_style_list_init(&ext->style_selected);

    /*The signal and design functions are not copied so set them here*/
    lv_obj_set_signal_cb(ddlist, lv_dropdown_signal);
    lv_obj_set_design_cb(ddlist, lv_dropdown_design);

    /*Init the new drop down list drop down list*/
    if(copy == NULL) {
        lv_dropdown_set_options(ddlist, "Option 1\nOption 2\nOption 3");

        lv_theme_apply(ddlist, LV_THEME_DROPDOWN);
    }
    /*Copy an existing drop down list*/
    else {
        lv_dropdown_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
        ext->options        = copy_ext->options;
        ext->option_cnt        = copy_ext->option_cnt;
        ext->sel_opt_id     = copy_ext->sel_opt_id;
        ext->sel_opt_id_orig = copy_ext->sel_opt_id;
        ext->symbol           = copy_ext->symbol;
        ext->max_height      = copy_ext->max_height;
        ext->anim_time      = copy_ext->anim_time;
        ext->text      = copy_ext->text;
        ext->dir      = copy_ext->dir;
        ext->show_selected      = copy_ext->show_selected;
        lv_style_list_copy(&ext->style_page, &copy_ext->style_page);
        lv_style_list_copy(&ext->style_selected, &copy_ext->style_selected);
        lv_style_list_copy(&ext->style_scrlbar, &copy_ext->style_scrlbar);
    }

    LV_LOG_INFO("drop down list created");

    return ddlist;
}

/*=====================
 * Setter functions
 *====================*/

/**
 * Set text of the ddlist (Displayed on the button if `show_selected = false`)
 * @param ddlist pointer to a drop down list object
 * @param txt the text as a string (Only it's pointer is saved)
 */
void lv_dropdown_set_text(lv_obj_t * ddlist, const char * txt)
{
    LV_ASSERT_OBJ(ddlist, LV_OBJX_NAME);
    lv_dropdown_ext_t * ext = lv_obj_get_ext_attr(ddlist);
    if(ext->text == txt) return;

    ext->text = txt;

    lv_obj_invalidate(ddlist);
}

/**
 * Set the options in a drop down list from a string
 * @param ddlist pointer to drop down list object
 * @param options a string with '\n' separated options. E.g. "One\nTwo\nThree"
 */
void lv_dropdown_set_options(lv_obj_t * ddlist, const char * options)
{
    LV_ASSERT_OBJ(ddlist, LV_OBJX_NAME);
    LV_ASSERT_STR(options);

    lv_dropdown_ext_t * ext = lv_obj_get_ext_attr(ddlist);

    /*Count the '\n'-s to determine the number of options*/
    ext->option_cnt = 0;
    uint16_t i;
    for(i = 0; options[i] != '\0'; i++) {
        if(options[i] == '\n') ext->option_cnt++;
    }
    ext->option_cnt++;   /*Last option has no `\n`*/
    ext->sel_opt_id      = 0;
    ext->sel_opt_id_orig = 0;
    ext->options = options;
}

/**
 * Set the selected option
 * @param ddlist pointer to drop down list object
 * @param sel_opt id of the selected option (0 ... number of option - 1);
 */
void lv_dropdown_set_selected(lv_obj_t * ddlist, uint16_t sel_opt)
{
    LV_ASSERT_OBJ(ddlist, LV_OBJX_NAME);

    lv_dropdown_ext_t * ext = lv_obj_get_ext_attr(ddlist);
    if(ext->sel_opt_id == sel_opt) return;

    ext->sel_opt_id      = sel_opt < ext->option_cnt ? sel_opt : ext->option_cnt - 1;
    ext->sel_opt_id_orig = ext->sel_opt_id;
    /*Move the list to show the current option*/
    if(ext->page != NULL) {
        lv_obj_invalidate(ddlist);<commit_msg>Changed options to dynamic allocation<commit_after>/**
 * @file lv_ddlist.c
 *
 */

/*********************
 *      INCLUDES
 *********************/
#include "lv_dropdown.h"
#if LV_USE_DROPDOWN != 0

#include "../lv_core/lv_debug.h"
#include "../lv_draw/lv_draw.h"
#include "../lv_core/lv_group.h"
#include "../lv_core/lv_indev.h"
#include "../lv_themes/lv_theme.h"
#include "../lv_font/lv_symbol_def.h"
#include "../lv_misc/lv_anim.h"
#include "../lv_misc/lv_math.h"
#include <string.h>

/*********************
 *      DEFINES
 *********************/
#define LV_OBJX_NAME "lv_dropdown"

#if LV_USE_ANIMATION == 0
    #undef LV_DROPDOWN_DEF_ANIM_TIME
    #define LV_DROPDOWN_DEF_ANIM_TIME 0 /*No animation*/
#endif

#define LV_DROPDOWN_PR_NONE 0xFFFF

/**********************
 *      TYPEDEFS
 **********************/
typedef struct {
    lv_page_ext_t page;
    lv_obj_t * ddlist;      /*Pointer to the ddlist where the page belongs*/
} lv_dropdown_page_ext_t;

/**********************
 *  STATIC PROTOTYPES
 **********************/
static lv_design_res_t lv_dropdown_design(lv_obj_t * ddlist, const lv_area_t * clip_area, lv_design_mode_t mode);
static lv_design_res_t lv_dropdown_page_design(lv_obj_t * ddlist, const lv_area_t * clip_area, lv_design_mode_t mode);
static lv_res_t lv_dropdown_signal(lv_obj_t * ddlist, lv_signal_t sign, void * param);
static lv_res_t lv_dropdown_page_signal(lv_obj_t * page, lv_signal_t sign, void * param);
static lv_res_t lv_dropdown_page_scrl_signal(lv_obj_t * scrl, lv_signal_t sign, void * param);
static lv_style_list_t * lv_dropdown_get_style(lv_obj_t * ddlist, uint8_t part);
static void draw_box(lv_obj_t * ddlist, const lv_area_t * clip_area, uint16_t id, lv_state_t state);
static void draw_box_label(lv_obj_t * ddlist, const lv_area_t * clip_area, uint16_t id, lv_state_t state);
static lv_res_t page_release_handler(lv_obj_t * page);
static void page_press_handler(lv_obj_t * page);
static uint16_t get_id_on_point(lv_obj_t * ddlist, lv_coord_t x, lv_coord_t y);
static void pos_selected(lv_obj_t * ddlist);
static lv_obj_t * get_label(const lv_obj_t * ddlist);
#if LV_USE_ANIMATION
    static void list_anim(void * p, lv_anim_value_t v);
    static void close_anim_ready(lv_anim_t * a);
#endif

/**********************
 *  STATIC VARIABLES
 **********************/
static lv_signal_cb_t ancestor_signal;
static lv_signal_cb_t ancestor_page_signal;
static lv_signal_cb_t ancestor_page_scrl_signal;
static lv_design_cb_t ancestor_design;
static lv_design_cb_t ancestor_page_design;

/**********************
 *      MACROS
 **********************/

/**********************
 *   GLOBAL FUNCTIONS
 **********************/

/**
 * Create a drop down list objects
 * @param par pointer to an object, it will be the parent of the new drop down list
 * @param copy pointer to a drop down list object, if not NULL then the new object will be copied
 * from it
 * @return pointer to the created drop down list
 */
lv_obj_t * lv_dropdown_create(lv_obj_t * par, const lv_obj_t * copy)
{
    LV_LOG_TRACE("drop down list create started");

    /*Create the ancestor drop down list*/
    lv_obj_t * ddlist = lv_btn_create(par, copy);
    LV_ASSERT_MEM(ddlist);
    if(ddlist == NULL) return NULL;

    if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_cb(ddlist);
    if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_cb(ddlist);

    /*Allocate the drop down list type specific extended data*/
    lv_dropdown_ext_t * ext = lv_obj_allocate_ext_attr(ddlist, sizeof(lv_dropdown_ext_t));
    LV_ASSERT_MEM(ext);
    if(ext == NULL) {
        lv_obj_del(ddlist);
        return NULL;
    }

    /*Initialize the allocated 'ext' */
    ext->page          = NULL;
    ext->options     = NULL;
    ext->symbol         = LV_SYMBOL_DOWN;
    ext->text         = "Select";
    ext->static_txt = 0;
    ext->show_selected   = 1;
    ext->sel_opt_id      = 0;
    ext->sel_opt_id_orig = 0;
    ext->pr_opt_id = LV_DROPDOWN_PR_NONE;
    ext->option_cnt      = 0;
    ext->dir = LV_DROPDOWN_DIR_DOWN;
    ext->max_height = LV_DPI * 2;
    ext->anim_time = LV_DROPDOWN_DEF_ANIM_TIME;
    lv_style_list_init(&ext->style_page);
    lv_style_list_init(&ext->style_scrlbar);
    lv_style_list_init(&ext->style_selected);

    /*The signal and design functions are not copied so set them here*/
    lv_obj_set_signal_cb(ddlist, lv_dropdown_signal);
    lv_obj_set_design_cb(ddlist, lv_dropdown_design);

    /*Init the new drop down list drop down list*/
    if(copy == NULL) {
        ext->options = NULL;

        lv_theme_apply(ddlist, LV_THEME_DROPDOWN);
    }
    /*Copy an existing drop down list*/
    else {
        lv_dropdown_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
        if(copy_ext->static_txt == 0)
            lv_dropdown_set_options(ddlist, lv_dropdown_get_options(copy));
		else
		    lv_dropdown_set_static_options(ddlist, lv_dropdown_get_options(copy));
        ext->option_cnt        = copy_ext->option_cnt;
        ext->sel_opt_id     = copy_ext->sel_opt_id;
        ext->sel_opt_id_orig = copy_ext->sel_opt_id;
        ext->symbol           = copy_ext->symbol;
        ext->max_height      = copy_ext->max_height;
        ext->anim_time      = copy_ext->anim_time;
        ext->text      = copy_ext->text;
        ext->dir      = copy_ext->dir;
        ext->show_selected      = copy_ext->show_selected;
        lv_style_list_copy(&ext->style_page, &copy_ext->style_page);
        lv_style_list_copy(&ext->style_selected, &copy_ext->style_selected);
        lv_style_list_copy(&ext->style_scrlbar, &copy_ext->style_scrlbar);
    }

    LV_LOG_INFO("drop down list created");

    return ddlist;
}

/*=====================
 * Setter functions
 *====================*/

/**
 * Set text of the ddlist (Displayed on the button if `show_selected = false`)
 * @param ddlist pointer to a drop down list object
 * @param txt the text as a string (Only it's pointer is saved)
 */
void lv_dropdown_set_text(lv_obj_t * ddlist, const char * txt)
{
    LV_ASSERT_OBJ(ddlist, LV_OBJX_NAME);
    lv_dropdown_ext_t * ext = lv_obj_get_ext_attr(ddlist);
    if(ext->text == txt) return;

    ext->text = txt;

    lv_obj_invalidate(ddlist);
}

/**
 * Set the options in a drop down list from a string
 * @param ddlist pointer to drop down list object
 * @param options a string with '\n' separated options. E.g. "One\nTwo\nThree"
 * The options string can be destroyed after calling this function
 */
void lv_dropdown_set_options(lv_obj_t * ddlist, const char * options)
{
    LV_ASSERT_OBJ(ddlist, LV_OBJX_NAME);
    LV_ASSERT_STR(options);

    lv_dropdown_ext_t * ext = lv_obj_get_ext_attr(ddlist);

    /*Count the '\n'-s to determine the number of options*/
    ext->option_cnt = 0;
    uint16_t i;
    for(i = 0; options[i] != '\0'; i++) {
        if(options[i] == '\n') ext->option_cnt++;
    }
    ext->option_cnt++;   /*Last option has no `\n`*/
    ext->sel_opt_id      = 0;
    ext->sel_opt_id_orig = 0;
    
    /*Allocate space for the new text*/
    size_t len = strlen(options) + 1;
    if(ext->options != NULL && ext->static_txt == 0) {
        lv_mem_free(ext->options);
        ext->options = NULL;
    }

    ext->options = lv_mem_alloc(len);

    LV_ASSERT_MEM(ext->options);
    if(ext->options == NULL) return;

    strcpy(ext->options, options);

    /*Now the text is dynamically allocated*/
    ext->static_txt = 0;
}

/**
 * Set the options in a drop down list from a string
 * @param ddlist pointer to drop down list object
 * @param options a staic string with '\n' separated options. E.g. "One\nTwo\nThree"
 */
void lv_dropdown_set_static_options(lv_obj_t * ddlist, const char * options)
{
    LV_ASSERT_OBJ(ddlist, LV_OBJX_NAME);
    LV_ASSERT_STR(options);

    lv_dropdown_ext_t * ext = lv_obj_get_ext_attr(ddlist);

    /*Count the '\n'-s to determine the number of options*/
    ext->option_cnt = 0;
    uint16_t i;
    for(i = 0; options[i] != '\0'; i++) {
        if(options[i] == '\n') ext->option_cnt++;
    }
    ext->option_cnt++;   /*Last option has no `\n`*/
    ext->sel_opt_id      = 0;
    ext->sel_opt_id_orig = 0;

    if(ext->static_txt == 0 && ext->options != NULL) {
        lv_mem_free(ext->options);
        ext->options = NULL;
    }

    ext->static_txt = 1;
    ext->options = (char *)options;
}

/**
 * Add an options to a drop down list from a string.  Only works for dynamic options.
 * @param ddlist pointer to drop down list object
 * @param options a string without '\n'. E.g. "Four"
 * @param position the insert position, indexed from 0, -1 = end of string
 */
void lv_dropdown_add_option(lv_obj_t * ddlist, const char * option, int pos)
{
    LV_ASSERT_OBJ(ddlist, LV_OBJX_NAME);
    LV_ASSERT_STR(option);

    lv_dropdown_ext_t * ext = lv_obj_get_ext_attr(ddlist);

    /*Can not append to static options*/
    if(ext->static_txt != 0) return;

    /*Allocate space for the new option*/
    size_t old_len = (ext->options == NULL) ? 0 : strlen(ext->options);
    size_t ins_len = strlen(option);
    size_t new_len = ins_len + old_len + 1; // +1 for \n
    ext->options        = lv_mem_realloc(ext->options, new_len + 1);
    LV_ASSERT_MEM(ext->options);
    if(ext->options == NULL) return;

    ext->options[old_len] = 0;

    /*Find the insert character position*/
    int charpos;
    if(pos == LV_DROPDOWN_POS_LAST) {
        charpos = 0;
        if(old_len > 0) {
            strcat(ext->options, "\n");
            charpos = old_len + 1;
        }
    }
    else {
        int opcnt = 0;
        for(charpos = 0; ext->options[charpos] != 0; charpos++) {
            if(opcnt == pos)
                break;
            if(ext->options[charpos] == '\n')
                opcnt++;
        }
    }

    /*Insert the option, adding \n if necessary*/
    char * ins_buf = lv_mem_buf_get(ins_len + 1);
    LV_ASSERT_MEM(ins_buf);
    if(ins_buf == NULL) return;
    strcpy(ins_buf, option);
    if(pos < ext->option_cnt)
        strcat(ins_buf, "\n");
    lv_txt_ins(ext->options, charpos, ins_buf);
    lv_mem_buf_release(ins_buf);

    ext->option_cnt++;
}

/**
 * Set the selected option
 * @param ddlist pointer to drop down list object
 * @param sel_opt id of the selected option (0 ... number of option - 1);
 */
void lv_dropdown_set_selected(lv_obj_t * ddlist, uint16_t sel_opt)
{
    LV_ASSERT_OBJ(ddlist, LV_OBJX_NAME);

    lv_dropdown_ext_t * ext = lv_obj_get_ext_attr(ddlist);
    if(ext->sel_opt_id == sel_opt) return;

    ext->sel_opt_id      = sel_opt < ext->option_cnt ? sel_opt : ext->option_cnt - 1;
    ext->sel_opt_id_orig = ext->sel_opt_id;
    /*Move the list to show the current option*/
    if(ext->page != NULL) {
        lv_obj_invalidate(ddlist);
{'fraction_non_alphanumeric': 0.14679862877363706, 'fraction_numerical': 0.004865641932986841, 'mean_word_length': 3.504856787048568, 'pattern_counts': {'":': 0, '<': 8, '<?xml version=': 0, '>': 152, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1527316', 'n_tokens_mistral': 6773, 'n_tokens_neox': 6445, 'n_words': 1464}