Terminal emulator with all the bells and whistles https://www.enlightenment.org
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

4274 lines
126 KiB

#include "private.h"
#include <Elementary.h>
#include <Elementary_Cursor.h>
#include <Ecore_Input.h>
#include "termio.h"
#include "termiolink.h"
#include "termpty.h"
#include "backlog.h"
#include "extns.h"
#include "termptyops.h"
#include "termcmd.h"
#include "termptydbl.h"
#include "utf8.h"
#include "colors.h"
#include "keyin.h"
#include "config.h"
#include "theme.h"
#include "media.h"
#include "miniview.h"
#include "gravatar.h"
#include "sb.h"
#include "utils.h"
#if defined (__MacOSX__) || (defined (__MACH__) && defined (__APPLE__))
# include <sys/proc_info.h>
# include <libproc.h>
#endif
static Evas_Smart *_smart = NULL;
static Evas_Smart_Class _parent_sc = EVAS_SMART_CLASS_INIT_NULL;
static Eina_List *terms = NULL;
static void _smart_apply(Evas_Object *obj);
static void _smart_size(Evas_Object *obj, int w, int h, Eina_Bool force);
static void _smart_calculate(Evas_Object *obj);
static Eina_Bool _mouse_in_selection(Termio *sd, int cx, int cy);
/* {{{ Helpers */
Termio *
termio_get_from_obj(Evas_Object *obj)
{
return evas_object_smart_data_get(obj);
}
void
termio_object_geometry_get(Termio *sd,
Evas_Coord *x, Evas_Coord *y,
Evas_Coord *w, Evas_Coord *h)
{
evas_object_geometry_get(sd->self, x, y, w, h);
}
static void
_win_obj_del(void *data,
Evas *_e EINA_UNUSED,
Evas_Object *obj,
void *_event EINA_UNUSED)
{
Termio *sd = evas_object_smart_data_get(data);
EINA_SAFETY_ON_NULL_RETURN(sd);
if (obj == sd->win)
{
evas_object_event_callback_del_full(sd->win, EVAS_CALLBACK_DEL,
_win_obj_del, data);
sd->win = NULL;
}
}
void
termio_theme_set(Evas_Object *obj, Evas_Object *theme)
{
Termio *sd = evas_object_smart_data_get(obj);
Evas_Object *bg;
EINA_SAFETY_ON_NULL_RETURN(sd);
EINA_SAFETY_ON_NULL_RETURN(sd->grid.obj);
EINA_SAFETY_ON_NULL_RETURN(theme);
sd->theme = theme;
bg = term_bg_get(sd->term);
EINA_SAFETY_ON_NULL_RETURN(bg);
edje_object_color_class_get(bg, "BG",
&sd->saved_bg.r,
&sd->saved_bg.g,
&sd->saved_bg.b,
&sd->saved_bg.a,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL);
edje_object_color_class_get(sd->cursor.obj, "CURSOR",
&sd->saved_cursor.r,
&sd->saved_cursor.g,
&sd->saved_cursor.b,
&sd->saved_cursor.a,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL);
evas_object_textgrid_palette_get(
sd->grid.obj,
EVAS_TEXTGRID_PALETTE_STANDARD, 0,
&sd->saved_fg.r,
&sd->saved_fg.g,
&sd->saved_fg.b,
&sd->saved_fg.a);
}
void
termio_reset_main_colors(Evas_Object *termio)
{
if (termio)
{
Termio *sd = evas_object_smart_data_get(termio);
Evas_Object *bg;
EINA_SAFETY_ON_NULL_RETURN(sd);
EINA_SAFETY_ON_NULL_RETURN(sd->grid.obj);
bg = term_bg_get(sd->term);
EINA_SAFETY_ON_NULL_RETURN(bg);
edje_object_color_class_set(bg, "BG",
sd->saved_bg.r,
sd->saved_bg.g,
sd->saved_bg.b,
sd->saved_bg.a,
sd->saved_bg.r,
sd->saved_bg.g,
sd->saved_bg.b,
sd->saved_bg.a,
sd->saved_bg.r,
sd->saved_bg.g,
sd->saved_bg.b,
sd->saved_bg.a);
edje_object_color_class_set(sd->cursor.obj, "CURSOR",
sd->saved_cursor.r,
sd->saved_cursor.g,
sd->saved_cursor.b,
sd->saved_cursor.a,
sd->saved_cursor.r,
sd->saved_cursor.g,
sd->saved_cursor.b,
sd->saved_cursor.a,
sd->saved_cursor.r,
sd->saved_cursor.g,
sd->saved_cursor.b,
sd->saved_cursor.a);
evas_object_textgrid_palette_set(
sd->grid.obj,
EVAS_TEXTGRID_PALETTE_STANDARD, 0,
sd->saved_fg.r,
sd->saved_fg.g,
sd->saved_fg.b,
sd->saved_fg.a);
}
}
void
termio_mouseover_suspend_pushpop(Evas_Object *obj, int dir)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN(sd);
sd->link.suspend += dir;
if (sd->link.suspend < 0) sd->link.suspend = 0;
if (sd->link.suspend)
{
if (sd->anim) ecore_animator_del(sd->anim);
sd->anim = NULL;
}
termio_smart_update_queue(sd);
}
void
termio_size_get(const Evas_Object *obj, int *w, int *h)
{
const Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN(sd);
if (w) *w = sd->grid.w;
if (h) *h = sd->grid.h;
}
void
termio_character_size_get(const Evas_Object *obj, int *w, int *h)
{
const Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN(sd);
if (w) *w = sd->font.chw;
if (h) *h = sd->font.chh;
}
int
termio_scroll_get(const Evas_Object *obj)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN_VAL(sd, 0);
return sd->scroll;
}
void
termio_scroll_delta(Evas_Object *obj, int delta, int by_page)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN(sd);
if (by_page)
{
int by = sd->grid.h - 2;
if (by > 1)
delta *= by;
}
sd->scroll += delta;
if ((delta <= 0) && (sd->scroll < 0)) sd->scroll = 0;
termio_smart_update_queue(sd);
miniview_position_offset(term_miniview_get(sd->term), -delta, EINA_TRUE);
}
void
termio_scroll_set(Evas_Object *obj, int scroll)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN(sd);
sd->scroll = scroll;
termio_remove_links(sd);
_smart_apply(obj);
}
void
termio_scroll_top_backlog(Evas_Object *obj)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN(sd);
sd->scroll = INT32_MAX;
termio_remove_links(sd);
_smart_apply(obj);
}
const char *
termio_title_get(const Evas_Object *obj)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN_VAL(sd, NULL);
if (sd->pty->prop.user_title)
return sd->pty->prop.user_title;
return sd->pty->prop.title;
}
const char *
termio_user_title_get(const Evas_Object *obj)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN_VAL(sd, NULL);
return sd->pty->prop.user_title;
}
void
termio_user_title_set(Evas_Object *obj, const char *title)
{
Termio *sd = evas_object_smart_data_get(obj);
size_t len = 0;
EINA_SAFETY_ON_NULL_RETURN(sd);
eina_stringshare_del(sd->pty->prop.user_title);
sd->pty->prop.user_title = NULL;
if (title)
{
len = strlen(title);
}
if (len)
sd->pty->prop.user_title = eina_stringshare_add_length(title, len);
if (sd->pty->cb.set_title.func)
sd->pty->cb.set_title.func(sd->pty->cb.set_title.data);
}
const char *
termio_icon_name_get(const Evas_Object *obj)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN_VAL(sd, NULL);
return sd->pty->prop.icon;
}
void
termio_media_mute_set(Evas_Object *obj, Eina_Bool mute)
{
Termio *sd = evas_object_smart_data_get(obj);
Eina_List *l;
Termblock *blk;
EINA_SAFETY_ON_NULL_RETURN(sd);
EINA_LIST_FOREACH(sd->pty->block.active, l, blk)
{
if (blk->obj && !blk->edje)
media_mute_set(blk->obj, mute);
}
}
void
termio_media_visualize_set(Evas_Object *obj, Eina_Bool visualize)
{
Termio *sd = evas_object_smart_data_get(obj);
Eina_List *l;
Termblock *blk;
EINA_SAFETY_ON_NULL_RETURN(sd);
EINA_LIST_FOREACH(sd->pty->block.active, l, blk)
{
if (blk->obj && !blk->edje)
media_visualize_set(blk->obj, visualize);
}
}
Eina_Bool
termio_selection_exists(const Evas_Object *obj)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN_VAL(sd, EINA_FALSE);
return sd->pty->selection.is_active;
}
Termpty *
termio_pty_get(const Evas_Object *obj)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN_VAL(sd, NULL);
return sd->pty;
}
Evas_Object *
termio_miniview_get(const Evas_Object *obj)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN_VAL(sd, NULL);
return term_miniview_get(sd->term);
}
Term*
termio_term_get(const Evas_Object *obj)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN_VAL(sd, NULL);
return sd->term;
}
Eina_Bool
termio_is_focused(const Evas_Object *obj)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN_VAL(sd, EINA_FALSE);
return term_is_focused(sd->term);
}
Evas_Object *
termio_bg_get(const Evas_Object *obj)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN_VAL(sd, NULL);
return term_bg_get(sd->term);
}
static void
_font_size_set(Evas_Object *obj, int size)
{
Termio *sd = evas_object_smart_data_get(obj);
Config *config;
EINA_SAFETY_ON_NULL_RETURN(sd);
int font_size_scale;
config = sd->config;
if (size < 5) size = 5;
else if (size > 100) size = 100;
font_size_scale = ELM_SCALE_SIZE(size);
if (sd->font_size_scale == font_size_scale) return;
sd->font_size_scale = font_size_scale;
if (config)
{
config->font.size = size;
sd->noreqsize = 1;
termio_config_update(obj);
sd->noreqsize = 0;
evas_object_data_del(obj, "sizedone");
}
}
void
termio_font_update(Evas_Object *obj)
{
Termio *sd = evas_object_smart_data_get(obj);
Config *config;
EINA_SAFETY_ON_NULL_RETURN(sd);
config = sd->config;
if (config)
{
sd->noreqsize = 1;
termio_config_update(obj);
sd->noreqsize = 0;
evas_object_data_del(obj, "sizedone");
}
}
void
termio_font_size_set(Evas_Object *obj, int size)
{
_font_size_set(obj, size);
}
void
termio_grid_size_set(Evas_Object *obj, int w, int h)
{
Termio *sd = evas_object_smart_data_get(obj);
Evas_Coord mw = 1, mh = 1;
EINA_SAFETY_ON_NULL_RETURN(sd);
if (w < 1) w = 1;
if (h < 1) h = 1;
evas_object_size_hint_min_get(obj, &mw, &mh);
evas_object_data_del(obj, "sizedone");
evas_object_size_hint_request_set(obj, mw * w, mh * h);
}
pid_t
termio_pid_get(const Evas_Object *obj)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN_VAL(sd, 0);
return termpty_pid_get(sd->pty);
}
Eina_Bool
termio_cwd_get(const Evas_Object *obj, char *buf, size_t size)
{
Termio *sd = evas_object_smart_data_get(obj);
pid_t pid;
EINA_SAFETY_ON_NULL_RETURN_VAL(sd, EINA_FALSE);
pid = termpty_pid_get(sd->pty);
#if defined (__MacOSX__) || (defined (__MACH__) && defined (__APPLE__))
struct proc_vnodepathinfo vpi;
if (proc_pidinfo(pid, PROC_PIDVNODEPATHINFO, 0, &vpi, sizeof(vpi)) <= 0)
{
ERR(_("Could not get working directory of pid %i: %s"),
pid, strerror(errno));
return EINA_FALSE;
}
memcpy(buf, vpi.pvi_cdir.vip_path, size);
#else
char procpath[PATH_MAX];
ssize_t siz;
snprintf(procpath, sizeof(procpath), "/proc/%ld/cwd", (long) pid);
siz = readlink(procpath, buf, size);
if ((siz == -1) || (siz >= (ssize_t)size))
{
ERR(_("Could not load working directory %s: %s"),
procpath, strerror(errno));
return EINA_FALSE;
}
buf[siz] = '\0';
#endif
buf[size -1] = '\0';
return EINA_TRUE;
}
Evas_Object *
termio_textgrid_get(const Evas_Object *obj)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN_VAL(sd, NULL);
return sd->grid.obj;
}
Evas_Object *
termio_win_get(const Evas_Object *obj)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN_VAL(sd, NULL);
return sd->win;
}
/* }}} */
/* {{{ Config */
void
termio_config_update(Evas_Object *obj)
{
Termio *sd = evas_object_smart_data_get(obj);
Evas_Coord w, h, ow = 0, oh = 0;
EINA_SAFETY_ON_NULL_RETURN(sd);
eina_stringshare_del(sd->font.name);
sd->font.name = NULL;
if (sd->config->font.bitmap)
{
char buf[4096];
snprintf(buf, sizeof(buf), "%s/fonts/%s",
elm_app_data_dir_get(), sd->config->font.name);
sd->font.name = eina_stringshare_add(buf);
}
else
sd->font.name = eina_stringshare_add(sd->config->font.name);
sd->font.size = sd->config->font.size;
sd->jump_on_change = sd->config->jump_on_change;
sd->jump_on_keypress = sd->config->jump_on_keypress;
termpty_config_update(sd->pty, sd->config);
sd->scroll = 0;
colors_term_init(sd->grid.obj, sd->config->color_scheme);
evas_object_textgrid_font_set(sd->grid.obj, sd->font.name, sd->font.size);
evas_object_textgrid_cell_size_get(sd->grid.obj, &w, &h);
evas_object_scale_set(sd->grid.obj, elm_config_scale_get());
if (w < 1) w = 1;
if (h < 1) h = 1;
sd->font.chw = w;
sd->font.chh = h;
evas_object_geometry_get(obj, NULL, NULL, &ow, &oh);
termio_set_cursor_shape(obj, sd->config->cursor_shape);
_smart_size(obj, ow / w, oh / h, EINA_TRUE);
}
Config *
termio_config_get(const Evas_Object *obj)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN_VAL(sd, NULL);
return sd->config;
}
void
termio_config_set(Evas_Object *obj, Config *config)
{
Termio *sd = evas_object_smart_data_get(obj);
Evas_Coord w = 2, h = 2;
sd->config = config;
sd->jump_on_change = config->jump_on_change;
sd->jump_on_keypress = config->jump_on_keypress;
if (config->font.bitmap)
{
char buf[PATH_MAX];
snprintf(buf, sizeof(buf), "%s/fonts/%s",
elm_app_data_dir_get(), config->font.name);
sd->font.name = eina_stringshare_add(buf);
}
else
sd->font.name = eina_stringshare_add(config->font.name);
sd->font.size = config->font.size;
evas_object_textgrid_font_set(sd->grid.obj, sd->font.name, sd->font.size);
evas_object_textgrid_size_get(sd->grid.obj, &w, &h);
evas_object_scale_set(sd->grid.obj, elm_config_scale_get());
if (w < 1) w = 1;
if (h < 1) h = 1;
evas_object_textgrid_size_set(sd->grid.obj, w, h);
evas_object_textgrid_cell_size_get(sd->grid.obj, &w, &h);
if (w < 1) w = 1;
if (h < 1) h = 1;
sd->font.chw = w;
sd->font.chh = h;
termio_set_cursor_shape(obj, sd->cursor.shape);
theme_apply(sd->sel.theme, config, "terminology/selection",
NULL, NULL, EINA_FALSE);
theme_auto_reload_enable(sd->sel.theme);
edje_object_part_swallow(sd->sel.theme, "terminology.top_left", sd->sel.top);
edje_object_part_swallow(sd->sel.theme, "terminology.bottom_right", sd->sel.bottom);
}
static const char *
_cursor_shape_to_group_name(Cursor_Shape shape)
{
switch (shape)
{
case CURSOR_SHAPE_BLOCK: return "terminology/cursor";
case CURSOR_SHAPE_BAR: return "terminology/cursor_bar";
case CURSOR_SHAPE_UNDERLINE: return "terminology/cursor_underline";
}
return NULL;
}
Evas_Object *
termio_get_cursor(const Evas_Object *obj)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN_VAL(sd, NULL);
return sd->cursor.obj;
}
void
termio_set_cursor_shape(Evas_Object *obj, Cursor_Shape shape)
{
Termio *sd = evas_object_smart_data_get(obj);
Config *config;
EINA_SAFETY_ON_NULL_RETURN(sd);
config = sd->config;
theme_apply(sd->cursor.obj, config, _cursor_shape_to_group_name(shape),
NULL, NULL, EINA_FALSE);
theme_auto_reload_enable(sd->cursor.obj);
evas_object_resize(sd->cursor.obj, sd->font.chw, sd->font.chh);
evas_object_show(sd->cursor.obj);
sd->cursor.shape = shape;
if (term_is_focused(sd->term))
{
edje_object_signal_emit(sd->cursor.obj, "focus,out", "terminology");
if (sd->config->disable_cursor_blink)
edje_object_signal_emit(sd->cursor.obj, "focus,in,noblink", "terminology");
else
edje_object_signal_emit(sd->cursor.obj, "focus,in", "terminology");
}
}
/* }}} */
/* {{{ Links */
static Eina_Bool
_should_inline(const Evas_Object *obj)
{
const Config *config = termio_config_get(obj);
const Evas *e;
const Evas_Modifier *mods;
if (!config->helper.inline_please) return EINA_FALSE;
e = evas_object_evas_get(obj);
mods = evas_key_modifier_get(e);
if (evas_key_modifier_is_set(mods, "Control")) return EINA_FALSE;
return EINA_TRUE;
}
/*
* Returned string needs to be freed.
* Does not handle colors */
const char *
termio_link_get(const Evas_Object *obj,
Eina_Bool *from_escape_code)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN_VAL(sd, NULL);
const char *link;
if (!sd->link.string && !sd->link.id)
return NULL;
if (from_escape_code)
*from_escape_code = EINA_FALSE;
link = sd->link.string;
if (sd->link.id)
{
Term_Link *hl = &sd->pty->hl.links[sd->link.id];
if (!hl->url)
return NULL;
link = hl->url;
if (from_escape_code)
*from_escape_code = EINA_TRUE;
}
if (link_is_url(link))
{
if (casestartswith(link, "file://"))
{
// TODO: decode string: %XX -> char
link = link + sizeof("file://") - 1;
/* Handle cases where / is omitted: file://HOSTNAME/home/ */
if (link[0] != '/')
{
link = strchr(link, '/');
if (!link)
return NULL;
}
}
}
return strdup(link);
}
static void
_activate_link(Evas_Object *obj, Eina_Bool may_inline)
{
Termio *sd = evas_object_smart_data_get(obj);
Config *config;
char buf[PATH_MAX], *s, *escaped;
const char *path = NULL, *cmd = NULL;
const char *link = NULL;
Eina_Bool from_escape_code = EINA_FALSE;
Eina_Bool url = EINA_FALSE, email = EINA_FALSE, handled = EINA_FALSE;
EINA_SAFETY_ON_NULL_RETURN(sd);
config = sd->config;
if (!config)
return;
link = termio_link_get(obj, &from_escape_code);
if (!link)
return;
if (from_escape_code && !config->active_links_escape)
goto end;
if (link_is_url(link))
{
if (casestartswith(link, "mailto:"))
{
email = EINA_TRUE;
if (!config->active_links_email)
goto end;
}
else
{
url = EINA_TRUE;
if (!config->active_links_url)
goto end;
}
}
else if (link[0] == '/')
{
path = link;
if (!config->active_links_file)
goto end;
}
else if (link_is_email(link))
{
email = EINA_TRUE;
if (!config->active_links_email)
goto end;
}
s = eina_str_escape(link);
if (!s)
goto end;
if (email)
{
const char *p = s;
// run mail client
cmd = "xdg-email";
if ((config->helper.email) &&
(config->helper.email[0]))
cmd = config->helper.email;
if (casestartswith(s, "mailto:"))
p += sizeof("mailto:") - 1;
escaped = ecore_file_escape_name(p);
if (escaped)
{
snprintf(buf, sizeof(buf), "%s %s", cmd, escaped);
free(escaped);
}
}
else if (path)
{
// locally accessible file
cmd = "xdg-open";
escaped = ecore_file_escape_name(path);
if (escaped)
{
size_t len = strlen(path);
Media_Type type = media_src_type_get(path, len);
if (may_inline && _should_inline(obj))
{
if ((type == MEDIA_TYPE_IMG) ||
(type == MEDIA_TYPE_SCALE) ||
(type == MEDIA_TYPE_EDJE))
{
evas_object_smart_callback_call(obj, "popup", NULL);
handled = EINA_TRUE;
}
else if (type == MEDIA_TYPE_MOV)
{
evas_object_smart_callback_call(obj, "popup", NULL);
handled = EINA_TRUE;
}
}
if (!handled)
{
if ((type == MEDIA_TYPE_IMG) ||
(type == MEDIA_TYPE_SCALE) ||
(type == MEDIA_TYPE_EDJE))
{
if ((config->helper.local.image) &&
(config->helper.local.image[0]))
cmd = config->helper.local.image;
}
else if (type == MEDIA_TYPE_MOV)
{
if ((config->helper.local.video) &&
(config->helper.local.video[0]))
cmd = config->helper.local.video;
}
else
{
if ((config->helper.local.general) &&
(config->helper.local.general[0]))
cmd = config->helper.local.general;
}
snprintf(buf, sizeof(buf), "%s %s", cmd, escaped);
free(escaped);
}
}
}
else if (url)
{
// remote file needs ecore-con-url
cmd = "xdg-open";
escaped = ecore_file_escape_name(s);
if (escaped)
{
size_t len = strlen(link);
Media_Type type = media_src_type_get(link, len);
if (may_inline && _should_inline(obj))
{
evas_object_smart_callback_call(obj, "popup", NULL);
handled = EINA_TRUE;
}
if (!handled)
{
if ((type == MEDIA_TYPE_IMG) ||
(type == MEDIA_TYPE_SCALE) ||
(type == MEDIA_TYPE_EDJE))
{
if ((config->helper.url.image) &&
(config->helper.url.image[0]))
cmd = config->helper.url.image;
}
else if (type == MEDIA_TYPE_MOV)
{
if ((config->helper.url.video) &&
(config->helper.url.video[0]))
cmd = config->helper.url.video;
}
else
{
if ((config->helper.url.general) &&
(config->helper.url.general[0]))
cmd = config->helper.url.general;
}
snprintf(buf, sizeof(buf), "%s %s", cmd, escaped);
free(escaped);
}
}
}
else
{
free(s);
goto end;
}
free(s);
if (!handled)
ecore_exe_run(buf, NULL);
end:
free((char*)link);
}
static void
_cb_ctxp_del(void *data,
Evas *_e EINA_UNUSED,
Evas_Object *_obj EINA_UNUSED,
void *_event EINA_UNUSED)
{
Termio *sd = data;
EINA_SAFETY_ON_NULL_RETURN(sd);
sd->ctxpopup = NULL;
/* Force refocus */
term_unfocus(sd->term);
term_focus(sd->term);
}
static void
_cb_ctxp_dismissed(void *data,
Evas_Object *obj,
void *_event EINA_UNUSED)
{
Termio *sd = data;
EINA_SAFETY_ON_NULL_RETURN(sd);
sd->ctxpopup = NULL;
evas_object_del(obj);
}
static void
_cb_ctxp_link_preview(void *data,
Evas_Object *obj,
void *_event EINA_UNUSED)
{
Evas_Object *term = data;
Termio *sd = evas_object_smart_data_get(term);
EINA_SAFETY_ON_NULL_RETURN(sd);
_activate_link(term, EINA_TRUE);
sd->ctxpopup = NULL;
evas_object_del(obj);
}
static void
_cb_ctxp_link_open(void *data,
Evas_Object *obj,
void *_event EINA_UNUSED)
{
Evas_Object *term = data;
Termio *sd = evas_object_smart_data_get(term);
EINA_SAFETY_ON_NULL_RETURN(sd);
_activate_link(term, EINA_FALSE);
sd->ctxpopup = NULL;
evas_object_del(obj);
}
static void _lost_selection(void *data, Elm_Sel_Type selection);
static void
_lost_selection_reset_job(void *data)
{
Termio *sd = evas_object_smart_data_get(data);
EINA_SAFETY_ON_NULL_RETURN(sd);
sd->sel_reset_job = NULL;
if (sd->sel_str)
{
elm_cnp_selection_set(sd->win, sd->sel_type,
ELM_SEL_FORMAT_TEXT,
sd->sel_str, strlen(sd->sel_str));
elm_cnp_selection_loss_callback_set(sd->win, sd->sel_type,
_lost_selection, data);
}
}
static void
_lost_selection(void *data, Elm_Sel_Type selection)
{
Eina_List *l;
Evas_Object *obj;
double t = ecore_time_get();
EINA_LIST_FOREACH(terms, l, obj)
{
Termio *sd = evas_object_smart_data_get(obj);
if (!sd) continue;
if ((t - sd->set_sel_at) < 0.2) /// hack
{
if ((sd->have_sel) && (sd->sel_str) && (!sd->reset_sel))
{
sd->reset_sel = EINA_TRUE;
if (sd->sel_reset_job) ecore_job_del(sd->sel_reset_job);
sd->sel_reset_job = ecore_job_add
(_lost_selection_reset_job, data);
}
continue;
}
if (sd->have_sel)
{
eina_stringshare_del(sd->sel_str);
sd->sel_str = NULL;
termio_sel_set(sd, EINA_FALSE);
elm_object_cnp_selection_clear(sd->win, selection);
termio_smart_update_queue(sd);
sd->have_sel = EINA_FALSE;
}
}
}
void
termio_take_selection_text(Termio *sd, Elm_Sel_Type type, const char *text)
{
EINA_SAFETY_ON_NULL_RETURN(sd);
text = eina_stringshare_add(text);
sd->have_sel = EINA_FALSE;
sd->reset_sel = EINA_FALSE;
sd->set_sel_at = ecore_time_get(); // hack
sd->sel_type = type;
elm_cnp_selection_set(sd->win, type,
ELM_SEL_FORMAT_TEXT,
text,
eina_stringshare_strlen(text));
elm_cnp_selection_loss_callback_set(sd->win, type,
_lost_selection, sd->self);
sd->have_sel = EINA_TRUE;
eina_stringshare_del(sd->sel_str);
sd->sel_str = eina_stringshare_add(text);
}
Eina_Bool
termio_take_selection(Evas_Object *obj, Elm_Sel_Type type)
{
Termio *sd = termio_get_from_obj(obj);
const char *s = NULL;
size_t len = 0;
EINA_SAFETY_ON_NULL_RETURN_VAL(sd, EINA_FALSE);
s = termio_internal_get_selection(sd, &len);
if (s)
{
if ((sd->win) && (len > 0))
termio_take_selection_text(sd, type, s);
eina_stringshare_del(s);
return EINA_TRUE;
}
return EINA_FALSE;
}
static void
_cb_ctxp_link_content_copy(void *data,
Evas_Object *obj,
void *event EINA_UNUSED)
{
Evas_Object *term = data;
Termio *sd = evas_object_smart_data_get(term);
EINA_SAFETY_ON_NULL_RETURN(sd);
if (sd->link.id)
{
Term_Link *hl = &sd->pty->hl.links[sd->link.id];
if (!hl->url)
return;
termio_take_selection_text(sd, ELM_SEL_TYPE_CLIPBOARD, hl->url);
}
else
{
struct ty_sb sb = {.buf = NULL, .len = 0, .alloc = 0};
char *raw_link;
termio_selection_get(sd,
sd->link.x1, sd->link.y1,
sd->link.x2, sd->link.y2,
&sb, EINA_FALSE);
raw_link = ty_sb_steal_buf(&sb);
termio_take_selection_text(sd, ELM_SEL_TYPE_CLIPBOARD, raw_link);
free(raw_link);
}
sd->ctxpopup = NULL;
evas_object_del(obj);
}
static void
_cb_ctxp_link_copy(void *data,
Evas_Object *obj,
void *_event EINA_UNUSED)
{
Evas_Object *term = data;
Termio *sd = evas_object_smart_data_get(term);
EINA_SAFETY_ON_NULL_RETURN(sd);
EINA_SAFETY_ON_NULL_RETURN(sd->link.string);
termio_take_selection_text(sd, ELM_SEL_TYPE_CLIPBOARD, sd->link.string);
sd->ctxpopup = NULL;
evas_object_del(obj);
}
static Eina_Bool
_mouse_in_selection(Termio *sd, int cx, int cy)
{
int start_x = 0, start_y = 0, end_x = 0, end_y = 0;
if (!sd->pty->selection.is_active)
return EINA_FALSE;
start_x = sd->pty->selection.start.x;
start_y = sd->pty->selection.start.y;
end_x = sd->pty->selection.end.x;
end_y = sd->pty->selection.end.y;
if (!sd->pty->selection.is_top_to_bottom)
{
INT_SWAP(start_y, end_y);
INT_SWAP(start_x, end_x);
}
if (sd->pty->selection.is_box)
{
if ((start_y <= cy) && (cy <= end_y) &&
(start_x <= cx) && (cx <= end_x) )
return EINA_TRUE;
}
else
{
if ((cy < start_y) || (cy > end_y))
return EINA_FALSE;
if (((cy == start_y) && (cx < start_x)) ||
((cy == end_y) && (cx > end_x)))
return EINA_FALSE;
return EINA_TRUE;
}
return EINA_FALSE;
}
static Eina_Bool
_getsel_cb(void *data,
Evas_Object *_obj EINA_UNUSED,
Elm_Selection_Data *ev)
{
Termio *sd = evas_object_smart_data_get(data);
EINA_SAFETY_ON_NULL_RETURN_VAL(sd, EINA_FALSE);
if (ev->format == ELM_SEL_FORMAT_TEXT)
{
char *buf;
if (ev->len <= 0) return EINA_TRUE;
buf = calloc(2, ev->len); /* twice in case the paste is only \n */
if (buf)
{
const char *s = ev->data;
int i, j, pos = 0;
/* apparently we have to convert \n into \r in terminal land. */
for (i = 0; i < (int)ev->len && s[i];)
{
Eina_Unicode g = 0;
int prev_i = i;
g = eina_unicode_utf8_next_get(s, &i);
/* Skip escape codes as a security measure */
if (! ((g == '\t') || (g == '\n') || (g >= ' ')))
{
continue;
}
if (g == '\n')
buf[pos++] = '\r';
else
for (j = prev_i; j < i; j++)
buf[pos++] = s[j];
}
if (pos)
{
if (sd->pty->bracketed_paste)
termpty_write(sd->pty, "\x1b[200~",
sizeof("\x1b[200~") - 1);
termpty_write(sd->pty, buf, pos);
if (sd->pty->bracketed_paste)
termpty_write(sd->pty, "\x1b[201~",
sizeof("\x1b[201~") - 1);
}
free(buf);
}
}
else
{
const char *fmt = "UNKNOWN";
switch (ev->format)
{
case ELM_SEL_FORMAT_TARGETS: fmt = "TARGETS"; break;
case ELM_SEL_FORMAT_NONE: fmt = "NONE"; break;
case ELM_SEL_FORMAT_TEXT: fmt = "TEXT"; break; /* shouldn't happen */
case ELM_SEL_FORMAT_MARKUP: fmt = "MARKUP"; break;
case ELM_SEL_FORMAT_IMAGE: fmt = "IMAGE"; break;
case ELM_SEL_FORMAT_VCARD: fmt = "VCARD"; break;
case ELM_SEL_FORMAT_HTML: fmt = "HTML"; break;
}
WRN(_("unsupported selection format '%s'"), fmt);
}
return EINA_TRUE;
}
void
termio_paste_selection(Evas_Object *obj, Elm_Sel_Type type)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN(sd);
if (!sd->win)
return;
elm_cnp_selection_get(sd->win, type, ELM_SEL_FORMAT_TEXT,
_getsel_cb, obj);
}
static const char *
_color_to_txt(const Termio *sd)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(sd, NULL);
if (sd->link.color.a == 255)
return eina_stringshare_printf("#%.2x%.2x%.2x",
sd->link.color.r,
sd->link.color.g,
sd->link.color.b);
else
return eina_stringshare_printf("#%.2x%.2x%.2x%.2x",
sd->link.color.r,
sd->link.color.g,
sd->link.color.b,
sd->link.color.a);
}
static void
_cb_ctxp_color_copy(void *data,
Evas_Object *obj,
void *_event EINA_UNUSED)
{
Termio *sd = data;
const char *txt;
EINA_SAFETY_ON_NULL_RETURN(sd);
txt = _color_to_txt(sd);
if (!txt) return;
termio_take_selection_text(sd, ELM_SEL_TYPE_CLIPBOARD, txt);
eina_stringshare_del(txt);
sd->ctxpopup = NULL;
evas_object_del(obj);
}
static void
_cb_link_down(void *data,
Evas *_e EINA_UNUSED,
Evas_Object *_obj EINA_UNUSED,
void *event)
{
Evas_Event_Mouse_Down *ev = event;
Termio *sd = evas_object_smart_data_get(data);
EINA_SAFETY_ON_NULL_RETURN(sd);
Term_Link *hl = NULL;
if (!sd->link.string && sd->link.id)
{
hl = &sd->pty->hl.links[sd->link.id];
if (!hl->url)
return;
sd->link.string = eina_stringshare_add(hl->url);
}
if (ev->button == 1)
{
sd->link.down.down = EINA_TRUE;
sd->link.down.x = ev->canvas.x;
sd->link.down.y = ev->canvas.y;
}
else if (ev->button == 3) /* right click */
{
Evas_Object *ctxp;
ctxp = elm_ctxpopup_add(sd->win);
sd->ctxpopup = ctxp;
if (sd->link.is_color)
{
const char *fmt, *txt;
if (sd->link.color.a == 255)
{
fmt = eina_stringshare_printf(_("Copy '%s'"),
"#%.2x%.2x%.2x");
txt = eina_stringshare_printf(fmt,
sd->link.color.r,
sd->link.color.g,
sd->link.color.b);
}
else
{
fmt = eina_stringshare_printf(_("Copy '%s'"),
"#%.2x%.2x%.2x%.2x");
txt = eina_stringshare_printf(fmt,
sd->link.color.r,
sd->link.color.g,
sd->link.color.b,
sd->link.color.a);
}
elm_ctxpopup_item_append(ctxp, txt, NULL,
_cb_ctxp_color_copy, sd);
eina_stringshare_del(txt);
eina_stringshare_del(fmt);
}
else
{
Eina_Bool absolut = EINA_FALSE;
const char *raw_link;
size_t len;
if (sd->pty->selection.is_active)
{
int cx = 0, cy = 0;
termio_cursor_to_xy(sd, ev->canvas.x, ev->canvas.y, &cx, &cy);
if (_mouse_in_selection(sd, cx, cy))
return;
}
if (hl)
{
raw_link = hl->url;
len = strlen(hl->url);
}
else
{
struct ty_sb sb = {.buf = NULL, .len = 0, .alloc = 0};
termio_selection_get(sd,
sd->link.x1,
sd->link.y1,
sd->link.x2,
sd->link.y2,
&sb,
EINA_FALSE);
len = sb.len;
raw_link = ty_sb_steal_buf(&sb);
}
if (len > 0 && raw_link[0] == '/')
absolut = EINA_TRUE;
if (sd->config->helper.inline_please)
{
Media_Type type = media_src_type_get(raw_link, len);
if ((type == MEDIA_TYPE_IMG) ||
(type == MEDIA_TYPE_SCALE) ||
(type == MEDIA_TYPE_EDJE) ||
(type == MEDIA_TYPE_MOV))
elm_ctxpopup_item_append(ctxp, _("Preview"), NULL,
_cb_ctxp_link_preview, sd->self);
}
elm_ctxpopup_item_append(ctxp, _("Open"), NULL, _cb_ctxp_link_open,
sd->self);
if (!absolut &&
!link_is_url(raw_link) &&
!link_is_email(raw_link))
{
elm_ctxpopup_item_append(ctxp, _("Copy relative path"), NULL, _cb_ctxp_link_content_copy,
sd->self);
elm_ctxpopup_item_append(ctxp, _("Copy full path"), NULL, _cb_ctxp_link_copy,
sd->self);
}
else
{
elm_ctxpopup_item_append(ctxp, _("Copy"), NULL, _cb_ctxp_link_copy, sd->self);
}
if (!hl)
free((void*)raw_link);
}
evas_object_move(ctxp, ev->canvas.x, ev->canvas.y);
evas_object_show(ctxp);
evas_object_smart_callback_add(ctxp, "dismissed",
_cb_ctxp_dismissed, sd);
evas_object_event_callback_add(ctxp, EVAS_CALLBACK_DEL,
_cb_ctxp_del, sd);
}
}
static Eina_Bool
_cb_link_up_delay(void *data)
{
Termio *sd = evas_object_smart_data_get(data);
EINA_SAFETY_ON_NULL_RETURN_VAL(sd, EINA_FALSE);
sd->link_do_timer = NULL;
if (!sd->didclick) _activate_link(data, EINA_TRUE);
sd->didclick = EINA_FALSE;
return EINA_FALSE;
}
static void
_cb_link_up(void *data,
Evas *_e EINA_UNUSED,
Evas_Object *_obj EINA_UNUSED,
void *event)
{
Evas_Event_Mouse_Up *ev = event;
Termio *sd = evas_object_smart_data_get(data);
EINA_SAFETY_ON_NULL_RETURN(sd);
if ((ev->button == 1) && (sd->link.down.down) && (!sd->link.is_color))
{
Evas_Coord dx, dy, finger_size;
dx = abs(ev->canvas.x - sd->link.down.x);
dy = abs(ev->canvas.y - sd->link.down.y);
finger_size = elm_config_finger_size_get();
if ((dx <= finger_size) && (dy <= finger_size))
{
if (sd->link_do_timer)
ecore_timer_reset(sd->link_do_timer);
else
sd->link_do_timer = ecore_timer_add(0.2, _cb_link_up_delay, data);
}
sd->link.down.down = EINA_FALSE;
}
}
static void
_cb_link_drag_move(void *data, Evas_Object *obj, Evas_Coord x, Evas_Coord y, Elm_Xdnd_Action action)
{
const Evas_Modifier *em = NULL;
Termio *sd = evas_object_smart_data_get(data);
EINA_SAFETY_ON_NULL_RETURN(sd);
DBG("dnd %i %i act %i", x, y, action);
em = evas_key_modifier_get(evas_object_evas_get(sd->event));
if (em)
{
if (evas_key_modifier_is_set(em, "Control"))
elm_drag_action_set(obj, ELM_XDND_ACTION_COPY);
else
elm_drag_action_set(obj, ELM_XDND_ACTION_MOVE);
}
}
static void
_cb_link_drag_accept(void *data,
Evas_Object *_obj EINA_UNUSED,
Eina_Bool doaccept)
{
Termio *sd = evas_object_smart_data_get(data);
EINA_SAFETY_ON_NULL_RETURN(sd);
DBG("dnd accept: %i", doaccept);
}
static void
_cb_link_drag_done(void *data, Evas_Object *_obj EINA_UNUSED)
{
Termio *sd = evas_object_smart_data_get(data);
EINA_SAFETY_ON_NULL_RETURN(sd);
DBG("dnd done");
sd->link.down.dnd = EINA_FALSE;
if ((sd->link.down.dndobjdel) && (sd->link.down.dndobj))
evas_object_del(sd->link.down.dndobj);
sd->link.down.dndobj = NULL;
}
static Evas_Object *
_cb_link_icon_new(void *data, Evas_Object *par, Evas_Coord *xoff, Evas_Coord *yoff)
{
Evas_Object *icon;
Termio *sd = evas_object_smart_data_get(data);
EINA_SAFETY_ON_NULL_RETURN_VAL(sd, NULL);
icon = elm_button_add(par);
elm_object_text_set(icon, sd->link.string);
*xoff = 0;
*yoff = 0;
return icon;
}
static void
_cb_link_move(void *data,
Evas *_e EINA_UNUSED,
Evas_Object *obj,
void *event)
{
Evas_Event_Mouse_Move *ev = event;
Termio *sd = evas_object_smart_data_get(data);
Evas_Coord dx, dy;
EINA_SAFETY_ON_NULL_RETURN(sd);
if (!sd->link.down.down)
return;
dx = abs(ev->cur.canvas.x - sd->link.down.x);
dy = abs(ev->cur.canvas.y - sd->link.down.y);
if ((sd->config->drag_links) &&
(sd->link.string) &&
((dx > elm_config_finger_size_get()) ||
(dy > elm_config_finger_size_get())))
{
sd->link.down.down = EINA_FALSE;
sd->link.down.dnd = EINA_TRUE;
DBG("dnd start %s %i %i", sd->link.string,
evas_key_modifier_is_set(ev->modifiers, "Control"),
evas_key_modifier_is_set(ev->modifiers, "Shift"));
if (evas_key_modifier_is_set(ev->modifiers, "Control"))
elm_drag_start(obj, ELM_SEL_FORMAT_IMAGE, sd->link.string,
ELM_XDND_ACTION_COPY,
_cb_link_icon_new, data,
_cb_link_drag_move, data,
_cb_link_drag_accept, data,
_cb_link_drag_done, data);
else
elm_drag_start(obj, ELM_SEL_FORMAT_IMAGE, sd->link.string,
ELM_XDND_ACTION_MOVE,
_cb_link_icon_new, data,
_cb_link_drag_move, data,
_cb_link_drag_accept, data,
_cb_link_drag_done, data);
sd->link.down.dndobj = obj;
sd->link.down.dndobjdel = EINA_FALSE;
}
}
static Evas_Object *
_color_tooltip_content(void *data,
Evas_Object *obj,
Evas_Object *_tt EINA_UNUSED)
{
Termio *sd = data;
Evas_Object *o;
Evas *canvas = evas_object_evas_get(obj);
const char *txt;
o = edje_object_add(canvas);
theme_apply(o, sd->config, "terminology/color_preview",
NULL, NULL, EINA_FALSE);
evas_object_size_hint_min_set(o, 80, 80);
edje_object_color_class_set(o, "color_preview",
sd->link.color.r, sd->link.color.g, sd->link.color.b, sd->link.color.a,
sd->link.color.r, sd->link.color.g, sd->link.color.b, sd->link.color.a,
sd->link.color.r, sd->link.color.g, sd->link.color.b, sd->link.color.a);
txt = _color_to_txt(sd);
edje_object_part_text_set(o, "name", txt);
eina_stringshare_del(txt);
return o;
}
static void
_color_tooltip(Evas_Object *obj,
Termio *sd)
{
elm_object_tooltip_content_cb_set(obj, _color_tooltip_content, sd, NULL);
}
static void
_update_link(Termio *sd, Eina_Bool same_geom)
{
Evas_Coord ox, oy, ow, oh;
Evas_Object *o;
Evas_Object *obj;
Eina_Bool popup_exists;
int y;
EINA_SAFETY_ON_NULL_RETURN(sd);
obj = sd->self;
if (sd->link.id || sd->link.is_color)
{
same_geom = EINA_FALSE;
}
if (same_geom)
return;
sd->link.id = 0;
if (sd->link.suspend)
return;
evas_object_geometry_get(obj, &ox, &oy, &ow, &oh);
EINA_LIST_FREE(sd->link.objs, o)
{
if (sd->link.down.dndobj == o)
{
sd->link.down.dndobjdel = EINA_TRUE;
evas_object_hide(o);
}
else
evas_object_del(o);
}
if (!sd->link.string && !sd->link.is_color)
return;
popup_exists = main_term_popup_exists(sd->term);
for (y = sd->link.y1; y <= sd->link.y2; y++)
{
o = elm_layout_add(sd->win);
evas_object_smart_member_add(o, obj);
theme_apply(o, sd->config, "terminology/link",
NULL, NULL, EINA_TRUE);
if (y == sd->link.y1)
{
evas_object_move(o, ox + (sd->link.x1 * sd->font.chw),
oy + (y * sd->font.chh));
if (sd->link.y1 == sd->link.y2)
evas_object_resize(o,
((sd->link.x2 - sd->link.x1 + 1) * sd->font.chw),
sd->font.chh);
else
evas_object_resize(o,
((sd->grid.w - sd->link.x1) * sd->font.chw),
sd->font.chh);
}
else if (y == sd->link.y2)
{
evas_object_move(o, ox, oy + (y * sd->font.chh));
evas_object_resize(o,
((sd->link.x2 + 1) * sd->font.chw),
sd->font.chh);
}
else
{
evas_object_move(o, ox, oy + (y * sd->font.chh));
evas_object_resize(o, (sd->grid.w * sd->font.chw),
sd->font.chh);
}
sd->link.objs = eina_list_append(sd->link.objs, o);
elm_object_cursor_set(o, ELM_CURSOR_HAND2);
evas_object_show(o);
evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_DOWN,
_cb_link_down, obj);
evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_UP,
_cb_link_up, obj);
evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_MOVE,
_cb_link_move, obj);
if (!popup_exists)
{
if (link_is_email(sd->link.string))
{
gravatar_tooltip(o, sd->config, sd->link.string);
}
if (sd->link.is_color)
{
_color_tooltip(o, sd);
}
}
}
}
void
termio_remove_links(Termio *sd)
{
Eina_Bool same_geom = EINA_FALSE;
eina_stringshare_del(sd->link.string);
sd->link.string = NULL;
sd->link.x1 = -1;
sd->link.y1 = -1;
sd->link.x2 = -1;
sd->link.y2 = -1;
sd->link.suspend = 0;
sd->link.id = 0;
sd->link.is_color = EINA_FALSE;
sd->link.color.r = 0;
sd->link.color.g = 0;
sd->link.color.b = 0;
sd->link.color.a = 0;
_update_link(sd, same_geom);
}
static void
_hyperlink_end(Termio *sd,
Term_Link *hl,
Evas_Object *o,
Eina_Bool add_tooltip)
{
Eina_Bool popup_exists;
sd->link.objs = eina_list_append(sd->link.objs, o);
elm_object_cursor_set(o, ELM_CURSOR_HAND2);
evas_object_show(o);
evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_DOWN,
_cb_link_down, sd->self);
evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_UP,
_cb_link_up, sd->self);
evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_MOVE,
_cb_link_move, sd->self);
popup_exists = main_term_popup_exists(sd->term);
if (!popup_exists && add_tooltip)
{
/* display tooltip */
elm_object_tooltip_text_set(o, hl->url);
if (link_is_email(hl->url))
{
gravatar_tooltip(o, sd->config, hl->url);
}
}
}
static void
_hyperlink_mouseover(Termio *sd,
uint16_t link_id)
{
Evas_Coord ox, oy, ow, oh;
int x, y;
Term_Link *hl;
EINA_SAFETY_ON_NULL_RETURN(sd);
/* If it's the same link, consider we already have the correct links
* displayed */
if (sd->link.id == link_id)
return;
if (sd->link.suspend)
return;
termio_remove_links(sd);
sd->link.id = link_id;
hl = &sd->pty->hl.links[link_id];
if (!hl->url)
return;
/* Scan the whole screen and display links as needed */
termio_object_geometry_get(sd, &ox, &oy, &ow, &oh);
termpty_backlog_lock();
termpty_backscroll_adjust(sd->pty, &sd->scroll);
for (y = 0; y < sd->grid.h; y++)
{
Termcell *cells;
Evas_Object *o;
ssize_t w = 0;
int start_x = -1;
Eina_Bool add_tooltip = EINA_FALSE;
o = NULL;
cells = termpty_cellrow_get(sd->pty, y - sd->scroll, &w);
if (!cells)
continue;
for (x = 0; x < w; x++)
{
Termcell *c = cells + x;
if (term_link_eq(sd->pty, hl, c->att.link_id))
{
if (!o)
{
o = elm_layout_add(sd->win);
evas_object_smart_member_add(o, sd->self);
theme_apply(o, sd->config, "terminology/link",
NULL, NULL, EINA_TRUE);
evas_object_move(o,
ox + (x * sd->font.chw),
oy + (y * sd->font.chh));
start_x = x;
}
}
else
{
if (o)
{
evas_object_resize(o,
(x - start_x) * sd->font.chw,
sd->font.chh);
add_tooltip = ((y == sd->mouse.cy) &&
((start_x <= sd->mouse.cx) &&
(sd->mouse.cx <= x)));
_hyperlink_end(sd, hl, o, add_tooltip);
if (add_tooltip)
{
sd->link.y1 = sd->link.y2 = y;
sd->link.x1 = start_x;
sd->link.x2 = x;
}
o = NULL;
}
}
}
if (o)
{
evas_object_resize(o,
(x - start_x + 1) * sd->font.chw,
sd->font.chh);
add_tooltip = ((y == sd->mouse.cy) &&
((start_x <= sd->mouse.cx) &&
(sd->mouse.cx <= x)));
_hyperlink_end(sd, hl, o, add_tooltip);
if (add_tooltip)
{
sd->link.y1 = sd->link.y2 = y;
sd->link.x1 = start_x;
sd->link.x2 = x;
}
}
}
termpty_backlog_unlock();
}
/* }}} */
/* {{{ Blocks */
static void
_smart_media_clicked(void *data, Evas_Object *obj, void *_info EINA_UNUSED)
{
// Termio *sd = evas_object_smart_data_get(data);
Termblock *blk;
const char *file = media_get(obj);
if (!file) return;
blk = evas_object_data_get(obj, "blk");
if (blk)
{
if (blk->link)
{
Media_Type type = media_src_type_get(blk->link, strlen(blk->link));
Config *config = termio_config_get(data);
if (config)
{
if ((!config->helper.inline_please) ||
(!((type == MEDIA_TYPE_IMG) || (type == MEDIA_TYPE_SCALE) ||
(type == MEDIA_TYPE_EDJE) || (type == MEDIA_TYPE_MOV))))
{
const char *cmd = NULL;
file = blk->link;
if ((config->helper.local.general) &&
(config->helper.local.general[0]))
cmd = config->helper.local.general;
if (cmd)
{
char *escaped;
escaped = ecore_file_escape_name(file);
if (escaped)
{
char buf[PATH_MAX];
snprintf(buf, sizeof(buf), "%s %s", cmd, escaped);
ecore_exe_run(buf, NULL);
free(escaped);
}
return;
}
}
file = blk->link;
}
}
}
evas_object_smart_callback_call(data, "popup", (void *)file);
}
static void
_smart_media_del(void *data,
Evas *_e EINA_UNUSED,
Evas_Object *obj,
void *_info EINA_UNUSED)
{
Termblock *blk = data;
if (blk->obj == obj) blk->obj = NULL;
}
static void
_smart_media_play(void *data,
Evas_Object *obj,
void *_info EINA_UNUSED)
{
Termblock *blk = data;
if (blk->obj != obj) return;
blk->mov_state = MOVIE_STATE_PLAY;
}
static void
_smart_media_pause(void *data,
Evas_Object *obj,
void *_info EINA_UNUSED)
{
Termblock *blk = data;
if (blk->obj != obj) return;
blk->mov_state = MOVIE_STATE_PAUSE;
}
static void
_smart_media_stop(void *data,
Evas_Object *obj,
void *_info EINA_UNUSED)
{
Termblock *blk = data;
if (blk->obj != obj) return;
blk->mov_state = MOVIE_STATE_STOP;
}
static void
_block_edje_signal_cb(void *data,
Evas_Object *_obj EINA_UNUSED,
const char *sig,
const char *src)
{
Termblock *blk = data;
Termio *sd = evas_object_smart_data_get(blk->pty->obj);
char *buf = NULL, *chid = NULL;
int buflen = 0;
Eina_List *l;
EINA_SAFETY_ON_NULL_RETURN(sd);
if ((!blk->chid) || (!sd->cur_chids)) return;
EINA_LIST_FOREACH(sd->cur_chids, l, chid)
{
if (!(!strcmp(blk->chid, chid))) break;
chid = NULL;
}
if (!chid) return;
if ((!strcmp(sig, "drag")) ||
(!strcmp(sig, "drag,start")) ||
(!strcmp(sig, "drag,stop")) ||
(!strcmp(sig, "drag,step")) ||
(!strcmp(sig, "drag,set")))
{
int v1, v2;
double f1 = 0.0, f2 = 0.0;
edje_object_part_drag_value_get(blk->obj, src, &f1, &f2);
v1 = (int)(f1 * 1000.0);
v2 = (int)(f2 * 1000.0);
buf = alloca(strlen(src) + strlen(blk->chid) + 256);
buflen = sprintf(buf, "%c};%s\n%s\n%s\n%i\n%i", 0x1b,
blk->chid, sig, src, v1, v2);
termpty_write(sd->pty, buf, buflen + 1);
}
else
{
buf = alloca(strlen(sig) + strlen(src) + strlen(blk->chid) + 128);
buflen = sprintf(buf, "%c}signal;%s\n%s\n%s", 0x1b,
blk->chid, sig, src);
termpty_write(sd->pty, buf, buflen + 1);
}
}
static void
_block_edje_message_cb(void *data,
Evas_Object *_obj EINA_UNUSED,
Edje_Message_Type type, int id, void *msg)
{
Termblock *blk = data;
Termio *sd = evas_object_smart_data_get(blk->pty->obj);
char *chid = NULL, buf[4096];
Eina_List *l;
int buflen;
EINA_SAFETY_ON_NULL_RETURN(sd);
if ((!blk->chid) || (!sd->cur_chids)) return;
EINA_LIST_FOREACH(sd->cur_chids, l, chid)
{
if (!(!strcmp(blk->chid, chid))) break;
chid = NULL;
}
if (!chid) return;
switch (type)
{
case EDJE_MESSAGE_STRING:
{
Edje_Message_String *m = msg;
buflen = sprintf(buf, "%c}message;%s\n%i\nstring\n%s", 0x1b,
blk->chid, id, m->str);
termpty_write(sd->pty, buf, buflen + 1);
}
break;
case EDJE_MESSAGE_INT:
{
Edje_Message_Int *m = msg;
buflen = snprintf(buf, sizeof(buf),
"%c}message;%s\n%i\nint\n%i", 0x1b,
blk->chid, id, m->val);
termpty_write(sd->pty, buf, buflen + 1);
}
break;
case EDJE_MESSAGE_FLOAT:
{
Edje_Message_Float *m = msg;
buflen = snprintf(buf, sizeof(buf),
"%c}message;%s\n%i\nfloat\n%i", 0x1b,
blk->chid, id, (int)(m->val * 1000.0));
termpty_write(sd->pty, buf, buflen + 1);
}
break;
case EDJE_MESSAGE_STRING_SET:
{
Edje_Message_String_Set *m = msg;
int i;
char zero[1] = { 0 };
buflen = snprintf(buf, sizeof(buf),
"%c}message;%s\n%i\nstring_set\n%i", 0x1b,
blk->chid, id, m->count);
termpty_write(sd->pty, buf, buflen);
for (i = 0; i < m->count; i++)
{
termpty_write(sd->pty, "\n", 1);
termpty_write(sd->pty, m->str[i], strlen(m->str[i]));
}
termpty_write(sd->pty, zero, 1);
}
break;
case EDJE_MESSAGE_INT_SET:
{
Edje_Message_Int_Set *m = msg;
int i;
char zero[1] = { 0 };
buflen = snprintf(buf, sizeof(buf),
"%c}message;%s\n%i\nint_set\n%i", 0x1b,
blk->chid, id, m->count);
termpty_write(sd->pty, buf, buflen);
for (i = 0; i < m->count; i++)
{
termpty_write(sd->pty, "\n", 1);
buflen = snprintf(buf, sizeof(buf), "%i", m->val[i]);
termpty_write(sd->pty, buf, buflen);
}
termpty_write(sd->pty, zero, 1);
}
break;
case EDJE_MESSAGE_FLOAT_SET:
{
Edje_Message_Float_Set *m = msg;
int i;
char zero[1] = { 0 };
buflen = snprintf(buf, sizeof(buf),
"%c}message;%s\n%i\nfloat_set\n%i", 0x1b,
blk->chid, id, m->count);
termpty_write(sd->pty, buf, buflen);
for (i = 0; i < m->count; i++)
{
termpty_write(sd->pty, "\n", 1);
buflen = snprintf(buf, sizeof(buf), "%i", (int)(m->val[i] * 1000.0));
termpty_write(sd->pty, buf, buflen);
}
termpty_write(sd->pty, zero, 1);
}
break;
case EDJE_MESSAGE_STRING_INT:
{
Edje_Message_String_Int *m = msg;
buflen = snprintf(buf, sizeof(buf),
"%c}message;%s\n%i\nstring_int\n%s\n%i", 0x1b,
blk->chid, id, m->str, m->val);
termpty_write(sd->pty, buf, buflen + 1);
}
break;
case EDJE_MESSAGE_STRING_FLOAT:
{
Edje_Message_String_Float *m = msg;
buflen = snprintf(buf, sizeof(buf),
"%c}message;%s\n%i\nstring_float\n%s\n%i", 0x1b,
blk->chid, id, m->str, (int)(m->val * 1000.0));
termpty_write(sd->pty, buf, buflen + 1);
}
break;
case EDJE_MESSAGE_STRING_INT_SET:
{
Edje_Message_String_Int_Set *m = msg;
int i;
char zero[1] = { 0 };
buflen = snprintf(buf, sizeof(buf),
"%c}message;%s\n%i\nstring_int_set\n%i", 0x1b,
blk->chid, id, m->count);
termpty_write(sd->pty, buf, buflen);
termpty_write(sd->pty, "\n", 1);
termpty_write(sd->pty, m->str, strlen(m->str));
for (i = 0; i < m->count; i++)
{
termpty_write(sd->pty, "\n", 1);
buflen = snprintf(buf, sizeof(buf), "%i", m->val[i]);
termpty_write(sd->pty, buf, buflen);
}
termpty_write(sd->pty, zero, 1);
}
break;
case EDJE_MESSAGE_STRING_FLOAT_SET:
{
Edje_Message_String_Float_Set *m = msg;
int i;
char zero[1] = { 0 };
buflen = snprintf(buf, sizeof(buf),
"%c}message;%s\n%i\nstring_float_set\n%i", 0x1b,
blk->chid, id, m->count);
termpty_write(sd->pty, buf, buflen);
termpty_write(sd->pty, "\n", 1);
termpty_write(sd->pty, m->str, strlen(m->str));
for (i = 0; i < m->count; i++)
{
termpty_write(sd->pty, "\n", 1);
buflen = snprintf(buf, sizeof(buf), "%i", (int)(m->val[i] * 1000.0));
termpty_write(sd->pty, buf, buflen);
}
termpty_write(sd->pty, zero, 1);
}
break;
default:
break;
}
}
static void
_block_edje_cmds(Termpty *ty, Termblock *blk, Eina_List *cmds, Eina_Bool created)
{
Eina_List *l;
#define ISCMD(cmd) !strcmp(s, cmd)
#define GETS(var) l = l->next; if (!l) return; var = l->data
#define GETI(var) l = l->next; if (!l) return; var = atoi(l->data)
#define GETF(var) l = l->next; if (!l) return; var = (double)atoi(l->data) / 1000.0
l = cmds;
while (l)
{
char *s = l->data;
/////////////////////////////////////////////////////////////////////
if (ISCMD("text")) // set text part
{
char *prt, *txt;
GETS(prt);
GETS(txt);
edje_object_part_text_set(blk->obj, prt, txt);
}
/////////////////////////////////////////////////////////////////////
else if (ISCMD("emit")) // emit signal
{
char *sig, *src;
GETS(sig);
GETS(src);
edje_object_signal_emit(blk->obj, sig, src);
}
/////////////////////////////////////////////////////////////////////
else if (ISCMD("drag")) // set dragable
{
char *prt, *val;
double v1, v2;
GETS(prt);
GETS(val);
GETF(v1);
GETF(v2);
if (!strcmp(val, "value"))
edje_object_part_drag_value_set(blk->obj, prt, v1, v2);
else if (!strcmp(val, "size"))
edje_object_part_drag_size_set(blk->obj, prt, v1, v2);
else if (!strcmp(val, "step"))
edje_object_part_drag_step_set(blk->obj, prt, v1, v2);
else if (!strcmp(val, "page"))
edje_object_part_drag_page_set(blk->obj, prt, v1, v2);
}
/////////////////////////////////////////////////////////////////////
else if (ISCMD("message")) // send message
{
int id;
char *typ;
GETI(id);
GETS(typ);
if (!strcmp(typ, "string"))
{
Edje_Message_String *m;
m = alloca(sizeof(Edje_Message_String));
GETS(m->str);
edje_object_message_send(blk->obj, EDJE_MESSAGE_STRING,
id, m);
}
else if (!strcmp(typ, "int"))
{
Edje_Message_Int *m;
m = alloca(sizeof(Edje_Message_Int));
GETI(m->val);
edje_object_message_send(blk->obj, EDJE_MESSAGE_INT,
id, m);
}
else if (!strcmp(typ, "float"))
{
Edje_Message_Float *m;
m = alloca(sizeof(Edje_Message_Float));
GETF(m->val);
edje_object_message_send(blk->obj, EDJE_MESSAGE_FLOAT,
id, m);
}
else if (!strcmp(typ, "string_set"))
{
Edje_Message_String_Set *m;
int i, count;
GETI(count);
m = alloca(sizeof(Edje_Message_String_Set) +
((count - 1) * sizeof(char *)));
m->count = count;
for (i = 0; i < m->count; i++)
{
GETS(m->str[i]);
}
edje_object_message_send(blk->obj,
EDJE_MESSAGE_STRING_SET,
id, m);
}
else if (!strcmp(typ, "int_set"))
{
Edje_Message_Int_Set *m;
int i, count;
GETI(count);
m = alloca(sizeof(Edje_Message_Int_Set) +
((count - 1) * sizeof(int)));
m->count = count;
for (i = 0; i < m->count; i++)
{
GETI(m->val[i]);
}
edje_object_message_send(blk->obj,
EDJE_MESSAGE_INT_SET,
id, m);
}
else if (!strcmp(typ, "float_set"))
{
Edje_Message_Float_Set *m;
int i, count;
GETI(count);
m = alloca(sizeof(Edje_Message_Float_Set) +
((count - 1) * sizeof(double)));
m->count = count;
for (i = 0; i < m->count; i++)
{
GETF(m->val[i]);
}
edje_object_message_send(blk->obj,
EDJE_MESSAGE_FLOAT_SET,
id, m);
}
else if (!strcmp(typ, "string_int"))
{
Edje_Message_String_Int *m;
m = alloca(sizeof(Edje_Message_String_Int));
GETS(m->str);
GETI(m->val);
edje_object_message_send(blk->obj, EDJE_MESSAGE_STRING_INT,
id, m);
}
else if (!strcmp(typ, "string_float"))
{
Edje_Message_String_Float *m;
m = alloca(sizeof(Edje_Message_String_Float));
GETS(m->str);
GETF(m->val);
edje_object_message_send(blk->obj, EDJE_MESSAGE_STRING_FLOAT,
id, m);
}
else if (!strcmp(typ, "string_int_set"))
{
Edje_Message_String_Int_Set *m;
int i, count;
GETI(count);
m = alloca(sizeof(Edje_Message_String_Int_Set) +
((count - 1) * sizeof(int)));
GETS(m->str);
m->count = count;
for (i = 0; i < m->count; i++)
{
GETI(m->val[i]);
}
edje_object_message_send(blk->obj,
EDJE_MESSAGE_STRING_INT_SET,
id, m);
}
else if (!strcmp(typ, "string_float_set"))
{
Edje_Message_String_Float_Set *m;
int i, count;
GETI(count);
m = alloca(sizeof(Edje_Message_String_Float_Set) +
((count - 1) * sizeof(double)));
GETS(m->str);
m->count = count;
for (i = 0; i < m->count; i++)
{
GETF(m->val[i]);
}
edje_object_message_send(blk->obj,
EDJE_MESSAGE_STRING_FLOAT_SET,
id, m);
}
}
/////////////////////////////////////////////////////////////////////
else if (ISCMD("chid")) // set callback channel id</