+ * Filter inserted text based on user defined character and byte limits
+ *
+ * Add this filter to an entry to limit the characters that it will accept
+ * based the the contents of the provided Elm_Entry_Filter_Limit_Size.
+ * The funtion works on the UTF-8 representation of the string, converting
+ * it from the set markup, thus not accounting for any format in it.
+ *
+ * The user must create an Elm_Entry_Filter_Limit_Size structure and pass
+ * it as data when setting the filter. In it it's possible to set limits
+ * by character count or bytes (any of them is disabled if 0), and both can
+ * be set at the same time. In that case, it first checks for characters,
+ * then bytes.
+ *
+ * The function will cut the inserted text in order to allow only the first
+ * number of characters that are still allowed. The cut is made in
+ * characters, even when limiting by bytes, in order to always contain
+ * valid ones and avoid half unicode characters making it in.
+ *
+ * @ingroup Entry
+ */
+EAPI void
+elm_entry_filter_limit_size(void *data, Evas_Object *entry, char **text)
+{
+ Elm_Entry_Filter_Limit_Size *lim = data;
+ char *current;
+ int len, newlen;
+ const char *(*text_get)(const Evas_Object *);
+ const char *widget_type;
+
+ EINA_SAFETY_ON_NULL_RETURN(data);
+ EINA_SAFETY_ON_NULL_RETURN(entry);
+ EINA_SAFETY_ON_NULL_RETURN(text);
+
+ /* hack. I don't want to copy the entire function to work with
+ * scrolled_entry */
+ widget_type = elm_widget_type_get(entry);
+ if (!strcmp(widget_type, "entry"))
+ text_get = elm_entry_entry_get;
+ else if (!strcmp(widget_type, "scrolled_entry"))
+ text_get = elm_scrolled_entry_entry_get;
+ else /* huh? */
+ return;
+
+ current = elm_entry_markup_to_utf8(text_get(entry));
+
+ if (lim->max_char_count > 0)
+ {
+ int cut;
+ len = evas_string_char_len_get(current);
+ if (len >= lim->max_char_count)
+ {
+ free(*text);
+ free(current);
+ *text = NULL;
+ return;
+ }
+ newlen = evas_string_char_len_get(*text);
+ cut = strlen(*text);
+ while ((len + newlen) > lim->max_char_count)
+ {
+ cut = evas_string_char_prev_get(*text, cut, NULL);
+ newlen--;
+ }
+ (*text)[cut] = 0;
+ }
+
+ if (lim->max_byte_count > 0)
+ {
+ len = strlen(current);
+ if (len >= lim->max_byte_count)
+ {
+ free(*text);
+ free(current);
+ *text = NULL;
+ return;
+ }
+ newlen = strlen(*text);
+ while ((len + newlen) > lim->max_byte_count)
+ {
+ int p = evas_string_char_prev_get(*text, newlen, NULL);
+ newlen -= (newlen - p);
+ }
+ if (newlen)
+ (*text)[newlen] = 0;
+ else
+ {
+ free(*text);
+ *text = NULL;
+ }
+ }
+ free(current);
+}
+
+/**
+ * Filter inserted text based on accepted or rejected sets of characters
+ *
+ * Add this filter to an entry to restrict the set of accepted characters
+ * based on the sets in the provided Elm_Entry_Filter_Accept_Set.
+ * This structure contains both accepted and rejected sets, but they are
+ * mutually exclusive. If accepted is set, it will be used, otherwise it
+ * goes on to the rejected set.
+ */
+EAPI void
+elm_entry_filter_accept_set(void *data, Evas_Object *entry __UNUSED__, char **text)
+{
+ Elm_Entry_Filter_Accept_Set *as = data;
+ const char *set;
+ char *insert;
+ Eina_Bool goes_in;
+ int read_idx, last_read_idx = 0, read_char;
+
+ EINA_SAFETY_ON_NULL_RETURN(data);
+ EINA_SAFETY_ON_NULL_RETURN(text);
+
+ if ((!as->accepted) && (!as->rejected))
+ return;
+
+ if (as->accepted)
+ {
+ set = as->accepted;
+ goes_in = EINA_TRUE;
+ }
+ else
+ {
+ set = as->rejected;
+ goes_in = EINA_FALSE;
+ }
+
+ insert = *text;
+ read_idx = evas_string_char_next_get(*text, 0, &read_char);
+ while (read_char)
+ {
+ int cmp_idx, cmp_char;
+ Eina_Bool in_set = EINA_FALSE;
+
+ cmp_idx = evas_string_char_next_get(set, 0, &cmp_char);
+ while (cmp_char)
+ {
+ if (read_char == cmp_char)
+ {
+ in_set = EINA_TRUE;
+ break;
+ }
+ cmp_idx = evas_string_char_next_get(set, cmp_idx, &cmp_char);
+ }
+ if (in_set == goes_in)
+ {
+ int size = read_idx - last_read_idx;
+ const char *src = (*text) + last_read_idx;
+ if (src != insert)
+ memcpy(insert, *text + last_read_idx, size);
+ insert += size;
+ }
+ last_read_idx = read_idx;
+ read_idx = evas_string_char_next_get(*text, read_idx, &read_char);
+ }
+ *insert = 0;
+}
+
+/**
+ * This sets the file (and implicitly loads it) for the text to display and
+ * then edit. All changes are written back to the file after a short delay if
+ * the entry object is set to autosave.
+ *
+ * @param obj The entry object
+ * @param file The path to the file to load and save
+ * @param format The file format
+ *
+ * @ingroup Entry
+ */
+EAPI void
+elm_entry_file_set(Evas_Object *obj, const char *file, Elm_Text_Format format)
+{
+ ELM_CHECK_WIDTYPE(obj, widtype);
+ Widget_Data *wd = elm_widget_data_get(obj);
+ if (!wd) return;
+ if (wd->delay_write)
+ {
+ ecore_timer_del(wd->delay_write);
+ wd->delay_write = NULL;
+ }
+ if (wd->autosave) _save(obj);
+ eina_stringshare_replace(&wd->file, file);
+ wd->format = format;
+ _load(obj);
+}
+
+/**
+ * Gets the file to load and save and the file format
+ *
+ * @param obj The entry object
+ * @param file The path to the file to load and save
+ * @param format The file format
+ *
+ * @ingroup Entry
+ */
+EAPI void
+elm_entry_file_get(const Evas_Object *obj, const char **file, Elm_Text_Format *format)
+{
+ ELM_CHECK_WIDTYPE(obj, widtype);
+ Widget_Data *wd = elm_widget_data_get(obj);
+ if (!wd) return;
+ if (file) *file = wd->file;
+ if (format) *format = wd->format;
+}
+
+/**
+ * This function writes any changes made to the file set with
+ * elm_entry_file_set()
+ *
+ * @param obj The entry object
+ *
+ * @ingroup Entry
+ */
+EAPI void
+elm_entry_file_save(Evas_Object *obj)
+{
+ ELM_CHECK_WIDTYPE(obj, widtype);
+ Widget_Data *wd = elm_widget_data_get(obj);
+ if (!wd) return;
+ if (wd->delay_write)
+ {
+ ecore_timer_del(wd->delay_write);
+ wd->delay_write = NULL;
+ }
+ _save(obj);
+ wd->delay_write = ecore_timer_add(2.0, _delay_write, obj);
+}
+
+/**
+ * This sets the entry object to 'autosave' the loaded text file or not.
+ *
+ * @param obj The entry object
+ * @param autosave Autosave the loaded file or not
+ *
+ * @ingroup Entry
+ */
+EAPI void
+elm_entry_autosave_set(Evas_Object *obj, Eina_Bool autosave)
+{
+ ELM_CHECK_WIDTYPE(obj, widtype);
+ Widget_Data *wd = elm_widget_data_get(obj);
+ if (!wd) return;
+ wd->autosave = !!autosave;
+}
+
+/**
+ * This gets the entry object's 'autosave' status.
+ *
+ * @param obj The entry object
+ * @return Autosave the loaded file or not
+ *
+ * @ingroup Entry
+ */
+EAPI Eina_Bool
+elm_entry_autosave_get(const Evas_Object *obj)
+{
+ ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
+ Widget_Data *wd = elm_widget_data_get(obj);
+ if (!wd) return EINA_FALSE;
+ return wd->autosave;
+}
+
+/**