#include "combo.h"
#include "combo.inl"
#include "component.inl"
#include "gui.inl"
#include <draw2d/font.h>
#include <draw2d/guictx.h>
#include <draw2d/image.h>
#include <geom2d/s2d.h>
#include <geom2d/v2d.h>
#include <core/arrpt.h>
#include <core/event.h>
#include <core/objh.h>
#include <core/strings.h>
#include <sewer/cassert.h>
#include <sewer/ptr.h>
struct _combo_t
{
GuiComponent component;
S2Df size;
bool_t duplicates;
bool_t is_focused;
bool_t is_placeholder_active;
ResId ttipid;
ResId placid;
String *placeholder;
String *text;
ArrPt(String) *texts;
ArrPt(Image) *images;
Font *font;
Font *placeholder_font;
uint32_t color;
uint32_t focus_color;
uint32_t bg_color;
uint32_t bg_focus_color;
uint32_t placeholder_color;
Listener *OnFilter;
Listener *OnChange;
Listener *OnSelect;
};
static void i_update_placeholder(Combo *combo);
void _combo_destroy(Combo **combo)
{
cassert_no_null(combo);
cassert_no_null(*combo);
_component_destroy_imp(&(*combo)->component);
listener_destroy(&(*combo)->OnFilter);
listener_destroy(&(*combo)->OnChange);
listener_destroy(&(*combo)->OnSelect);
ptr_destopt(str_destroy, &(*combo)->placeholder, String);
str_destroy(&(*combo)->text);
arrpt_destroy(&(*combo)->texts, str_destroy, String);
arrpt_destroy(&(*combo)->images, image_destroy, Image);
font_destroy(&(*combo)->font);
ptr_destopt(font_destroy, &(*combo)->placeholder_font, Font);
obj_delete(combo, Combo);
}
static void i_OnFilter(Combo *combo, Event *event)
{
cassert_no_null(combo);
if (combo->OnFilter != NULL)
listener_pass_event(combo->OnFilter, event, combo, Combo);
}
static void i_OnChange(Combo *combo, Event *event)
{
const EvText *params = event_params(event, EvText);
cassert_no_null(combo);
cassert_no_null(params);
cassert(event_type(event) == ekGUI_EVENT_TXTCHANGE);
cassert(combo->component.ositem == event_sender_imp(event, NULL));
str_upd(&combo->text, params->text);
if (combo->OnChange != NULL)
listener_pass_event(combo->OnChange, event, combo, Combo);
}
static void i_OnFocus(Combo *combo, Event *event)
{
const bool_t *params = event_params(event, bool_t);
cassert_no_null(combo);
cassert_no_null(params);
if (*params == TRUE)
{
if (combo->focus_color != UINT32_MAX || combo->placeholder_color != UINT32_MAX)
combo->component.context->func_combo_set_text_color(combo->component.ositem, combo->focus_color);
if (combo->bg_focus_color != UINT32_MAX)
combo->component.context->func_combo_set_bg_color(combo->component.ositem, combo->bg_focus_color);
}
else
{
if (combo->focus_color != UINT32_MAX)
combo->component.context->func_combo_set_text_color(combo->component.ositem, combo->color);
if (combo->bg_focus_color != UINT32_MAX)
combo->component.context->func_combo_set_bg_color(combo->component.ositem, combo->bg_color);
}
combo->is_focused = *params;
i_update_placeholder(combo);
}
Combo *combo_create(void)
{
const GuiCtx *context = guictx_get_current();
Combo *combo = obj_new0(Combo);
void *ositem = NULL;
combo->text = str_c("");
combo->font = _gui_create_default_font();
combo->texts = arrpt_create(String);
combo->images = arrpt_create(Image);
combo->color = UINT32_MAX;
combo->focus_color = UINT32_MAX;
combo->bg_color = UINT32_MAX;
combo->bg_focus_color = UINT32_MAX;
combo->placeholder_color = UINT32_MAX;
ositem = context->func_create[ekGUI_TYPE_COMBOBOX](ekCOMBO_FLAG);
context->func_combo_set_font(ositem, combo->font);
_component_init(&combo->component, context, PARAM(type, ekGUI_TYPE_COMBOBOX), &ositem);
context->func_combo_OnFilter(combo->component.ositem, obj_listener(combo, i_OnFilter, Combo));
context->func_combo_OnChange(combo->component.ositem, obj_listener(combo, i_OnChange, Combo));
return combo;
}
void combo_OnFilter(Combo *combo, Listener *listener)
{
cassert_no_null(combo);
listener_update(&combo->OnFilter, listener);
}
void combo_OnChange(Combo *combo, Listener *listener)
{
cassert_no_null(combo);
listener_update(&combo->OnChange, listener);
}
static void i_update_placeholder(Combo *combo)
{
cassert_no_null(combo);
if (combo->placeholder != NULL && combo->is_focused == FALSE && str_equ(combo->text, "") == TRUE)
{
if (_gui_effective_alt_font(combo->font, combo->placeholder_font) == TRUE)
combo->component.context->func_combo_set_font(combo->component.ositem, combo->placeholder_font);
if (combo->placeholder_color != UINT32_MAX)
combo->component.context->func_combo_set_text_color(combo->component.ositem, combo->placeholder_color);
combo->component.context->func_combo_set_text(combo->component.ositem, tc(combo->placeholder));
combo->is_placeholder_active = TRUE;
}
else if (combo->is_placeholder_active == TRUE)
{
if (_gui_effective_alt_font(combo->font, combo->placeholder_font) == TRUE)
combo->component.context->func_combo_set_font(combo->component.ositem, combo->font);
if (combo->placeholder_color != UINT32_MAX)
{
if (combo->is_focused == TRUE && combo->focus_color != UINT32_MAX)
combo->component.context->func_combo_set_text_color(combo->component.ositem, combo->focus_color);
else
combo->component.context->func_combo_set_text_color(combo->component.ositem, combo->color);
}
combo->component.context->func_combo_set_text(combo->component.ositem, tc(combo->text));
combo->is_placeholder_active = FALSE;
}
}
void combo_text(Combo *combo, const char_t *text)
{
cassert_no_null(combo);
str_upd(&combo->text, text);
combo->component.context->func_combo_set_text(combo->component.ositem, text);
i_update_placeholder(combo);
}
void combo_align(Combo *combo, const align_t align)
{
unref(combo);
unref(align);
cassert(FALSE);
}
void combo_duplicates(Combo *combo, const bool_t duplicates)
{
cassert_no_null(combo);
combo->duplicates = duplicates;
}
void combo_tooltip(Combo *combo, const char_t *text)
{
const char_t *ltext = NULL;
cassert_no_null(combo);
if (text != NULL)
ltext = _gui_respack_text(text, &combo->ttipid);
combo->component.context->func_set_tooltip[ekGUI_TYPE_COMBOBOX](combo->component.ositem, ltext);
}
void combo_color(Combo *combo, const color_t color)
{
cassert_no_null(combo);
combo->color = color;
combo->component.context->func_combo_set_text_color(combo->component.ositem, color);
}
static void i_update_focus_listener(Combo *combo)
{
cassert_no_null(combo);
if (combo->focus_color != UINT32_MAX || combo->bg_focus_color != UINT32_MAX || combo->placeholder != NULL)
{
combo->component.context->func_combo_OnFocus(combo->component.ositem, obj_listener(combo, i_OnFocus, Combo));
}
else
{
combo->component.context->func_combo_OnFocus(combo->component.ositem, NULL);
}
}
void combo_color_focus(Combo *combo, const color_t color)
{
cassert_no_null(combo);
combo->focus_color = color;
i_update_focus_listener(combo);
}
void combo_bgcolor(Combo *combo, const color_t color)
{
cassert_no_null(combo);
combo->bg_color = color;
combo->component.context->func_combo_set_bg_color(combo->component.ositem, color);
}
void combo_bgcolor_focus(Combo *combo, const color_t color)
{
cassert_no_null(combo);
combo->bg_focus_color = color;
i_update_focus_listener(combo);
if (combo->is_focused)
combo->component.context->func_combo_set_bg_color(combo->component.ositem, color);
}
void combo_phtext(Combo *combo, const char_t *text)
{
const char_t *ltext = NULL;
cassert_no_null(combo);
if (text != NULL)
ltext = _gui_respack_text(text, &combo->placid);
str_upd(&combo->placeholder, ltext);
i_update_focus_listener(combo);
i_update_placeholder(combo);
}
void combo_phcolor(Combo *combo, const color_t color)
{
cassert_no_null(combo);
combo->placeholder_color = color;
i_update_placeholder(combo);
}
void combo_phstyle(Combo *combo, const uint32_t fstyle)
{
cassert_no_null(combo);
ptr_destopt(font_destroy, &combo->placeholder_font, Font);
if (fstyle != font_style(combo->font))
combo->placeholder_font = font_with_style(combo->font, fstyle);
i_update_placeholder(combo);
}
const char_t *combo_get_text(const Combo *combo, const uint32_t index)
{
cassert_no_null(combo);
if (index != UINT32_MAX)
{
const String *str = arrpt_get(combo->texts, index, String);
return tc(str);
}
else
{
return tc(combo->text);
}
}
static void i_delete_duplicates(GuiComponent *component, ArrPt(String) *texts, ArrPt(Image) *images, const char_t *text)
{
uint32_t i = 0, num_elems = 0;
cassert_no_null(component);
num_elems = arrpt_size(texts, String);
cassert(num_elems == arrpt_size(images, Image));
for (i = 0; i < num_elems;)
{
const String *str = arrpt_get(texts, i, String);
if (str_str(text, tc(str)) != NULL)
{
arrpt_delete(texts, i, str_destroy, String);
arrpt_delete(images, i, image_destroy, Image);
component->context->func_combo_set_elem(component->ositem, ekCTRL_OP_DEL, i, NULL, NULL);
num_elems--;
}
else
{
i++;
}
}
}
static bool_t i_exists_substring(ArrPt(String) *texts, const char_t *subtext)
{
arrpt_foreach(text, texts, String)
if (str_str(tc(text), subtext) != NULL)
return TRUE;
arrpt_end()
return FALSE;
}
uint32_t combo_count(const Combo *combo)
{
uint32_t num_elems = 0;
cassert_no_null(combo);
num_elems = arrpt_size(combo->texts, String);
cassert(num_elems == arrpt_size(combo->images, Image));
return num_elems;
}
void combo_add_elem(Combo *combo, const char_t *text, const Image *image)
{
bool_t exists = FALSE;
cassert_no_null(combo);
if (combo->duplicates == FALSE)
{
i_delete_duplicates(&combo->component, combo->texts, combo->images, text);
exists = i_exists_substring(combo->texts, text);
}
if (exists == FALSE)
{
String *ltext = NULL;
const Image *limage = NULL;
Image *cimage;
ltext = str_c(text);
limage = _gui_respack_image((ResId)image, NULL);
cimage = limage ? image_copy(limage) : NULL;
arrpt_append(combo->texts, ltext, String);
arrpt_append(combo->images, cimage, Image);
combo->component.context->func_combo_set_elem(combo->component.ositem, ekCTRL_OP_ADD, UINT32_MAX, text, cimage);
}
}
void combo_set_elem(Combo *combo, const uint32_t index, const char_t *text, const Image *image)
{
String **ltext = NULL;
Image **limage = NULL;
cassert_no_null(combo);
cassert(index < arrpt_size(combo->texts, String));
ltext = arrpt_all(combo->texts, String) + index;
limage = arrpt_all(combo->images, Image) + index;
str_destroy(ltext);
ptr_destopt(image_destroy, limage, Image);
*ltext = str_c(text);
*limage = ptr_copyopt(image_copy, image, Image);
combo->component.context->func_combo_set_elem(combo->component.ositem, ekCTRL_OP_SET, index, text, image);
}
void combo_ins_elem(Combo *combo, const uint32_t index, const char_t *text, const Image *image)
{
bool_t exists = FALSE;
cassert_no_null(combo);
cassert(index <= arrpt_size(combo->texts, String));
if (combo->duplicates == FALSE)
{
i_delete_duplicates(&combo->component, combo->texts, combo->images, text);
exists = i_exists_substring(combo->texts, text);
}
if (exists == FALSE)
{
String *ltext = NULL;
Image *limage = NULL;
ctrl_op_t op;
uint32_t lindex;
ltext = str_c(text);
limage = ptr_copyopt(image_copy, image, Image);
if (index < arrpt_size(combo->texts, String))
{
op = ekCTRL_OP_INS;
lindex = index;
arrpt_insert(combo->texts, index, ltext, String);
arrpt_insert(combo->images, index, limage, Image);
}
else
{
op = ekCTRL_OP_ADD;
lindex = UINT32_MAX;
arrpt_append(combo->texts, ltext, String);
arrpt_append(combo->images, limage, Image);
}
combo->component.context->func_combo_set_elem(combo->component.ositem, op, lindex, text, image);
}
}
void combo_del_elem(Combo *combo, const uint32_t index)
{
cassert_no_null(combo);
arrpt_delete(combo->texts, index, str_destroy, String);
arrpt_delete(combo->images, index, image_destroy, Image);
combo->component.context->func_combo_set_elem(combo->component.ositem, ekCTRL_OP_DEL, index, NULL, NULL);
}
void _combo_natural(Combo *combo, const uint32_t i, real32_t *dim0, real32_t *dim1)
{
cassert_no_null(combo);
cassert_no_null(dim0);
cassert_no_null(dim1);
if (i == 0)
{
combo->component.context->func_combo_bounds(combo->component.ositem, 100.f, &combo->size.width, &combo->size.height);
*dim0 = combo->size.width;
}
else
{
cassert(i == 1);
*dim1 = combo->size.height;
}
}
void _combo_locale(Combo *combo)
{
cassert_no_null(combo);
if (combo->placid != NULL)
{
const char_t *text = _gui_respack_text(combo->placid, NULL);
str_upd(&combo->placeholder, text);
i_update_placeholder(combo);
}
if (combo->ttipid != NULL)
{
const char_t *text = _gui_respack_text(combo->ttipid, NULL);
combo->component.context->func_set_tooltip[ekGUI_TYPE_COMBOBOX](combo->component.ositem, text);
}
}