This is an automated email from the git hooks/post-receive script.
git pushed a commit to branch devs/thanatermesis/paste-media
in repository terminology.
View the commit online.
commit 8a431ad67a7b756789bf0fdd69b2d2aff16ae1a1
Author: Samuel F. Baggen <[email protected]>
AuthorDate: Sun Aug 17 23:10:07 2025 -0500
This feature allows to paste images in your current directory
If you have a copied image or selection and you don't know where to paste it in order to save it, do it in your terminology, it will popup a window to ask the filename to save your image
Press Enter or Escape if you just want to save it / exit modal
Signed-off-by: Thanatermesis <[email protected]>
---
src/bin/termio.c | 383 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 381 insertions(+), 2 deletions(-)
diff --git a/src/bin/termio.c b/src/bin/termio.c
index b5017e83..50968235 100644
--- a/src/bin/termio.c
+++ b/src/bin/termio.c
@@ -29,6 +29,59 @@
#endif
+static const char *
+_get_image_extension_from_file_content(const char *filepath)
+{
+ unsigned char magic[8];
+ FILE *f;
+ const char *ext = NULL;
+
+ f = fopen(filepath, "rb");
+ if (!f) return NULL;
+
+ if (fread(magic, 1, sizeof(magic), f) == sizeof(magic))
+ {
+ if (memcmp(magic, "\x89PNG\r\n\x1a\n", 8) == 0) ext = ".png";
+ else if (magic[0] == 0xff && magic[1] == 0xd8 && magic[2] == 0xff) ext = ".jpg";
+ else if (memcmp(magic, "GIF87a", 6) == 0 || memcmp(magic, "GIF89a", 6) == 0) ext = ".gif";
+ else if (memcmp(magic, "BM", 2) == 0) ext = ".bmp";
+ else if (memcmp(magic, "II\x2a\x00", 4) == 0 || memcmp(magic, "MM\x00\x2a", 4) == 0) ext = ".tiff";
+ }
+ fclose(f);
+
+ if (ext) return ext;
+
+ // Check for SVG
+ f = fopen(filepath, "r");
+ if (!f) return NULL;
+ char buf[512];
+ if (fgets(buf, sizeof(buf), f))
+ {
+ char *p = buf;
+ while (isspace((unsigned char)*p)) p++;
+ if (strncasecmp(p, "<svg", 4) == 0)
+ {
+ ext = ".svg";
+ }
+ else if (strncasecmp(p, "<?xml", 5) == 0)
+ {
+ while (fgets(buf, sizeof(buf), f))
+ {
+ p = buf;
+ while (isspace((unsigned char)*p)) p++;
+ if (strncasecmp(p, "<svg", 4) == 0)
+ {
+ ext = ".svg";
+ break;
+ }
+ }
+ }
+ }
+ fclose(f);
+
+ return ext;
+}
+
static Evas_Smart *_smart = NULL;
static Evas_Smart_Class _parent_sc = EVAS_SMART_CLASS_INIT_NULL;
@@ -1175,6 +1228,171 @@ _mouse_in_selection(Termio *sd, int cx, int cy)
return EINA_FALSE;
}
+typedef struct _Save_Image_Data
+{
+ Evas_Object *termio;
+ Evas_Object *popup;
+ Evas_Object *entry;
+ Evas_Object *img_obj;
+} Save_Image_Data;
+
+static void
+_popup_save_del_cb(void *data,
+ Evas *e EINA_UNUSED,
+ Evas_Object *obj EINA_UNUSED,
+ void *event_info EINA_UNUSED)
+{
+ Save_Image_Data *sid = data;
+ evas_object_del(sid->img_obj);
+ free(sid);
+}
+
+static void
+_popup_save_ok_cb(void *data, Evas_Object *obj EINA_UNUSED,
+ void *event_info EINA_UNUSED)
+{
+ Save_Image_Data *sid = data;
+ const char *filename;
+ char cwd[PATH_MAX];
+ char fullpath[PATH_MAX * 2];
+
+ filename = elm_object_text_get(sid->entry);
+ if (filename && filename[0])
+ {
+ if (termio_cwd_get(sid->termio, cwd, sizeof(cwd)))
+ {
+ snprintf(fullpath, sizeof(fullpath), "%s/%s", cwd, filename);
+ if (!evas_object_image_save(sid->img_obj, fullpath, NULL, "quality=100 compress=9"))
+ {
+ ERR("Failed to save image to %s", fullpath);
+ }
+ }
+ }
+ evas_object_del(sid->popup);
+}
+
+static void
+_popup_save_cancel_cb(void *data, Evas_Object *obj EINA_UNUSED,
+ void *event_info EINA_UNUSED)
+{
+ Save_Image_Data *sid = data;
+ evas_object_del(sid->popup);
+}
+
+static void
+_popup_save_keydown_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info)
+{
+ Save_Image_Data *sid = data;
+ Evas_Event_Key_Down *ev = event_info;
+
+ if (strcmp(ev->key, "Escape") == 0)
+ {
+ evas_object_del(sid->popup);
+ }
+}
+
+static void
+_popup_save_image_show(Termio *sd, Evas_Object *img_obj_from_sel)
+{
+ if (!img_obj_from_sel) return;
+
+ Evas *e = evas_object_evas_get(sd->win);
+ Evas_Object *popup, *box, *entry, *btn_box, *ok_btn, *cancel_btn;
+ Evas_Object *new_img;
+ int w, h;
+ const void *img_data;
+ void *new_img_data;
+ Save_Image_Data *sid;
+
+ sid = calloc(1, sizeof(Save_Image_Data));
+ if (!sid) return;
+
+ evas_object_image_size_get(img_obj_from_sel, &w, &h);
+ if ((w < 1) || (h < 1))
+ {
+ free(sid);
+ return;
+ }
+
+ new_img = evas_object_image_filled_add(e);
+ evas_object_image_size_set(new_img, w, h);
+ evas_object_image_alpha_set(new_img, evas_object_image_alpha_get(img_obj_from_sel));
+ evas_object_image_colorspace_set(new_img, evas_object_image_colorspace_get(img_obj_from_sel));
+
+ img_data = evas_object_image_data_get(img_obj_from_sel, EINA_FALSE);
+ if (img_data)
+ {
+ new_img_data = evas_object_image_data_get(new_img, EINA_TRUE);
+ memcpy(new_img_data, img_data, w * h * 4);
+ evas_object_image_data_update_add(new_img, 0, 0, w, h);
+ }
+ else
+ {
+ evas_object_del(new_img);
+ free(sid);
+ ERR("Could not get image data from selection");
+ return;
+ }
+
+ popup = elm_popup_add(sd->win);
+ elm_popup_orient_set(popup, ELM_POPUP_ORIENT_CENTER);
+ evas_object_show(popup);
+
+ box = elm_box_add(popup);
+ elm_box_horizontal_set(box, EINA_FALSE);
+ evas_object_show(box);
+ elm_object_part_text_set(popup, "title,text", "Save image as...");
+
+ entry = elm_entry_add(popup);
+ elm_entry_single_line_set(entry, EINA_TRUE);
+ elm_entry_scrollable_set(entry, EINA_TRUE);
+ {
+ char filename[256];
+ time_t t = time(NULL);
+ struct tm *tm = localtime(&t);
+ strftime(filename, sizeof(filename), "pasted-image-%Y%m%d-%H%M%S.png", tm);
+ elm_object_text_set(entry, filename);
+ }
+ evas_object_size_hint_weight_set(entry, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ evas_object_size_hint_align_set(entry, EVAS_HINT_FILL, EVAS_HINT_FILL);
+ elm_box_pack_end(box, entry);
+ evas_object_show(entry);
+
+ btn_box = elm_box_add(popup);
+ elm_box_horizontal_set(btn_box, EINA_TRUE);
+ elm_box_homogeneous_set(btn_box, EINA_TRUE);
+ elm_box_padding_set(btn_box, 5, 0);
+ evas_object_size_hint_weight_set(btn_box, EVAS_HINT_EXPAND, 0.0);
+ evas_object_size_hint_align_set(btn_box, EVAS_HINT_FILL, EVAS_HINT_FILL);
+
+ ok_btn = elm_button_add(popup);
+ elm_object_text_set(ok_btn, "Save");
+ elm_box_pack_end(btn_box, ok_btn);
+ evas_object_show(ok_btn);
+
+ cancel_btn = elm_button_add(popup);
+ elm_object_text_set(cancel_btn, "Cancel");
+ elm_box_pack_end(btn_box, cancel_btn);
+ evas_object_show(cancel_btn);
+
+ elm_box_pack_end(box, btn_box);
+ evas_object_show(btn_box);
+ elm_object_content_set(popup, box);
+
+ elm_object_focus_set(ok_btn, EINA_TRUE);
+
+ sid->termio = sd->self;
+ sid->popup = popup;
+ sid->entry = entry;
+ sid->img_obj = new_img;
+
+ evas_object_smart_callback_add(entry, "activated", _popup_save_ok_cb, sid);
+ evas_object_smart_callback_add(ok_btn, "clicked", _popup_save_ok_cb, sid);
+ evas_object_smart_callback_add(cancel_btn, "clicked", _popup_save_cancel_cb, sid);
+ evas_object_event_callback_add(popup, EVAS_CALLBACK_KEY_DOWN, _popup_save_keydown_cb, sid);
+ evas_object_event_callback_add(popup, EVAS_CALLBACK_DEL, _popup_save_del_cb, sid);
+}
+
static Eina_Bool
_getsel_cb(void *data,
Evas_Object *_obj EINA_UNUSED,
@@ -1184,10 +1402,12 @@ _getsel_cb(void *data,
EINA_SAFETY_ON_NULL_RETURN_VAL(sd, EINA_FALSE);
+ printf("_getsel_cb: format=%d, len=%zd\n", ev->format, ev->len);
if (ev->format == ELM_SEL_FORMAT_TEXT)
{
char *buf;
+ printf("_getsel_cb: Pasting text.\n");
if (ev->len <= 0) return EINA_TRUE;
buf = calloc(2, ev->len); /* twice in case the paste is only \n */
@@ -1228,6 +1448,22 @@ _getsel_cb(void *data,
free(buf);
}
}
+ else if (ev->format == ELM_SEL_FORMAT_IMAGE)
+ {
+ Evas_Object *img_obj_from_sel;
+
+ printf("_getsel_cb: Pasting image. data="" ev->data ? (const char *)ev->data : "(null)");
+ sscanf((const char *)ev->data, "%p", &img_obj_from_sel);
+ if (img_obj_from_sel)
+ {
+ _popup_save_image_show(sd, img_obj_from_sel);
+ }
+ else
+ {
+ printf("_getsel_cb: could not get image object from selection.\n");
+ WRN("could not get image object from selection");
+ }
+ }
else
{
const char *fmt = "UNKNOWN";
@@ -1249,6 +1485,138 @@ _getsel_cb(void *data,
return EINA_TRUE;
}
+static Eina_Bool
+_getsel_targets_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);
+
+ printf("_getsel_targets_cb: ev->data = "" len=%zd\n", ev->data ? (const char *)ev->data : "(null)", ev->len);
+ if (ev->data &&
+ ((strstr(ev->data, "application/x-elementary-image")) ||
+ (strstr(ev->data, "image/")) ||
+ (strstr(ev->data, "text/uri-list"))
+ ))
+ {
+ printf("_getsel_targets_cb: Found image target, getting image data.\n");
+ elm_cnp_selection_get(sd->win, sd->sel_type, ELM_SEL_FORMAT_IMAGE,
+ _getsel_cb, data);
+ return EINA_TRUE;
+ }
+ else if (ev->data &&
+ (strstr(ev->data, "text/plain") ||
+ strstr(ev->data, "TEXT") ||
+ strstr(ev->data, "UTF8_STRING") ||
+ strstr(ev->data, "STRING"))
+ )
+ {
+ printf("_getsel_targets_cb: Found text target, getting text data.\n");
+ elm_cnp_selection_get(sd->win, sd->sel_type, ELM_SEL_FORMAT_TEXT,
+ _getsel_cb, data);
+ return EINA_TRUE;
+ }
+
+ if (ev->data && ev->len > 0)
+ {
+ char *path = alloca(ev->len + 1);
+ const char *filepath, *orig_filepath;
+ char *new_filepath = NULL;
+ Media_Type type;
+
+ memcpy(path, ev->data, ev->len);
+ path[ev->len] = '\0';
+ filepath = path;
+
+ if (strncmp(filepath, "file://", 7) == 0)
+ filepath += 7;
+ orig_filepath = filepath;
+
+ const char *mime = efreet_mime_type_get(orig_filepath);
+ printf("_getsel_targets_cb: mime type of '%s' is '%s'\n", orig_filepath, mime ? mime : "(null)");
+ const char *ext = NULL;
+
+ if (mime)
+ {
+ if (!strcmp(mime, "image/png")) ext = ".png";
+ else if (!strcmp(mime, "image/jpeg")) ext = ".jpg";
+ else if (!strcmp(mime, "image/gif")) ext = ".gif";
+ else if (!strcmp(mime, "image/bmp")) ext = ".bmp";
+ else if (!strcmp(mime, "image/tiff")) ext = ".tiff";
+ else if (!strcmp(mime, "image/svg+xml")) ext = ".svg";
+ }
+
+ if (!ext)
+ {
+ ext = _get_image_extension_from_file_content(orig_filepath);
+ printf("_getsel_targets_cb: fallback file content detection gave extension '%s'\n", ext ? ext : "(null)");
+ }
+
+ if (ext)
+ {
+ printf("_getsel_targets_cb: determined extension is '%s'\n", ext);
+ const char *current_ext = strrchr(orig_filepath, '.');
+ printf("_getsel_targets_cb: current extension is '%s'\n", current_ext ? current_ext : "(null)");
+ if (!current_ext || strcasecmp(current_ext, ext) != 0)
+ {
+ printf("_getsel_targets_cb: attempting to rename file due to extension mismatch\n");
+ size_t len = strlen(orig_filepath) + strlen(ext) + 1;
+ new_filepath = malloc(len);
+ if (new_filepath)
+ {
+ snprintf(new_filepath, len, "%s%s", orig_filepath, ext);
+ if (ecore_file_mv(orig_filepath, new_filepath))
+ {
+ printf("_getsel_targets_cb: renamed '%s' to '%s'\n", orig_filepath, new_filepath);
+ filepath = new_filepath;
+ }
+ else
+ {
+ printf("_getsel_targets_cb: failed to rename '%s' to '%s'\n", orig_filepath, new_filepath);
+ free(new_filepath);
+ new_filepath = NULL;
+ }
+ }
+ }
+ }
+
+ type = media_src_type_get(filepath, strlen(filepath));
+ printf("_getsel_targets_cb: checking path '%s', type %d\n", filepath, type);
+
+ if ((type == MEDIA_TYPE_IMG) || (type == MEDIA_TYPE_SCALE))
+ {
+ Evas *e = evas_object_evas_get(sd->win);
+ Evas_Object *img = evas_object_image_filled_add(e);
+ evas_object_image_file_set(img, filepath, NULL);
+ const Evas_Load_Error err = evas_object_image_load_error_get(img);
+ if (err == EVAS_LOAD_ERROR_NONE)
+ {
+ printf("_getsel_targets_cb: loaded image from path '%s'\n", filepath);
+ _popup_save_image_show(sd, img);
+ evas_object_del(img);
+ if (new_filepath)
+ {
+ ecore_file_mv(new_filepath, orig_filepath);
+ free(new_filepath);
+ }
+ return EINA_TRUE;
+ }
+ printf("_getsel_targets_cb: failed to load image from path '%s', error: %d\n", filepath, err);
+ evas_object_del(img);
+ }
+ if (new_filepath)
+ {
+ ecore_file_mv(new_filepath, orig_filepath);
+ free(new_filepath);
+ }
+ }
+
+ printf("_getsel_targets_cb: No supported target found. Available targets: '%s'\n",
+ ev->data ? (const char *)ev->data : "(null)");
+ return EINA_TRUE;
+}
+
void
termio_paste_selection(Evas_Object *obj, Elm_Sel_Type type)
@@ -1257,8 +1625,19 @@ termio_paste_selection(Evas_Object *obj, Elm_Sel_Type type)
EINA_SAFETY_ON_NULL_RETURN(sd);
if (!sd->win)
return;
- elm_cnp_selection_get(sd->win, type, ELM_SEL_FORMAT_TEXT,
- _getsel_cb, obj);
+ sd->sel_type = type;
+
+ printf("termio_paste_selection: type=%d\n", type);
+ if ((type == ELM_SEL_TYPE_PRIMARY) || (type == ELM_SEL_TYPE_SECONDARY))
+ {
+ elm_cnp_selection_get(sd->win, type, ELM_SEL_FORMAT_TEXT,
+ _getsel_cb, obj);
+ }
+ else
+ {
+ elm_cnp_selection_get(sd->win, type, ELM_SEL_FORMAT_TARGETS,
+ _getsel_targets_cb, obj);
+ }
}
static const char *
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.