eruler/src/bin/main.c

2812 lines
77 KiB
C

#include "private.h"
#include <Ecore_Getopt.h>
#include <Elementary_Cursor.h>
#include <Ecore_File.h>
static void
_group_smart_set_user(Evas_Smart_Class *sc EINA_UNUSED)
{
}
EVAS_SMART_SUBCLASS_NEW("Group", _group,
Evas_Smart_Class, Evas_Smart_Class,
evas_object_smart_clipped_class_get, NULL);
static Evas_Object *
group_add(Evas *evas)
{
return evas_object_smart_add(evas, _group_smart_class_new());
}
struct _Zone {
Evas *evas;
Evas_Object *win;
Evas_Object *event;
Evas_Object *handling;
Evas_Object *hint;
Evas_Object *guide_h;
Evas_Object *guide_v;
Evas_Object *display_pos;
Evas_Object *cmdbox;
Evas_Object *help_popup;
Evas_Object *error_popup;
Ecore_Idler *del_help_popup_idler;
struct {
Evas_Object *frame;
Evas_Object *main_box;
Evas_Object *create;
Evas_Object *clear;
Evas_Object *style;
Evas_Object *zoom;
Evas_Object *show_guides;
Evas_Object *show_distances;
Evas_Object *hex_colors;
struct {
int x, y, dx, dy;
Ecore_Animator *anim;
} moving;
} gui;
struct {
Evas_Object *frame;
Evas_Object *image;
struct {
int w, h;
} ideal_size;
int factor;
Eina_Bool ready : 1;
} zoom;
struct {
Ecore_Evas *ee;
Evas_Object *image; /* lives inside ee */
/* the following are inside zone->win */
Evas_Object *popup;
Evas_Object *save_bt;
Evas_Object *cancel_bt;
struct {
Evas_Object *entry;
Evas_Object *button;
Evas_Object *popup;
Evas_Object *selector;
} file;
Evas_Object *preview;
Ecore_Timer *timer;
} screenshot;
struct {
Evas_Object *rulers;
Evas_Object *distances;
} group;
Eina_List *rulers;
Eina_List *distances;
Ecore_Animator *tracker;
int idx;
int x, y, w, h;
struct {
int x, y;
} last_mouse;
unsigned int color;
Eina_Bool last_ruler_used : 1;
Eina_Bool keyboard_move : 1;
};
static Eina_List *zones;
static const Platform_Funcs *platform_funcs;
static Eina_Bool show_distances = EINA_TRUE;
static Eina_Bool show_guides = EINA_TRUE;
static Eina_Bool hex_colors = EINA_FALSE;
static Eina_Bool visible = EINA_TRUE;
static int retval = EXIT_SUCCESS;
int _log_dom = -1;
enum ruler_type {
RULER_TYPE_DEFAULT = 0,
RULER_TYPE_LIGHT,
RULER_TYPE_DARK,
RULER_TYPE_LIGHT_FILLED,
RULER_TYPE_DARK_FILLED,
RULER_TYPE_GOOD,
RULER_TYPE_WARNING,
RULER_TYPE_BAD,
RULER_TYPE_SENTINEL
};
static enum ruler_type initial_ruler_type = RULER_TYPE_DEFAULT;
static char theme_file[PATH_MAX];
#define RULER_TYPE_PREFIX "eruler/rule/"
static const char *ruler_type_names_strs[] = {
[RULER_TYPE_DEFAULT] = "default",
[RULER_TYPE_LIGHT] = "light",
[RULER_TYPE_DARK] = "dark",
[RULER_TYPE_LIGHT_FILLED] = "light-filled",
[RULER_TYPE_DARK_FILLED] = "dark-filled",
[RULER_TYPE_GOOD] = "good",
[RULER_TYPE_WARNING] = "warning",
[RULER_TYPE_BAD] = "bad",
[RULER_TYPE_SENTINEL] = NULL,
};
#define N_RULER_TYPES (EINA_C_ARRAY_LENGTH(ruler_type_names_strs) - 1)
typedef struct _Distance Distance;
typedef struct _Ruler_Data Ruler_Data;
struct _Distance {
Zone *zone;
Evas_Object *a, *b;
Evas_Object *top, *bottom, *left, *right;
};
struct _Ruler_Data {
Zone *zone;
struct {
int x, y;
} start, stop;
Eina_List *distances;
enum ruler_type type;
};
static Eina_Bool
theme_apply(Evas_Object *edje, const char *group)
{
const char *errmsg;
EINA_SAFETY_ON_NULL_RETURN_VAL(edje, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(group, EINA_FALSE);
if (edje_object_file_set(edje, theme_file, group))
return EINA_TRUE;
errmsg = edje_load_error_str(edje_object_load_error_get(edje));
CRI("Cannot find theme: file=%s group=%s error='%s'",
theme_file, group, errmsg);
return EINA_FALSE;
}
static void
set_color(const Zone *zone)
{
char buf[64];
if (hex_colors)
snprintf(buf, sizeof(buf), "#%06x", zone->color);
else
{
snprintf(buf, sizeof(buf), "%d %d %d",
(zone->color >> 16) & 0xff,
(zone->color >> 8) & 0xff,
(zone->color & 0xff));
}
edje_object_part_text_set(zone->zoom.frame, "color", buf);
}
static void
show_hexa_apply(void)
{
const Eina_List *l;
const Zone *zone;
EINA_LIST_FOREACH(zones, l, zone)
{
elm_check_state_set(zone->gui.hex_colors, hex_colors);
set_color(zone);
}
}
static void
show_guides_apply(void)
{
const Eina_List *l;
const Zone *zone;
EINA_LIST_FOREACH(zones, l, zone)
{
if (show_guides)
{
evas_object_move(zone->guide_v, zone->last_mouse.x, 0);
evas_object_resize(zone->guide_v, 1, zone->h);
evas_object_show(zone->guide_v);
evas_object_move(zone->guide_h, 0, zone->last_mouse.y);
evas_object_resize(zone->guide_h, zone->w, 1);
evas_object_show(zone->guide_h);
}
else
{
evas_object_hide(zone->guide_v);
evas_object_hide(zone->guide_h);
}
elm_check_state_set(zone->gui.show_guides, show_guides);
}
}
static void
show_distances_apply(void)
{
const Eina_List *l;
const Zone *zone;
EINA_LIST_FOREACH(zones, l, zone)
{
if (show_distances && zone->distances)
evas_object_show(zone->group.distances);
else
evas_object_hide(zone->group.distances);
elm_check_state_set(zone->gui.show_distances,
show_distances);
}
}
static Ruler_Data *
ruler_data_get(const Evas_Object *o)
{
return evas_object_data_get(o, "ruler_data");
}
static Evas_Object *
zone_ruler_last_get(const Zone *zone)
{
return eina_list_data_get(eina_list_last(zone->rulers));
}
static void distance_update(Distance *d);
static void
_ruler_distances_update(Evas_Object *ruler)
{
Ruler_Data *rd = ruler_data_get(ruler);
const Eina_List *l;
Distance *d;
EINA_LIST_FOREACH(rd->distances, l, d)
distance_update(d);
}
static void
zone_last_ruler_used_set(Zone *zone, Eina_Bool used)
{
Evas_Object *ruler;
zone->last_ruler_used = used;
ruler = zone_ruler_last_get(zone);
_ruler_distances_update(ruler);
if (zone->gui.frame)
{
elm_object_disabled_set(zone->gui.create, !used);
if ((!used) && (eina_list_count(zone->rulers) == 1))
elm_object_disabled_set(zone->gui.clear, EINA_TRUE);
else if (eina_list_count(zone->rulers) > 1)
elm_object_disabled_set(zone->gui.clear, EINA_FALSE);
else
elm_object_disabled_set(zone->gui.clear, !used);
}
}
static void
zone_rulers_clear(Zone *zone)
{
Evas_Object *ruler;
EINA_LIST_FREE(zone->rulers, ruler)
evas_object_del(ruler);
}
static void
_ruler_state_update(Evas_Object *ruler)
{
const Ruler_Data *rd = ruler_data_get(ruler);
Edje_Message_Int_Set *msg;
int x, y, w, h, dx, dy;
x = rd->start.x;
y = rd->start.y;
dx = w = rd->stop.x - rd->start.x;
dy = h = rd->stop.y - rd->start.y;
if (w < 0)
{
w = -w;
x -= w;
}
if (h < 0)
{
h = -h;
y -= h;
}
w++;
h++;
dx = dx < 0 ? -w : w;
dy = dy < 0 ? -h : h;
evas_object_move(ruler, x, y);
evas_object_resize(ruler, w, h);
msg = alloca(sizeof(Edje_Message_Int_Set) + sizeof(int));
msg->count = 2;
msg->val[0] = rd->start.x;
msg->val[1] = rd->start.y;
edje_object_message_send(ruler, EDJE_MESSAGE_INT_SET, 0, msg);
msg->val[0] = dx;
msg->val[1] = dy;
edje_object_message_send(ruler, EDJE_MESSAGE_INT_SET, 1, msg);
}
static void _zone_gui_style_label_update(Zone *zone);
static void
ruler_type_apply(Evas_Object *ruler)
{
Ruler_Data *rd = ruler_data_get(ruler);
char buf[128];
eina_strlcpy(buf, RULER_TYPE_PREFIX, sizeof(buf));
eina_strlcat(buf, ruler_type_names_strs[rd->type], sizeof(buf));
theme_apply(ruler, buf);
_ruler_state_update(ruler);
if (rd->zone)
_zone_gui_style_label_update(rd->zone);
}
static void
ruler_move_relative(Evas_Object *ruler, int dx, int dy)
{
Ruler_Data *rd;
DBG("place ruler %p relative by %d, %d", ruler, dx, dy);
rd = ruler_data_get(ruler);
rd->start.x += dx;
rd->start.y += dy;
rd->stop.x += dx;
rd->stop.y += dy;
_ruler_state_update(ruler);
evas_object_show(ruler);
}
static void
ruler_resize_relative(Evas_Object *ruler, int dw, int dh)
{
Ruler_Data *rd;
DBG("resize ruler %p relative by %d, %d", ruler, dw, dh);
rd = ruler_data_get(ruler);
rd->stop.x += dw;
rd->stop.y += dh;
_ruler_state_update(ruler);
evas_object_show(ruler);
}
static void
ruler_place(Evas_Object *ruler, int x, int y, int w, int h)
{
Ruler_Data *rd;
DBG("place ruler %p at %d,%d size %dx%d", ruler, x, y, w, h);
rd = ruler_data_get(ruler);
rd->start.x = x;
rd->start.y = y;
rd->stop.x = x + w - 1;
rd->stop.y = y + h - 1;
_ruler_state_update(ruler);
evas_object_show(ruler);
}
static Eina_Bool
_event_mouse_tracker(void *data)
{
Zone *zone = data;
Evas_Coord x, y, dx, dy, dw, dh, gx, gy, gw, gh;
char buf[64];
if (zone->screenshot.ee) return EINA_TRUE;
evas_pointer_canvas_xy_get(zone->evas, &x, &y);
if ((x < 0) || (x >= zone->w) || (y < 0) || (y >= zone->h))
return EINA_TRUE;
if ((x == zone->last_mouse.x) && (y == zone->last_mouse.y))
return EINA_TRUE;
zone->last_mouse.x = x;
zone->last_mouse.y = y;
evas_object_geometry_get(zone->gui.frame, &gx, &gy, &gw, &gh);
if (((x >= gx) && (x < gx + gw)) &&
((y >= gy) && (y < gy + gh)))
{
evas_object_hide(zone->display_pos);
evas_object_hide(zone->guide_v);
evas_object_hide(zone->guide_h);
if (zone->zoom.frame)
evas_object_hide(zone->zoom.frame);
return EINA_TRUE;
}
evas_object_show(zone->display_pos);
if (zone->handling)
{
Ruler_Data *rd = ruler_data_get(zone->handling);
rd->stop.x = x;
rd->stop.y = y;
_ruler_state_update(zone->handling);
evas_object_show(zone->handling);
}
if (show_guides)
{
evas_object_move(zone->guide_v, x, 0);
evas_object_resize(zone->guide_v, 1, zone->h);
evas_object_show(zone->guide_v);
evas_object_move(zone->guide_h, 0, y);
evas_object_resize(zone->guide_h, zone->w, 1);
evas_object_show(zone->guide_h);
}
snprintf(buf, sizeof(buf), "%d,%d", x, y);
edje_object_part_text_set(zone->display_pos, "text", buf);
edje_object_size_min_calc(zone->display_pos, &dw, &dh);
dx = x - dw - 10;
if (dx < 0)
dx = x + 10;
dy = y - dh - 10;
if (dy < 0)
dy = y + 10;
evas_object_move(zone->display_pos, dx, dy);
evas_object_resize(zone->display_pos, dw, dh);
if (zone->zoom.ready)
{
int fx, fy, fw, fh, zx, zy, zw, zh, stride, iw, ih;
unsigned int *pixels;
zw = zone->zoom.ideal_size.w < zone->w / 4 ?
zone->zoom.ideal_size.w : zone->w / 4;
zh = zone->zoom.ideal_size.h < zone->h / 4 ?
zone->zoom.ideal_size.h : zone->h / 4;
fx = -x * zone->zoom.factor + zw / 2 - zone->zoom.factor / 2;
fy = -y * zone->zoom.factor + zh / 2 - zone->zoom.factor / 2;
fw = zone->w * zone->zoom.factor;
fh = zone->h * zone->zoom.factor;
zx = x + 10;
zy = y + 10;
if (zx + zw > zone->w)
zx = dx - zw - 10;
if (zy + zh > zone->h)
zy = dy - zh - 10;
evas_object_move(zone->zoom.frame, zx, zy);
evas_object_resize(zone->zoom.frame, zw, zh);
evas_object_image_fill_set(zone->zoom.image, fx, fy, fw, fh);
pixels = evas_object_image_data_get(zone->zoom.image, EINA_FALSE);
stride = evas_object_image_stride_get(zone->zoom.image);
evas_object_image_size_get(zone->zoom.image, &iw, &ih);
EINA_SAFETY_ON_NULL_RETURN_VAL(pixels, EINA_TRUE);
EINA_SAFETY_ON_FALSE_GOTO(x >= 0, position_invalid);
EINA_SAFETY_ON_FALSE_GOTO(y >= 0, position_invalid);
EINA_SAFETY_ON_FALSE_GOTO(x < iw, position_invalid);
EINA_SAFETY_ON_FALSE_GOTO(y < ih, position_invalid);
zone->color = pixels[y * (stride / sizeof(unsigned int)) + x];
zone->color = zone->color & 0xffffff;
set_color(zone);
position_invalid:
evas_object_image_data_set(zone->zoom.image, pixels);
evas_object_show(zone->zoom.frame);
}
return EINA_TRUE;
}
static void
_handling_start(Zone *zone)
{
Evas_Object *ruler;
Ruler_Data *rd;
Edje_Message_Int_Set *msg;
ruler = zone_ruler_last_get(zone);
EINA_SAFETY_ON_NULL_RETURN(ruler);
zone->handling = ruler;
rd = ruler_data_get(ruler);
zone_last_ruler_used_set(zone, EINA_TRUE);
evas_pointer_canvas_xy_get(zone->evas, &rd->start.x, &rd->start.y);
evas_object_move(ruler, rd->start.x, rd->start.y);
evas_object_show(ruler);
msg = alloca(sizeof(Edje_Message_Int_Set) + sizeof(int));
msg->count = 2;
msg->val[0] = rd->start.x;
msg->val[1] = rd->start.y;
edje_object_message_send(ruler, EDJE_MESSAGE_INT_SET, 0, msg);
zone->last_mouse.x = -1;
zone->last_mouse.y = -1;
_event_mouse_tracker(zone);
}
static void
_handling_stop(Zone *zone)
{
zone->handling = NULL;
}
static void
_event_mouse_down(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *event)
{
Zone *zone = data;
Evas_Event_Mouse_Down *ev = event;
if (zone->handling)
return;
if (ev->button != 1)
return;
if (zone->cmdbox)
evas_object_del(zone->cmdbox);
zone->keyboard_move = EINA_FALSE;
_handling_start(zone);
}
static void
_event_mouse_up(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *event EINA_UNUSED)
{
Zone *zone = data;
_handling_stop(zone);
}
static void
_event_del(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *event EINA_UNUSED)
{
Zone *zone = data;
if (zone->tracker)
{
ecore_animator_del(zone->tracker);
zone->tracker = NULL;
}
zone_rulers_clear(zone);
}
static void
zone_hint_setup(Zone *zone)
{
zone->hint = edje_object_add(zone->evas);
if (!theme_apply(zone->hint, "eruler/hint"))
return;
evas_object_repeat_events_set(zone->hint, EINA_TRUE);
evas_object_show(zone->hint);
edje_object_part_text_set(zone->hint, "hint",
"<title>ERuler</><br>"
"Press <hilight>F1</hilight> for help or "
"<hilight>Escape</hilight> to quit.");
evas_object_size_hint_weight_set(zone->hint,
EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(zone->hint, EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_win_resize_object_add(zone->win, zone->hint);
zone->guide_h = evas_object_rectangle_add(zone->evas);
evas_object_pass_events_set(zone->guide_h, EINA_TRUE);
evas_object_color_set(zone->guide_h, 128, 0, 0, 128);
zone->guide_v = evas_object_rectangle_add(zone->evas);
evas_object_pass_events_set(zone->guide_v, EINA_TRUE);
evas_object_color_set(zone->guide_v, 128, 0, 0, 128);
zone->display_pos = edje_object_add(zone->evas);
if (!theme_apply(zone->display_pos, "eruler/display_pos"))
return;
evas_object_pass_events_set(zone->display_pos, EINA_TRUE);
evas_object_show(zone->display_pos);
}
static void
_ruler_free(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *event EINA_UNUSED)
{
Ruler_Data *rd = data;
eina_list_free(rd->distances);
free(rd);
}
static void
_distance_source_changed(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *event EINA_UNUSED)
{
Distance *d = data;
distance_update(d);
}
static void
distance_update(Distance *d)
{
int ax1, ay1, ax2, ay2, aw, ah;
int bx1, by1, bx2, by2, bw, bh;
int x, y, w, h;
char buf[32];
if (!d->zone->last_ruler_used)
{
Evas_Object *last = zone_ruler_last_get(d->zone);
if ((last == d->a) || (last == d->b))
{
DBG("Hide distance to unused ruler %p (a: %p, b: %p)",
last, d->a, d->b);
evas_object_hide(d->top);
evas_object_hide(d->bottom);
evas_object_hide(d->left);
evas_object_hide(d->right);
return;
}
}
evas_object_geometry_get(d->a, &ax1, &ay1, &aw, &ah);
evas_object_geometry_get(d->b, &bx1, &by1, &bw, &bh);
ax2 = ax1 + aw;
ay2 = ay1 + ah;
bx2 = bx1 + bw;
by2 = by1 + bh;
/* horizontal distances */
if (ax2 < bx1)
{
x = ax2;
w = bx1 - ax2;
}
else if (ax1 > bx2)
{
x = bx2;
w = ax1 - bx2;
}
else
{
int x2 = ax2 < bx2 ? ax2 : bx2; /* the left one */
x = ax1 < bx1 ? bx1 : ax1; /* the right one */
w = x2 - x;
}
if (ay2 < by1)
{
evas_object_hide(d->top);
evas_object_move(d->bottom, x, ay2);
evas_object_resize(d->bottom, w, (by1 - ay2));
snprintf(buf, sizeof(buf), "%d", (by1 - ay2));
edje_object_part_text_set(d->bottom, "display", buf);
evas_object_show(d->bottom);
}
else if (ay1 > by2)
{
evas_object_hide(d->bottom);
evas_object_move(d->top, x, by2);
evas_object_resize(d->top, w, (ay1 - by2));
snprintf(buf, sizeof(buf), "%d", (ay1 - by2));
edje_object_part_text_set(d->top, "display", buf);
evas_object_show(d->top);
}
else
{
if (ay1 < by1)
{
y = ay1;
h = by1 - ay1 + 1;
}
else
{
y = by1;
h = ay1 - by1 + 1;
}
evas_object_move(d->top, x, y);
evas_object_resize(d->top, w, h);
snprintf(buf, sizeof(buf), "%d", h);
edje_object_part_text_set(d->top, "display", buf);
evas_object_show(d->top);
if (ay2 < by2)
{
y = ay2;
h = by2 - ay2 + 1;
}
else
{
y = by2;
h = ay2 - by2 + 1;
}
evas_object_move(d->bottom, x, y);
evas_object_resize(d->bottom, w, h);
snprintf(buf, sizeof(buf), "%d", h);
edje_object_part_text_set(d->bottom, "display", buf);
evas_object_show(d->bottom);
}
/* horizontal distances */
if (ay2 < by1)
{
y = ay2;
h = by1 - ay2;
}
else if (ay1 > by2)
{
y = by2;
h = ay1 - by2;
}
else
{
int y2 = ay2 < by2 ? ay2 : by2; /* the top one */
y = ay1 < by1 ? by1 : ay1; /* the bottom one */
h = y2 - y;
}
if (ax2 < bx1)
{
evas_object_hide(d->left);
evas_object_move(d->right, ax2, y);
evas_object_resize(d->right, (bx1 - ax2), h);
snprintf(buf, sizeof(buf), "%d", (bx1 - ax2));
edje_object_part_text_set(d->right, "display", buf);
evas_object_show(d->right);
}
else if (ax1 > bx2)
{
evas_object_hide(d->right);
evas_object_move(d->left, bx2, y);
evas_object_resize(d->left, (ax1 - bx2), h);
snprintf(buf, sizeof(buf), "%d", (ax1 - bx2));
edje_object_part_text_set(d->left, "display", buf);
evas_object_show(d->left);
}
else
{
if (ax1 < bx1)
{
x = ax1;
w = bx1 - ax1 + 1;
}
else
{
x = bx1;
w = ax1 - bx1 + 1;
}
evas_object_move(d->left, x, y);
evas_object_resize(d->left, w, h);
snprintf(buf, sizeof(buf), "%d", w);
edje_object_part_text_set(d->left, "display", buf);
evas_object_show(d->left);
if (ax2 < bx2)
{
x = ax2;
w = bx2 - ax2 + 1;
}
else
{
x = bx2;
w = ax2 - bx2 + 1;
}
evas_object_move(d->right, x, y);
evas_object_resize(d->right, w, h);
snprintf(buf, sizeof(buf), "%d", w);
edje_object_part_text_set(d->right, "display", buf);
evas_object_show(d->right);
}
}
static void _distance_source_del(void *data, Evas *e EINA_UNUSED, Evas_Object *o, void *event EINA_UNUSED);
static void
distance_del(Distance *d)
{
Ruler_Data *rd;
Zone *zone = d->zone;
/* 'a' or 'b' may have vanished (_distance_source_del) and will be NULL */
if (d->a)
{
rd = ruler_data_get(d->a);
rd->distances = eina_list_remove(rd->distances, d);
evas_object_event_callback_del_full(d->a, EVAS_CALLBACK_DEL,
_distance_source_del, d);
evas_object_event_callback_del_full(d->a, EVAS_CALLBACK_MOVE,
_distance_source_changed, d);
evas_object_event_callback_del_full(d->a, EVAS_CALLBACK_RESIZE,
_distance_source_changed, d);
}
if (d->b)
{
rd = ruler_data_get(d->b);
rd->distances = eina_list_remove(rd->distances, d);
evas_object_event_callback_del_full(d->b, EVAS_CALLBACK_DEL,
_distance_source_del, d);
evas_object_event_callback_del_full(d->b, EVAS_CALLBACK_MOVE,
_distance_source_changed, d);
evas_object_event_callback_del_full(d->b, EVAS_CALLBACK_RESIZE,
_distance_source_changed, d);
}
evas_object_del(d->top);
evas_object_del(d->bottom);
evas_object_del(d->left);
evas_object_del(d->right);
zone->distances = eina_list_remove(zone->distances, d);
free(d);
if (!zone->distances)
evas_object_hide(zone->group.distances);
}
static void
_distance_source_del(void *data, Evas *e EINA_UNUSED, Evas_Object *o, void *event EINA_UNUSED)
{
Distance *d = data;
/* if we delete 'a' we need to stop monitoring 'b' to avoid double free
* and remove from 'b' distances to not mess with deleted distance
*/
if (o == d->a)
d->a = NULL;
else
d->b = NULL;
distance_del(d);
}
static void
distance_create(Zone *zone, Evas_Object *a, Evas_Object *b)
{
Ruler_Data *rd;
Distance *d;
d = calloc(1, sizeof(Distance));
EINA_SAFETY_ON_NULL_RETURN(d);
d->zone = zone;
d->a = a;
d->b = b;
evas_object_event_callback_add(a, EVAS_CALLBACK_DEL,
_distance_source_del, d);
evas_object_event_callback_add(a, EVAS_CALLBACK_MOVE,
_distance_source_changed, d);
evas_object_event_callback_add(a, EVAS_CALLBACK_RESIZE,
_distance_source_changed, d);
rd = ruler_data_get(d->a);
rd->distances = eina_list_append(rd->distances, d);
evas_object_event_callback_add(b, EVAS_CALLBACK_DEL,
_distance_source_del, d);
evas_object_event_callback_add(b, EVAS_CALLBACK_MOVE,
_distance_source_changed, d);
evas_object_event_callback_add(b, EVAS_CALLBACK_RESIZE,
_distance_source_changed, d);
rd = ruler_data_get(d->b);
rd->distances = eina_list_append(rd->distances, d);
/* clipper was hidden if clipping nothing */
if ((!zone->distances) && (show_distances))
evas_object_show(zone->group.distances);
zone->distances = eina_list_append(zone->distances, d);
d->top = edje_object_add(zone->evas);
theme_apply(d->top, "eruler/distance_vertical");
evas_object_smart_member_add(d->top, zone->group.distances);
d->bottom = edje_object_add(zone->evas);
theme_apply(d->bottom, "eruler/distance_vertical");
evas_object_smart_member_add(d->bottom, zone->group.distances);
d->left = edje_object_add(zone->evas);
theme_apply(d->left, "eruler/distance_horizontal");
evas_object_smart_member_add(d->left, zone->group.distances);
d->right = edje_object_add(zone->evas);
theme_apply(d->right, "eruler/distance_horizontal");
evas_object_smart_member_add(d->right, zone->group.distances);
}
static void
zone_ruler_create(Zone *zone)
{
Evas_Object *ruler;
Ruler_Data *rd;
rd = calloc(1, sizeof(Ruler_Data));
EINA_SAFETY_ON_NULL_RETURN(rd);
rd->zone = zone;
ruler = edje_object_add(zone->evas);
evas_object_smart_member_add(ruler, zone->group.rulers);
evas_object_event_callback_add(ruler, EVAS_CALLBACK_FREE,
_ruler_free, rd);
evas_object_pass_events_set(ruler, EINA_TRUE);
evas_object_data_set(ruler, "ruler_data", rd);
rd->type = initial_ruler_type;
if (zone->rulers)
{
Evas_Object *other;
Eina_List *l;
EINA_LIST_FOREACH(zone->rulers, l, other)
distance_create(zone, other, ruler);
}
zone->rulers = eina_list_append(zone->rulers, ruler);
ruler_type_apply(ruler);
zone_last_ruler_used_set(zone, EINA_FALSE);
}
static void
zone_ruler_setup(Zone *zone)
{
zone->event = evas_object_rectangle_add(zone->evas);
evas_object_color_set(zone->event, 0, 0, 0, 0);
evas_object_repeat_events_set(zone->event, EINA_TRUE);
evas_object_show(zone->event);
evas_object_event_callback_add(zone->event, EVAS_CALLBACK_MOUSE_DOWN,
_event_mouse_down, zone);
evas_object_event_callback_add(zone->event, EVAS_CALLBACK_MOUSE_UP,
_event_mouse_up, zone);
evas_object_event_callback_add(zone->event, EVAS_CALLBACK_DEL,
_event_del, zone);
evas_object_size_hint_weight_set(zone->event,
EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(zone->event, EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_win_resize_object_add(zone->win, zone->event);
zone->group.distances = group_add(zone->evas);
evas_object_size_hint_weight_set(zone->group.distances,
EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(zone->group.distances,
EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_win_resize_object_add(zone->win, zone->group.distances);
zone->group.rulers = group_add(zone->evas);
evas_object_size_hint_weight_set(zone->group.rulers,
EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(zone->group.rulers,
EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_win_resize_object_add(zone->win, zone->group.rulers);
evas_object_show(zone->group.rulers);
zone_ruler_create(zone);
zone->tracker = ecore_animator_add(_event_mouse_tracker, zone);
}
static void
_zone_gui_resize_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *event EINA_UNUSED)
{
Zone *zone = data;
int x, y, w, h;
evas_object_geometry_get(zone->gui.frame, &x, &y, NULL, NULL);
evas_object_size_hint_min_get(zone->gui.frame, &w, &h);
if (x + w > zone->w - 10)
x = zone->w - 10 - w;
if (y + h > zone->h - 10)
y = zone->h - 10 - h;
if (x < 10)
x = 10;
if (y < 10)
y = 10;
evas_object_move(zone->gui.frame, x, y);
evas_object_resize(zone->gui.frame, w, h);
}
static void
_zone_gui_create(void *data, Evas_Object *o EINA_UNUSED, void *event EINA_UNUSED)
{
Zone *zone = data;
if (zone->last_ruler_used)
zone_ruler_create(zone);
}
static void
_zone_gui_clear(void *data, Evas_Object *o EINA_UNUSED, void *event EINA_UNUSED)
{
Zone *zone = data;
zone_rulers_clear(zone);
zone_ruler_create(zone);
}
static void create_ruler_from_cmdbox(Zone *zone);
static void
_zone_gui_type(void *data, Evas_Object *o EINA_UNUSED, void *event EINA_UNUSED)
{
Zone *zone = data;
create_ruler_from_cmdbox(zone);
}
static void
_zone_gui_style_label_update(Zone *zone)
{
Evas_Object *ruler;
Ruler_Data *rd;
char buf[128];
if (!zone->gui.style) return;
ruler = zone_ruler_last_get(zone);
rd = ruler_data_get(ruler);
snprintf(buf, sizeof(buf), "Style: %s", ruler_type_names_strs[rd->type]);
elm_object_text_set(zone->gui.style, buf);
}
static void
_zone_gui_style_changed(void *data, Evas_Object *o EINA_UNUSED, void *event)
{
Zone *zone = data;
Evas_Object *ruler;
Ruler_Data *rd;
Elm_Object_Item *it = event;
const char *style = elm_object_item_text_get(it);
unsigned int i;
for (i = 0; ruler_type_names_strs[i] != NULL; i++)
{
if (strcmp(ruler_type_names_strs[i], style) == 0)
break;
}
EINA_SAFETY_ON_NULL_RETURN(ruler_type_names_strs[i]);
ruler = zone_ruler_last_get(zone);
EINA_SAFETY_ON_NULL_RETURN(ruler);
rd = ruler_data_get(ruler);
rd->type = i;
ruler_type_apply(ruler);
}
static void zone_zoom_pre_setup(Zone *zone);
static void _zone_screen_copy_cb(void *data, Eina_Bool success);
static void
_zone_gui_zoom_changed(void *data, Evas_Object *o, void *event EINA_UNUSED)
{
Zone *zone = data;
Eina_Bool state = elm_check_state_get(o);
if (state)
{
if (!zone->zoom.image)
{
zone_zoom_pre_setup(zone);
platform_funcs->zone_screen_copy(zone, zone->zoom.image,
_zone_screen_copy_cb,
zone);
}
}
else
{
if (zone->zoom.image)
{
evas_object_del(zone->zoom.image);
zone->zoom.image = NULL;
evas_object_del(zone->zoom.frame);
zone->zoom.frame = NULL;
zone->zoom.ready = EINA_FALSE;
}
}
}
static void
_zone_gui_show_hex_colors_changed(void *data EINA_UNUSED, Evas_Object *o, void *event EINA_UNUSED)
{
Eina_Bool state = elm_check_state_get(o);
hex_colors = state;
show_hexa_apply();
}
static void
_zone_gui_show_guides_changed(void *data EINA_UNUSED, Evas_Object *o, void *event EINA_UNUSED)
{
Eina_Bool state = elm_check_state_get(o);
show_guides = state;
show_guides_apply();
}
static void
_zone_gui_show_distances_changed(void *data EINA_UNUSED, Evas_Object *o, void *event EINA_UNUSED)
{
Eina_Bool state = elm_check_state_get(o);
show_distances = state;
show_distances_apply();
}
static void show_gui_help(Zone *zone);
static void
_zone_gui_help(void *data, Evas_Object *o EINA_UNUSED, void *event EINA_UNUSED)
{
Zone *zone = data;
show_gui_help(zone);
}
static void
_zone_gui_exit(void *data EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *event EINA_UNUSED)
{
elm_exit();
}
static void create_screenshot(Zone *zone);
static void
_zone_gui_shot(void *data, Evas_Object *o EINA_UNUSED, void *event EINA_UNUSED)
{
Zone *zone = data;
create_screenshot(zone);
}
static void
_zone_gui_widget_setup(Evas_Object *o)
{
evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, 0.0);
evas_object_size_hint_align_set(o, EVAS_HINT_FILL, 0.0);
elm_object_focus_allow_set(o, EINA_FALSE);
}
static void
_zone_gui_icon_set(Evas_Object *o, const char *part, const char *iconname)
{
Evas_Object *ic = elm_icon_add(o);
if (!elm_icon_standard_set(ic, iconname))
{
evas_object_del(ic);
return;
}
elm_object_part_content_set(o, part, ic);
}
static Evas_Object *
_zone_gui_check_add(Zone *zone, Eina_Bool state, const char *label, Evas_Smart_Cb cb)
{
Evas_Object *ck = elm_check_add(zone->gui.main_box);
_zone_gui_widget_setup(ck);
elm_object_text_set(ck, label);
elm_check_state_set(ck, state);
evas_object_smart_callback_add(ck, "changed", cb, zone);
evas_object_show(ck);
elm_box_pack_end(zone->gui.main_box, ck);
return ck;
}
static Evas_Object *
_zone_gui_button_add(Zone *zone, const char *icon, const char *label, Evas_Smart_Cb cb)
{
Evas_Object *bt = elm_button_add(zone->gui.main_box);
_zone_gui_widget_setup(bt);
_zone_gui_icon_set(bt, NULL, icon);
elm_object_text_set(bt, label);
evas_object_smart_callback_add(bt, "clicked", cb, zone);
evas_object_show(bt);
elm_box_pack_end(zone->gui.main_box, bt);
return bt;
}
static Eina_Bool
_zone_gui_frame_moving(void *data)
{
Zone *zone = data;
int x, y, w, h;
evas_pointer_canvas_xy_get(zone->evas, &x, &y);
evas_object_size_hint_min_get(zone->gui.frame, &w, &h);
x -= zone->gui.moving.dx;
y -= zone->gui.moving.dy;
if (x + w > zone->w - 10)
x = zone->w - 10 - w;
if (y + h > zone->h - 10)
y = zone->h - 10 - h;
if (x < 10)
x = 10;
if (y < 10)
y = 10;
evas_object_move(zone->gui.frame, x, y);
evas_object_resize(zone->gui.frame, w, h);
return EINA_TRUE;
}
static void
_zone_gui_frame_mouse_down(void *data, Evas *e EINA_UNUSED, Evas_Object *o, void *event)
{
Zone *zone = data;
Evas_Event_Mouse_Down *ev = event;
int x, y;
if ((zone->gui.moving.anim) || (ev->button != 1))
return;
if (!elm_frame_collapse_get(o))
{
int bx, by, bw, bh;
evas_object_geometry_get(zone->gui.main_box, &bx, &by, &bw, &bh);
if (((ev->canvas.x >= bx) && (ev->canvas.x < bx + bw)) &&
((ev->canvas.y >= by) && (ev->canvas.y < by + bh)))
return;
}
evas_object_geometry_get(o, &x, &y, NULL, NULL);
zone->gui.moving.x = ev->canvas.x;
zone->gui.moving.y = ev->canvas.y;
zone->gui.moving.dx = ev->canvas.x - x;
zone->gui.moving.dy = ev->canvas.y - y;
zone->gui.moving.anim = ecore_animator_add(_zone_gui_frame_moving, zone);
}
static void
_zone_gui_frame_mouse_up(void *data, Evas *e EINA_UNUSED, Evas_Object *o, void *event)
{
Zone *zone = data;
Evas_Event_Mouse_Up *ev = event;
int fingersize, dx, dy;
if ((!zone->gui.moving.anim) || (ev->button != 1))
return;
fingersize = elm_config_finger_size_get();
dx = zone->gui.moving.x - ev->canvas.x;
dy = zone->gui.moving.y - ev->canvas.y;
if (dx < 0)
dx = -dx;
if (dy < 0)
dy = -dy;
if ((dx < fingersize) && (dy < fingersize))
{
evas_object_move(o, zone->gui.moving.x - zone->gui.moving.dx,
zone->gui.moving.y - zone->gui.moving.dy);
elm_frame_collapse_set(o, !elm_frame_collapse_get(o));
}
zone->gui.moving.x = 0;
zone->gui.moving.y = 0;
zone->gui.moving.dx = 0;
zone->gui.moving.dy = 0;
ecore_animator_del(zone->gui.moving.anim);
zone->gui.moving.anim = NULL;
}
static void
zone_gui_setup(Zone *zone)
{
Evas_Object *o;
unsigned int i;
zone->gui.frame = elm_frame_add(zone->win);
elm_object_focus_allow_set(zone->gui.frame, EINA_FALSE);
evas_object_event_callback_add(zone->gui.frame,
EVAS_CALLBACK_CHANGED_SIZE_HINTS,
_zone_gui_resize_cb, zone);
if (eina_list_count(zones) == 1)
elm_object_text_set(zone->gui.frame, "ERuler");
else
{
char buf[128];
snprintf(buf, sizeof(buf), "ERuler (Zone #%d)", zone->idx);
elm_object_text_set(zone->gui.frame, buf);
}
evas_object_event_callback_add(zone->gui.frame, EVAS_CALLBACK_MOUSE_DOWN,
_zone_gui_frame_mouse_down, zone);
evas_object_event_callback_add(zone->gui.frame, EVAS_CALLBACK_MOUSE_UP,
_zone_gui_frame_mouse_up, zone);
zone->gui.main_box = elm_box_add(zone->gui.frame);
elm_box_horizontal_set(zone->gui.main_box, EINA_FALSE);
zone->gui.create = _zone_gui_button_add
(zone, "add", "Create ruler", _zone_gui_create);
elm_object_disabled_set(zone->gui.create, EINA_TRUE);
zone->gui.clear = _zone_gui_button_add
(zone, "editclear", "Clear rulers", _zone_gui_clear);
elm_object_disabled_set(zone->gui.clear, EINA_TRUE);
_zone_gui_button_add(zone, "keyboard", "Type coordinates", _zone_gui_type);
zone->gui.style = o = elm_hoversel_add(zone->gui.main_box);
elm_hoversel_hover_parent_set(o, zone->win);
_zone_gui_widget_setup(o);
for (i = 0; ruler_type_names_strs[i] != NULL; i++)
{
const char *label = ruler_type_names_strs[i];
elm_hoversel_item_add(o, label, NULL, ELM_ICON_NONE, NULL, NULL);
}
evas_object_smart_callback_add(o, "selected", _zone_gui_style_changed, zone);
_zone_gui_style_label_update(zone);
evas_object_show(o);
elm_box_pack_end(zone->gui.main_box, o);
zone->gui.zoom = _zone_gui_check_add
(zone, !!zone->zoom.image, "Zoom", _zone_gui_zoom_changed);
zone->gui.hex_colors = _zone_gui_check_add
(zone, hex_colors, "Show hexadecimal colors",
_zone_gui_show_hex_colors_changed);
zone->gui.show_guides = _zone_gui_check_add
(zone, show_guides, "Show guidelines", _zone_gui_show_guides_changed);
zone->gui.show_distances = _zone_gui_check_add
(zone, show_distances, "Show distances", _zone_gui_show_distances_changed);
_zone_gui_button_add(zone, "filesaveas", "Save screenshot", _zone_gui_shot);
_zone_gui_button_add(zone, "help", "Help", _zone_gui_help);
_zone_gui_button_add(zone, "exit", "Exit", _zone_gui_exit);
elm_object_content_set(zone->gui.frame, zone->gui.main_box);
evas_object_show(zone->gui.main_box);
evas_object_move(zone->gui.frame, 10, 10);
evas_object_show(zone->gui.frame);
}
static void
zone_zoom_pre_setup(Zone *zone)
{
const char *str;
if (zone->zoom.frame) return;
zone->zoom.ideal_size.w = 256;
zone->zoom.ideal_size.h = 256;
zone->zoom.factor = 10;
zone->zoom.frame = edje_object_add(zone->evas);
theme_apply(zone->zoom.frame, "eruler/zoom_viewfinder");
zone->zoom.image = evas_object_image_add(zone->evas);
evas_object_image_smooth_scale_set(zone->zoom.image, EINA_FALSE);
edje_object_part_swallow(zone->zoom.frame, "content", zone->zoom.image);
str = edje_object_data_get(zone->zoom.frame, "ideal_size");
DBG("Zoom viewfinder ideal size: %s", str);
if (str)
{
int n = sscanf(str, "%d %d",
&zone->zoom.ideal_size.w,
&zone->zoom.ideal_size.h);
if (n == 1)
zone->zoom.ideal_size.h = zone->zoom.ideal_size.w;
if (zone->zoom.ideal_size.w < 1)
zone->zoom.ideal_size.w = 256;
if (zone->zoom.ideal_size.h < 1)
zone->zoom.ideal_size.h = 256;
}
str = edje_object_data_get(zone->zoom.frame, "factor");
DBG("Zoom factor: %s", str);
if (str)
{
zone->zoom.factor = atoi(str);
if (zone->zoom.factor < 1)
zone->zoom.factor = 10;
}
zone->zoom.ready = EINA_FALSE;
}
static void
_error_popup_hide_cb(void *data, Evas_Object *o EINA_UNUSED, void *event EINA_UNUSED)
{
Zone *zone = data;
evas_object_del(zone->error_popup);
zone->error_popup = NULL;
}
static void
show_error_popup(Zone *zone, const char *str)
{
Evas_Object *popup, *msg;
if (zone->error_popup) return;
popup = elm_popup_add(zone->win);
elm_popup_content_text_wrap_type_set(popup, ELM_WRAP_MIXED);
elm_object_part_text_set(popup, "title,text", "ERuler");
msg = elm_entry_add(popup);
elm_entry_editable_set(msg, EINA_FALSE);
#if (ELM_VERSION_MAJOR >= 1) && (ELM_VERSION_MINOR >= 8)
elm_scroller_policy_set(msg, ELM_SCROLLER_POLICY_OFF,
ELM_SCROLLER_POLICY_AUTO);
#else
elm_entry_scrollbar_policy_set(msg, ELM_SCROLLER_POLICY_OFF,
ELM_SCROLLER_POLICY_AUTO);
#endif
elm_object_text_set(msg, str);
elm_object_content_set(popup, msg);
elm_popup_timeout_set(popup, 3);
evas_object_smart_callback_add(popup, "timeout", _error_popup_hide_cb, zone);
evas_object_show(popup);
zone->error_popup = popup;
}
static void
_popup_dismiss_cb(void *data, Evas_Object *o EINA_UNUSED, void *event EINA_UNUSED)
{
Zone *zone = data;
evas_object_del(zone->help_popup);
zone->help_popup = NULL;
}
static void
show_gui_help(Zone *zone)
{
Evas_Object *popup, *help, *bt;
popup = elm_popup_add(zone->win);
elm_popup_content_text_wrap_type_set(popup, ELM_WRAP_MIXED);
elm_object_part_text_set(popup, "title,text", "ERuler");
help = elm_entry_add(popup);
elm_entry_editable_set(help, EINA_FALSE);
#if (ELM_VERSION_MAJOR >= 1) && (ELM_VERSION_MINOR >= 8)
elm_scroller_policy_set(help, ELM_SCROLLER_POLICY_OFF,
ELM_SCROLLER_POLICY_AUTO);
#else
elm_entry_scrollbar_policy_set(help, ELM_SCROLLER_POLICY_OFF,
ELM_SCROLLER_POLICY_AUTO);
#endif
elm_object_text_set
(help,
"Press your mouse's left-button to start measuring, "
"release to stop.<br><br>"
"Keyboard shortcuts:<br>"
" • <b>Escape:</b> quit;<br>"
" • <b>F2:</b> toggle ERuler visibility;<br>"
" • <b>p:</b> print size to stdout;<br>"
" • <b>c:</b> create new ruler;<br>"
" • <b>Control-c:</b> clear current zone rulers;<br>"
" • <b>g:</b> toggle display of guide lines;<br>"
" • <b>d:</b> toggle display of distances between boxes;<br>"
" • <b>t:</b> toggle ruler type (dark, light, filled...);<br>"
" • <b>z:</b> toggle zoom;<br>"
" • <b>x:</b> toggle display of colors in hexadecimal;<br>"
" • <b>@:</b> open command box to type ruler placement;<br>"
" • <b>Space:</b> start or stop measure using keyboard;<br>"
" • <b>Left:</b> move ruler start point to the left (<b>Shift</b> to use 10px step);<br>"
" • <b>Control-Left:</b> move ruler end point to the left (<b>Shift</b> to use 10px step);<br>"
" • <b>Right:</b> move ruler start point to the right (<b>Shift</b> to use 10px step);<br>"
" • <b>Control-Right:</b> move ruler end point to the right (<b>Shift</b> to use 10px step);<br>"
" • <b>Up:</b> move ruler start point up (<b>Shift</b> to use 10px step);<br>"
" • <b>Control-Up:</b> move ruler end point up (<b>Shift</b> to use 10px step);<br>"
" • <b>Down:</b> move ruler start point down (<b>Shift</b> to use 10px step);<br>"
" • <b>Control-Down:</b> move ruler end point down (<b>Shift</b> to use 10px step);<br>"
"");
elm_object_content_set(popup, help);
bt = elm_button_add(popup);
elm_object_text_set(bt, "Close");
evas_object_smart_callback_add(bt, "clicked", _popup_dismiss_cb, zone);
elm_object_part_content_set(popup, "button1", bt);
evas_object_show(popup);
zone->help_popup = popup;
elm_object_focus_set(bt, EINA_TRUE);
}
static void
zone_print_measurements(const Zone *zone)
{
const Evas_Object *ruler, *last_ruler;
const Eina_List *l;
unsigned int i;
printf("Zone %d,%d size %dx%d\n", zone->x, zone->y, zone->w, zone->h);
if ((!zone->rulers) ||
((eina_list_count(zone->rulers) == 1) && (!zone->last_ruler_used)))
{
puts("\tnothing measured.");
return;
}
last_ruler = zone_ruler_last_get(zone);
i = 0;
EINA_LIST_FOREACH(zone->rulers, l, ruler)
{
const Ruler_Data *rd = ruler_data_get(ruler);
if ((ruler == last_ruler) && (!zone->last_ruler_used))
break;
i++;
printf("\tmeasure #%d: %+dx%+d from %d,%d to %d,%d\n",
i,
rd->stop.x - rd->start.x + 1,
rd->stop.y - rd->start.y + 1,
zone->x + rd->start.x,
zone->y + rd->start.y,
zone->x + rd->stop.x,
zone->y + rd->stop.y);
}
}
static void
print_measurements(void)
{
const Eina_List *l;
const Zone *zone;
EINA_LIST_FOREACH(zones, l, zone)
zone_print_measurements(zone);
}
static void
_create_ruler_cmdbox_activated(void *data, Evas_Object *o, void *event_info EINA_UNUSED)
{
Zone *zone = data;
Evas_Object *ruler;
int x, y, w, h;
const char *str;
str = elm_entry_entry_get(o);
EINA_SAFETY_ON_NULL_GOTO(str, end);
if (sscanf(str, "%d %d %d %d", &x, &y, &w, &h) != 4)
{
show_error_popup(zone,
"Error: Invalid creation format. Expected 'x y w h'");
ERR("Invalid creation format. Expected 'x y w h', got '%s'", str);
goto end;
}
if (w < 1 || h < 1)
{
show_error_popup(zone,
"Error: Invalid size. w and h must be positive.");
ERR("Invalid size: %dx%d", w, h);
goto end;
}
ruler = zone_ruler_last_get(zone);
EINA_SAFETY_ON_NULL_GOTO(ruler, end);
zone_last_ruler_used_set(zone, EINA_TRUE);
ruler_place(ruler, x, y, w, h);
end:
evas_object_del(zone->cmdbox);
}
static void
_create_ruler_cmdbox_aborted(void *data, Evas_Object *o EINA_UNUSED, void *event_info EINA_UNUSED)
{
Zone *zone = data;
evas_object_del(zone->cmdbox);
}
static void
_create_ruler_cmdbox_size_changed(void *data, Evas *e EINA_UNUSED, Evas_Object *frame, void *event_info EINA_UNUSED)
{
Zone *zone = data;
int mh;
evas_object_size_hint_min_get(frame, NULL, &mh);
evas_object_move(frame, 0, 0);
evas_object_resize(frame, zone->w, mh);
}
static void
_create_ruler_cmdbox_del(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *event_info EINA_UNUSED)
{
Zone *zone = data;
zone->cmdbox = NULL;
}
static void
create_ruler_from_cmdbox(Zone *zone)
{
Evas_Object *frame, *entry;
if (zone->cmdbox) return;
frame = elm_frame_add(zone->win);
/* frame has delayed min size calc, listen for size hint changed */
evas_object_event_callback_add(frame, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
_create_ruler_cmdbox_size_changed, zone);
evas_object_event_callback_add(frame, EVAS_CALLBACK_DEL,
_create_ruler_cmdbox_del, zone);
if (!zone->last_ruler_used)
elm_object_text_set(frame, "Create ruler (x y w h):");
else
elm_object_text_set(frame, "Edit ruler (x y w h):");
entry = elm_entry_add(frame);
elm_entry_single_line_set(entry, EINA_TRUE);
elm_entry_editable_set(entry, EINA_TRUE);
elm_entry_scrollable_set(entry, EINA_TRUE);
evas_object_show(entry);
evas_object_smart_callback_add(entry, "activated",
_create_ruler_cmdbox_activated, zone);
evas_object_smart_callback_add(entry, "aborted",
_create_ruler_cmdbox_aborted, zone);
elm_object_focus_set(entry, EINA_TRUE);
elm_object_content_set(frame, entry);
evas_object_show(frame);
zone->cmdbox = frame;
}
static void
_create_screenshot_select_done_cb(void *data, Evas_Object *o EINA_UNUSED, void *event)
{
Zone *zone = data;
const char *str = event;
if (str)
elm_entry_entry_set(zone->screenshot.file.entry, str);
evas_object_del(zone->screenshot.file.popup);
zone->screenshot.file.popup = NULL;
zone->screenshot.file.selector = NULL;
elm_object_focus_set(zone->screenshot.popup, EINA_TRUE);
}
static void
_create_screenshot_select_cb(void *data, Evas_Object *btn EINA_UNUSED, void *event EINA_UNUSED)
{
Zone *zone = data;
Evas_Object *popup, *bx, *sizer, *sel;
const char *fname = elm_entry_entry_get(zone->screenshot.file.entry);
zone->screenshot.file.popup = popup = elm_popup_add(zone->win);
elm_object_part_text_set(popup, "title,text", "Select file to save");
bx = elm_box_add(popup);
elm_box_layout_set(bx, evas_object_box_layout_stack, NULL, NULL);
/* stack + sizer to make popup bigger */
sizer = evas_object_rectangle_add(zone->evas);
#if (ELM_VERSION_MAJOR >= 1) && (ELM_VERSION_MINOR >= 8)
evas_object_size_hint_min_set(sizer, zone->w * 0.5, zone->h * 0.5);
#else
/* 1.7 popup theme is badly written and won't adapt base to contents,
* instead the contents is "limited" to 400px in width. So use this :-(
*/
evas_object_size_hint_min_set(sizer, 400, (zone->h * 400.0) / zone->w);
#endif
evas_object_color_set(sizer, 0, 0, 0, 0);
evas_object_show(sizer);
elm_box_pack_end(bx, sizer);
zone->screenshot.file.selector = sel = elm_fileselector_add(popup);
evas_object_size_hint_weight_set(sel, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(sel, EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_fileselector_is_save_set(sel, EINA_TRUE);
evas_object_show(sel);
if (fname)
{
if (ecore_file_is_dir(fname))
elm_fileselector_path_set(sel, fname);
else
{
char *dname = ecore_file_dir_get(fname);
elm_fileselector_path_set(sel, dname);
free(dname);
}
}
evas_object_smart_callback_add(sel, "done",
_create_screenshot_select_done_cb, zone);
elm_box_pack_end(bx, sel);
elm_object_content_set(popup, bx);
evas_object_show(popup);
}
static void
_create_screenshot_cleanup(Zone *zone)
{
ecore_evas_free(zone->screenshot.ee);
zone->screenshot.ee = NULL;
zone->screenshot.image = NULL;
evas_object_del(zone->screenshot.popup);
zone->screenshot.preview = NULL;
zone->screenshot.popup = NULL;
zone->screenshot.file.entry = NULL;
zone->screenshot.file.button = NULL;
zone->screenshot.file.popup = NULL;
zone->screenshot.file.selector = NULL;
evas_object_focus_set(zone->win, EINA_TRUE);
}
static void
_create_screenshot_notify_dismiss_cb(void *data, Evas_Object *o EINA_UNUSED, void *event EINA_UNUSED)
{
evas_object_del(data);
}
static void
_create_screenshot_notify_show(Zone *zone, double timeout, const char *msg)
{
Evas_Object *notify, *bx, *o;
notify = elm_notify_add(zone->win);
elm_notify_allow_events_set(notify, EINA_FALSE);
#if (ELM_VERSION_MAJOR >= 1) && (ELM_VERSION_MINOR >= 8)
elm_notify_align_set(notify, 0.5, 0.5);
#else
elm_notify_orient_set(notify, ELM_NOTIFY_ORIENT_CENTER);
#endif
if (timeout > 0.0)
elm_notify_timeout_set(notify, timeout);
bx = elm_box_add(notify);
elm_box_horizontal_set(bx, EINA_FALSE);
o = elm_label_add(bx);
elm_object_text_set(o, msg);
evas_object_show(o);
elm_box_pack_end(bx, o);
o = elm_button_add(bx);
elm_object_text_set(o, "Close");
evas_object_smart_callback_add(o, "clicked",
_create_screenshot_notify_dismiss_cb, notify);
evas_object_show(o);
elm_box_pack_end(bx, o);
evas_object_show(bx);
elm_object_content_set(notify, bx);
evas_object_smart_callback_add(notify, "timeout",
_create_screenshot_notify_dismiss_cb, notify);
evas_object_show(notify);
}
static Eina_Bool
_create_screenshot_save_timer_cb(void *data)
{
Zone *zone = data;
const char *fname, *ext, *opts = NULL;
char msg[1024];
char *dname;
fname = elm_object_text_get(zone->screenshot.file.entry);
EINA_SAFETY_ON_NULL_GOTO(fname, error);
dname = ecore_file_dir_get(fname);
if ((dname) && (!ecore_file_is_dir(dname)))
{
if (ecore_file_mkpath(dname))
{
snprintf(msg, sizeof(msg),
"Could not create save directory: \"%s\"", dname);
_create_screenshot_notify_show(zone, -1, msg);
free(dname);
goto error;
}
}
free(dname);
ext = strrchr(fname, '.');
if (ext)
{
ext++;
if (strcasecmp(ext, "png") == 0)
opts = "compress=9";
else if ((strcasecmp(ext, "jpg") == 0) ||
(strcasecmp(ext, "jpeg") == 0))
opts = "quality=95";
}
if (!evas_object_image_save(zone->screenshot.preview, fname, NULL, opts))
{
snprintf(msg, sizeof(msg), "Could not save image: \"%s\"", fname);
_create_screenshot_notify_show(zone, -1, msg);
}
else
{
snprintf(msg, sizeof(msg), "Saved as: \"%s\"", fname);
_create_screenshot_notify_show(zone, 5.0, msg);
}
error:
_create_screenshot_cleanup(zone);
zone->screenshot.timer = NULL;
return EINA_FALSE;
}
static void
_create_screenshot_save_cb(void *data, Evas_Object *o EINA_UNUSED, void *event EINA_UNUSED)
{
Zone *zone = data;
elm_object_text_set(zone->screenshot.save_bt, "Wait...");
elm_object_disabled_set(zone->screenshot.save_bt, EINA_TRUE);
elm_object_disabled_set(zone->screenshot.cancel_bt, EINA_TRUE);
elm_object_disabled_set(zone->screenshot.file.entry, EINA_TRUE);
elm_object_disabled_set(zone->screenshot.file.button, EINA_TRUE);
zone->screenshot.timer = ecore_timer_add
(0.01, _create_screenshot_save_timer_cb, zone);
}
static void
_create_screenshot_cancel_cb(void *data, Evas_Object *o EINA_UNUSED, void *event EINA_UNUSED)
{
Zone *zone = data;
_create_screenshot_cleanup(zone);
}
static void
find_path_next(char *buf, size_t buflen, const char *dname, const char *fmtname)
{
char name[NAME_MAX], *bufp;
size_t bufplen;
unsigned int i;
bufp = buf + eina_strlcpy(buf, dname, buflen);
bufplen = buflen - (bufp - buf);
if ((bufplen > 0) && (bufp[-1] != '/'))
{
bufp[0] = '/';
bufp++;
bufplen--;
}
for (i = 1; i < (unsigned)-1; i++)
{
snprintf(name, sizeof(name), fmtname, i);
eina_strlcpy(bufp, name, bufplen);
if (!ecore_file_exists(buf))
return;
}
}
static void
_create_screenshot_copy_cb(void *data, Eina_Bool success)
{
Zone *zone = data;
const Eina_List *l;
const Evas_Object *orig_ruler, *last_ruler;
Evas_Object *popup, *bx, *img, *o, *hbx;
Edje_Message_Int_Set *msg;
Evas *e;
char path[PATH_MAX], buf[128], *bufp;
int iw, ih;
size_t bufplen;
const void *pixels;
if (!success)
{
ecore_evas_free(zone->screenshot.ee);
zone->screenshot.ee = NULL;
zone->screenshot.image = NULL;
return;
}
evas_object_resize(zone->screenshot.image, zone->w, zone->h);
evas_object_image_fill_set(zone->screenshot.image, 0, 0, zone->w, zone->h);
evas_object_show(zone->screenshot.image);
msg = alloca(sizeof(Edje_Message_Int_Set) + sizeof(int));
msg->count = 2;
bufp = buf + eina_strlcpy(buf, RULER_TYPE_PREFIX, sizeof(buf));
bufplen = sizeof(buf) - (bufp - buf);
e = ecore_evas_get(zone->screenshot.ee);
last_ruler = zone_ruler_last_get(zone);
EINA_LIST_FOREACH(zone->rulers, l, orig_ruler)
{
const Ruler_Data *orig_rd;
Evas_Object *ro;
int x, y, w, h, dx, dy;
if ((!zone->last_ruler_used) && (orig_ruler == last_ruler)) continue;
orig_rd = ruler_data_get(orig_ruler);
ro = edje_object_add(e);
x = orig_rd->start.x;
y = orig_rd->start.y;
dx = w = orig_rd->stop.x - orig_rd->start.x;
dy = h = orig_rd->stop.y - orig_rd->start.y;
if (w < 0)
{
w = -w;
x -= w;
}
if (h < 0)
{
h = -h;
y -= h;
}
w++;
h++;
dx = dx < 0 ? -w : w;
dy = dy < 0 ? -h : h;
eina_strlcpy(bufp, ruler_type_names_strs[orig_rd->type], bufplen);
theme_apply(ro, buf);
evas_object_move(ro, x, y);
evas_object_resize(ro, w, h);
evas_object_show(ro);
msg->val[0] = orig_rd->start.x;
msg->val[1] = orig_rd->start.y;
edje_object_message_send(ro, EDJE_MESSAGE_INT_SET, 0, msg);
msg->val[0] = dx;
msg->val[1] = dy;
edje_object_message_send(ro, EDJE_MESSAGE_INT_SET, 1, msg);
}
/* dialog to ask for where to save, with preview */
find_path_next(path, sizeof(path), getenv("HOME"), "eruler-%u.png");
zone->screenshot.popup = popup = elm_popup_add(zone->win);
elm_object_part_text_set(popup, "title,text", "Save screenshot");
bx = elm_box_add(popup);
elm_box_padding_set(bx, 0, 10);
elm_box_horizontal_set(bx, EINA_FALSE);
zone->screenshot.preview = img = evas_object_image_filled_add(zone->evas);
evas_object_size_hint_weight_set(img, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(img, EVAS_HINT_FILL, EVAS_HINT_FILL);
#if (ELM_VERSION_MAJOR >= 1) && (ELM_VERSION_MINOR >= 8)
evas_object_size_hint_min_set(img, zone->w * 0.5, zone->h * 0.5);
#else
/* 1.7 popup theme is badly written and won't adapt base to contents,
* instead the contents is "limited" to 400px in width. So use this :-(
*/
evas_object_size_hint_min_set(img, 400, (zone->h * 400.0) / zone->w);
#endif
ecore_evas_geometry_get(zone->screenshot.ee, NULL, NULL, &iw, &ih);
pixels = ecore_evas_buffer_pixels_get(zone->screenshot.ee);
evas_object_image_size_set(img, iw, ih);
evas_object_image_colorspace_set(img, EVAS_COLORSPACE_ARGB8888);
evas_object_image_alpha_set(img, EINA_FALSE);
evas_object_image_data_set(img, (void *)pixels);
evas_object_show(img);
elm_box_pack_end(bx, img);
hbx = elm_box_add(bx);
evas_object_size_hint_weight_set(hbx, EVAS_HINT_EXPAND, 0.0);
evas_object_size_hint_align_set(hbx, EVAS_HINT_FILL, 0.5);
elm_box_padding_set(hbx, 0, 0);
elm_box_horizontal_set(hbx, EINA_TRUE);
o = elm_label_add(hbx);
elm_object_text_set(o, "Save as:");
evas_object_show(o);
elm_box_pack_end(hbx, o);
zone->screenshot.file.entry = o = elm_entry_add(hbx);
evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, 0.0);
evas_object_size_hint_align_set(o, EVAS_HINT_FILL, 0.5);
elm_entry_single_line_set(o, EINA_TRUE);
elm_entry_scrollable_set(o, EINA_TRUE);
elm_entry_entry_set(o, path);
evas_object_show(o);
elm_box_pack_end(hbx, o);
zone->screenshot.file.button = o = elm_button_add(hbx);
elm_object_text_set(o, "Select");
evas_object_smart_callback_add(o, "clicked",
_create_screenshot_select_cb, zone);
evas_object_show(o);
elm_box_pack_end(hbx, o);
evas_object_show(hbx);
elm_box_pack_end(bx, hbx);
hbx = elm_box_add(bx);
elm_box_padding_set(hbx, 10, 0);
elm_box_horizontal_set(hbx, EINA_TRUE);
zone->screenshot.save_bt = o = elm_button_add(hbx);
elm_object_text_set(o, "Save");
evas_object_smart_callback_add(o, "clicked",
_create_screenshot_save_cb, zone);
evas_object_show(o);
elm_box_pack_end(hbx, o);
zone->screenshot.cancel_bt = o = elm_button_add(hbx);
elm_object_text_set(o, "Cancel");
evas_object_smart_callback_add(o, "clicked",
_create_screenshot_cancel_cb, zone);
evas_object_show(o);
elm_box_pack_end(hbx, o);
evas_object_show(hbx);
elm_box_pack_end(bx, hbx);
elm_object_content_set(popup, bx);
evas_object_show(popup);
}
static void
create_screenshot(Zone *zone)
{
Evas *e;
if (zone->screenshot.ee) return;
zone->screenshot.ee = ecore_evas_buffer_new(zone->w, zone->h);
ecore_evas_alpha_set(zone->screenshot.ee, EINA_FALSE);
e = ecore_evas_get(zone->screenshot.ee);
zone->screenshot.image = evas_object_image_add(e);
platform_funcs->zone_screen_copy(zone, zone->screenshot.image,
_create_screenshot_copy_cb, zone);
}
static void
_zone_screen_copy_cb(void *data, Eina_Bool success)
{
Zone *zone = data;
if (!success)
{
evas_object_del(zone->zoom.image);
zone->zoom.image = NULL;
evas_object_del(zone->zoom.frame);
zone->zoom.frame = NULL;
zone->zoom.ready = EINA_FALSE;
elm_check_state_set(zone->gui.zoom, EINA_FALSE);
return;
}
zone->zoom.ready = EINA_TRUE;
zone->last_mouse.x = -1;
zone->last_mouse.y = -1;
_event_mouse_tracker(zone);
evas_object_show(zone->zoom.frame);
elm_check_state_set(zone->gui.zoom, EINA_TRUE);
}
static void
_toggle_visibility_cb(void *data EINA_UNUSED, const char *keyname EINA_UNUSED)
{
visible = !visible;
platform_funcs->windows_visibility_set(visible);
}
static Eina_Bool
_del_help_popup(void *data)
{
Zone *zone = data;
evas_object_del(zone->help_popup);
zone->help_popup = NULL;
zone->del_help_popup_idler = NULL;
return ECORE_CALLBACK_CANCEL;
}
static void
_zone_win_key_down_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *event_info)
{
Zone *zone = data;
const Evas_Event_Key_Down *ev = event_info;
const char *keyname = ev->keyname;
Eina_Bool control = evas_key_modifier_is_set(ev->modifiers, "Control");
Eina_Bool shift = evas_key_modifier_is_set(ev->modifiers, "Shift");
if (zone->screenshot.ee) return;
if (zone->del_help_popup_idler) return;
if (zone->help_popup)
{
zone->del_help_popup_idler = ecore_idler_add(_del_help_popup, zone);
return;
}
if (strcmp(keyname, "Escape") == 0)
{
DBG("User requested exit!");
elm_exit();
}
else if ((strcmp(keyname, "F1") == 0) ||
(strcmp(keyname, "h") == 0))
show_gui_help(zone);
else if (strcmp(keyname, "p") == 0)
print_measurements();
else if (strcmp(keyname, "c") == 0)
{
if (control)
{
zone_rulers_clear(zone);
zone_ruler_create(zone);
}
else
{
if (zone->last_ruler_used)
zone_ruler_create(zone);
else
DBG("Last ruler wasn't used, do not create a new one.");
}
}
else if (strcmp(keyname, "g") == 0)
{
show_guides = !show_guides;
show_guides_apply();
}
else if (strcmp(keyname, "d") == 0)
{
show_distances = !show_distances;
show_distances_apply();
}
else if (strcmp(keyname, "t") == 0)
{
Evas_Object *ruler = zone_ruler_last_get(zone);
Ruler_Data *rd = ruler_data_get(ruler);
rd->type = (rd->type + 1) % N_RULER_TYPES;
ruler_type_apply(ruler);
}
else if (strcmp(keyname, "z") == 0)
{
Eina_Bool state;
if (zone->zoom.image)
{
evas_object_del(zone->zoom.image);
zone->zoom.image = NULL;
evas_object_del(zone->zoom.frame);
zone->zoom.frame = NULL;
zone->zoom.ready = EINA_FALSE;
state = EINA_FALSE;
}
else
{
zone_zoom_pre_setup(zone);
platform_funcs->zone_screen_copy(zone, zone->zoom.image,
_zone_screen_copy_cb,
zone);
state = EINA_TRUE;
}
elm_check_state_set(zone->gui.zoom, state);
}
else if (strcmp(keyname, "x") == 0)
{
hex_colors = !hex_colors;
show_hexa_apply();
}
else if (strcmp(keyname, "space") == 0)
{
if (!zone->handling)
{
zone->keyboard_move = EINA_TRUE;
_handling_start(zone);
}
else
{
zone->keyboard_move = EINA_FALSE;
_handling_stop(zone);
}
}
else if (strcmp(keyname, "Left") == 0)
{
int d = shift ? 10 : 1;
if ((zone->last_ruler_used) && (!zone->keyboard_move))
{
Evas_Object *ruler = zone_ruler_last_get(zone);
if (!control)
ruler_move_relative(ruler, -d, 0);
else
ruler_resize_relative(ruler, -d, 0);
}
else
platform_funcs->mouse_move_by(zone, -d, 0);
}
else if (strcmp(keyname, "Right") == 0)
{
int d = shift ? 10 : 1;
if ((zone->last_ruler_used) && (!zone->keyboard_move))
{
Evas_Object *ruler = zone_ruler_last_get(zone);
if (!control)
ruler_move_relative(ruler, d, 0);
else
ruler_resize_relative(ruler, d, 0);
}
else
platform_funcs->mouse_move_by(zone, d, 0);
}
else if (strcmp(keyname, "Up") == 0)
{
int d = shift ? 10 : 1;
if ((zone->last_ruler_used) && (!zone->keyboard_move))
{
Evas_Object *ruler = zone_ruler_last_get(zone);
if (!control)
ruler_move_relative(ruler, 0, -d);
else
ruler_resize_relative(ruler, 0, -d);
}
else
platform_funcs->mouse_move_by(zone, 0, -d);
}
else if (strcmp(keyname, "Down") == 0)
{
int d = shift ? 10 : 1;
if ((zone->last_ruler_used) && (!zone->keyboard_move))
{
Evas_Object *ruler = zone_ruler_last_get(zone);
if (!control)
ruler_move_relative(ruler, 0, d);
else
ruler_resize_relative(ruler, 0, d);
}
else
platform_funcs->mouse_move_by(zone, 0, d);
}
else if ((ev->string) && (strcmp(ev->string, "@") == 0))
create_ruler_from_cmdbox(zone);
else if (strcmp(keyname, "s") == 0)
create_screenshot(zone);
else
DBG("Unhandled key %s, string: %s", keyname, ev->string);
}
typedef struct _Ruler_Spec Ruler_Spec;
struct _Ruler_Spec
{
int x, y, w, h;
enum ruler_type type;
};
static Eina_Bool
_parse_cmdline_ruler(const Ecore_Getopt *parser EINA_UNUSED, const Ecore_Getopt_Desc *desc EINA_UNUSED, const char *str, void *data EINA_UNUSED, Ecore_Getopt_Value *storage)
{
int x, y, w, h, n;
char *style = NULL;
Eina_List **p_rulers_specs = (Eina_List **)storage->ptrp;
Ruler_Spec *rs;
n = sscanf(str, "%d:%d:%d:%d:%ms", &x, &y, &w, &h, &style);
if (n < 4)
{
ERR("Invalid ruler format '%s', expected 'x:y:w:h[:style]'", str);
return EINA_FALSE;
}
rs = malloc(sizeof(Ruler_Spec));
EINA_SAFETY_ON_NULL_RETURN_VAL(rs, EINA_FALSE);
rs->x = x;
rs->y = y;
rs->w = w;
rs->h = h;
rs->type = RULER_TYPE_SENTINEL;
if (style)
{
int i;
for (i = 0; ruler_type_names_strs[i] != NULL; i++)
{
if (strcasecmp(ruler_type_names_strs[i], style) == 0)
rs->type = i;
}
free(style);
}
DBG("command line ruler: %dx%d at %d,%d type=%s",
rs->w, rs->h, rs->x, rs->y, ruler_type_names_strs[rs->type]);
*p_rulers_specs = eina_list_append(*p_rulers_specs, rs);
return EINA_TRUE;
}
static void
_zone_win_resize_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *event EINA_UNUSED)
{
Zone *zone = data;
evas_object_geometry_get(zone->win, &zone->x, &zone->y, &zone->w, &zone->h);
zone->last_mouse.x = -1;
zone->last_mouse.y = -1;
_event_mouse_tracker(zone);
}
static void
zone_del(Zone *zone)
{
if (zone->tracker)
ecore_animator_del(zone->tracker);
if (zone->del_help_popup_idler)
ecore_idler_del(zone->del_help_popup_idler);
/* all objects are deleted when canvas goes away.
* when ruler and distance objects are deleted, they remove themselves from
* their lists.
*/
evas_object_event_callback_del_full(zone->event, EVAS_CALLBACK_DEL, _event_del, zone);
free(zone);
zones = eina_list_remove(zones, zone);
}
static void
_zone_win_del_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *event EINA_UNUSED)
{
Zone *zone = data;
zone_del(zone);
}
static Eina_Bool
zone_create(int idx, int x, int y, int w, int h)
{
Zone *zone = calloc(1, sizeof(Zone));
EINA_SAFETY_ON_NULL_RETURN_VAL(zone, EINA_FALSE);
zone->idx = idx;
zone->x = x;
zone->y = y;
zone->w = w;
zone->h = h;
zone->last_mouse.x = -1;
zone->last_mouse.y = -1;
zone->win = elm_win_add(NULL, "eruler", ELM_WIN_UTILITY);
EINA_SAFETY_ON_NULL_RETURN_VAL(zone->win, EINA_FALSE);
DBG("created zone #%d (%d, %d, %d, %d) as win=%p",
idx, x, y, w, h, zone->win);
zone->evas = evas_object_evas_get(zone->win);
elm_win_title_set(zone->win, "ERuler");
elm_win_autodel_set(zone->win, EINA_TRUE);
elm_win_layer_set(zone->win, 1000);
elm_win_alpha_set(zone->win, EINA_TRUE);
evas_object_move(zone->win, x, y);
evas_object_resize(zone->win, w, h);
evas_object_event_callback_add(zone->win,
EVAS_CALLBACK_RESIZE,
_zone_win_resize_cb, zone);
evas_object_event_callback_add(zone->win,
EVAS_CALLBACK_KEY_DOWN,
_zone_win_key_down_cb, zone);
evas_object_event_callback_add(zone->win,
EVAS_CALLBACK_DEL,
_zone_win_del_cb, zone);
zone_hint_setup(zone);
zone_ruler_setup(zone);
zone_zoom_pre_setup(zone);
zone_gui_setup(zone);
zones = eina_list_append(zones, zone);
return EINA_TRUE;
}
static Eina_Bool
_list_ruler_themes(const Ecore_Getopt *parser EINA_UNUSED, const Ecore_Getopt_Desc *desc EINA_UNUSED, const char *str EINA_UNUSED, void *data EINA_UNUSED, Ecore_Getopt_Value *storage)
{
const char **itr;
puts("Ruler types:");
for (itr = ruler_type_names_strs; *itr != NULL; itr++)
printf("\t%s\n", *itr);
*storage->boolp = EINA_TRUE;
return EINA_TRUE;
}
static const Ecore_Getopt options = {
PACKAGE_NAME,
"%prog [options]",
PACKAGE_VERSION,
"(C) 2013 Enlightenment Project",
"GPL-2",
"On-Screen Ruler and Measurement Tools.",
EINA_TRUE,
{
ECORE_GETOPT_STORE_FALSE('D', "hide-distances",
"Start with distances hidden."),
ECORE_GETOPT_STORE_FALSE('G', "hide-guides",
"Start with guides hidden."),
ECORE_GETOPT_STORE_TRUE('x', "hexa-colors",
"Start with colors in hexadecimal format."),
ECORE_GETOPT_CALLBACK_ARGS('r', "ruler",
"Define a ruler using format "
"'x:y:w:h[:style]'",
"x:y:w:h[:style]",
_parse_cmdline_ruler, NULL),
ECORE_GETOPT_CALLBACK_NOARGS('T', "list-ruler-themes", "List ruler themes",
_list_ruler_themes, NULL),
ECORE_GETOPT_CHOICE('t', "ruler-theme",
"Specify initial theme for all new rulers",
ruler_type_names_strs),
ECORE_GETOPT_VERSION('V', "version"),
ECORE_GETOPT_COPYRIGHT('C', "copyright"),
ECORE_GETOPT_LICENSE('L', "license"),
ECORE_GETOPT_HELP('h', "help"),
ECORE_GETOPT_SENTINEL
}
};
int
elm_main(int argc, char **argv)
{
int args;
Eina_List *rulers_specs = NULL, *unused_rulers_specs;
Ruler_Spec *rs = NULL;
char *initial_ruler_type_str = NULL;
Eina_Bool quit_option = EINA_FALSE;
Ecore_Getopt_Value values[] = {
ECORE_GETOPT_VALUE_BOOL(show_distances),
ECORE_GETOPT_VALUE_BOOL(show_guides),
ECORE_GETOPT_VALUE_BOOL(hex_colors),
ECORE_GETOPT_VALUE_PTR_CAST(rulers_specs),
ECORE_GETOPT_VALUE_BOOL(quit_option),
ECORE_GETOPT_VALUE_STR(initial_ruler_type_str),
ECORE_GETOPT_VALUE_BOOL(quit_option),
ECORE_GETOPT_VALUE_BOOL(quit_option),
ECORE_GETOPT_VALUE_BOOL(quit_option),
ECORE_GETOPT_VALUE_BOOL(quit_option),
ECORE_GETOPT_VALUE_NONE
};
const Eina_List *lm, *lz;
Zone *zone;
Evas_Object *tmp;
int i, count;
_log_dom = eina_log_domain_register("eruler", EINA_COLOR_CYAN);
if (_log_dom < 0)
{
EINA_LOG_CRIT("Couldn't register log domain: 'eruler'");
elm_shutdown();
return EXIT_FAILURE;
}
args = ecore_getopt_parse(&options, values, argc, argv);
if (args < 0)
{
ERR("Could not parse command line options.");
retval = EXIT_FAILURE;
goto end;
}
if (quit_option) goto end;
if (initial_ruler_type_str)
{
for (i = 0; ruler_type_names_strs[i] != NULL; i++)
if (strcmp(ruler_type_names_strs[i], initial_ruler_type_str) == 0)
{
initial_ruler_type = i;
break;
}
}
elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED);
elm_app_compile_bin_dir_set(PACKAGE_BIN_DIR);
elm_app_compile_lib_dir_set(PACKAGE_LIB_DIR);
elm_app_compile_data_dir_set(PACKAGE_DATA_DIR);
#if HAVE_GETTEXT && ENABLE_NLS
elm_app_compile_locale_set(LOCALEDIR);
#endif
elm_app_info_set(elm_main, "eruler", "themes/default.edj");
snprintf(theme_file, sizeof(theme_file), "%s/themes/default.edj",
elm_app_data_dir_get());
tmp = elm_win_add(NULL, "eruler", ELM_WIN_BASIC);
if (0) {}
#ifdef HAVE_ECORE_X
else if (elm_win_xwindow_get(tmp))
platform_funcs = platform_funcs_x_get();
#endif
else
{
CRI("Window platform not supported.");
retval = EXIT_FAILURE;
goto end;
}
if (!platform_funcs->pre_setup())
{
CRI("Could not pre-setup platform.");
retval = EXIT_FAILURE;
goto end;
}
count = platform_funcs->zones_count();
for (i = 0; i < count; i++)
{
int x, y, w, h;
if (!platform_funcs->zone_geometry_get(i, &x, &y, &w, &h))
CRI("Could not get Zone #%d geometry.", i);
else if (!zone_create(i, x, y, w, h))
CRI("Could not create zone #%d (%d, %d, %d, %d).",
i, x, y, w, h);
}
if (!zones)
{
show_gui_error("Couldn't create any zone!");
retval = EXIT_FAILURE;
}
else
{
Eina_List *l;
EINA_LIST_FOREACH(zones, l, zone)
{
if (!platform_funcs->zone_setup(zone))
CRI("Could not setup Zone #%d", zone->idx);
}
}
if (!platform_funcs->post_setup())
{
show_gui_error("Platform couldn't do post_setup()");
retval = EXIT_FAILURE;
}
unused_rulers_specs = eina_list_clone(rulers_specs);
EINA_LIST_FOREACH(zones, lz, zone)
{
Eina_Bool did_ruler_spec = EINA_FALSE;
int zx1, zy1, zx2, zy2;
zx1 = zone->x;
zy1 = zone->y;
zx2 = zone->x + zone->w;
zy2 = zone->y + zone->h;
EINA_LIST_FOREACH(rulers_specs, lm, rs)
{
Evas_Object *ruler;
Ruler_Data *rd;
int mx, my;
mx = rs->x - zx1;
my = rs->y - zy1;
if ((mx < 0) || (rs->x + rs->w > zx2) ||
(my < 0) || (rs->y + rs->h > zy2))
continue;
if (zone->last_ruler_used)
zone_ruler_create(zone);
ruler = zone_ruler_last_get(zone);
rd = ruler_data_get(ruler);
rd->type = rs->type == RULER_TYPE_SENTINEL ?
initial_ruler_type : rs->type;
rd->start.x = mx;
rd->start.y = my;
rd->stop.x = mx + rs->w - 1;
rd->stop.y = my + rs->h - 1;
ruler_type_apply(ruler);
evas_object_show(ruler);
zone_last_ruler_used_set(zone, EINA_TRUE);
unused_rulers_specs = eina_list_remove(unused_rulers_specs, rs);
did_ruler_spec = EINA_TRUE;
}
if (did_ruler_spec)
zone_ruler_create(zone);
}
EINA_LIST_FREE(unused_rulers_specs, rs)
ERR("Unused ruler spec %d,%d + %dx%d (type=%s) not fully inside a zone!",
rs->x, rs->y, rs->w, rs->h, ruler_type_names_strs[rs->type]);
EINA_LIST_FREE(rulers_specs, rs)
free(rs);
evas_object_del(tmp);
if (retval != EXIT_SUCCESS)
{
while (zones)
{
zone = zones->data;
evas_object_del(zone->win);
}
}
else
{
platform_funcs->global_key_grab("F2", _toggle_visibility_cb, NULL);
}
elm_run();
platform_funcs->pre_teardown();
end:
eina_log_domain_unregister(_log_dom);
elm_shutdown();
return retval;
}
static void
_error_quit_cb(void *data, Evas_Object *o EINA_UNUSED, void *event EINA_UNUSED)
{
Evas_Object *popup = data;
evas_object_del(popup);
DBG("Error dialog deleted, exit!");
elm_exit();
}
void
show_gui_error(const char *message)
{
Evas_Object *win, *bx, *help, *sep, *bt;
CRI("show gui for error '%s'", message);
win = elm_win_util_standard_add("eruler-error", "ERuler - Error!");
bx = elm_box_add(win);
elm_box_horizontal_set(bx, EINA_FALSE);
evas_object_size_hint_weight_set(bx, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(bx, EVAS_HINT_FILL, EVAS_HINT_FILL);
evas_object_show(bx);
elm_win_resize_object_add(win, bx);
help = elm_entry_add(bx);
elm_entry_editable_set(help, EINA_FALSE);
#if (ELM_VERSION_MAJOR >= 1) && (ELM_VERSION_MINOR >= 8)
elm_scroller_policy_set(help, ELM_SCROLLER_POLICY_OFF,
ELM_SCROLLER_POLICY_AUTO);
#else
elm_entry_scrollbar_policy_set(help, ELM_SCROLLER_POLICY_OFF,
ELM_SCROLLER_POLICY_AUTO);
#endif
evas_object_size_hint_weight_set(help, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(help, EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_object_text_set(help, message);
evas_object_show(help);
elm_box_pack_end(bx, help);
sep = elm_separator_add(bx);
evas_object_size_hint_weight_set(sep, EVAS_HINT_EXPAND, 0.0);
evas_object_size_hint_align_set(sep, EVAS_HINT_FILL, 0.5);
evas_object_show(sep);
elm_box_pack_end(bx, sep);
bt = elm_button_add(bx);
elm_object_text_set(bt, "Quit");
evas_object_smart_callback_add(bt, "clicked", _error_quit_cb, win);
evas_object_show(bt);
elm_box_pack_end(bx, bt);
evas_object_resize(win, 320, 100);
evas_object_show(win);
elm_win_activate(win);
retval = EXIT_FAILURE;
}
Evas_Object *
zone_win_get(const Zone *zone)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(zone, NULL);
return zone->win;
}
Evas_Object *
zone_screen_copy_object_get(const Zone *zone)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(zone, NULL);
return zone->zoom.image;
}
void
zone_screen_copy_ready_set(Zone *zone, Eina_Bool ready)
{
EINA_SAFETY_ON_NULL_RETURN(zone);
zone->zoom.ready = !!ready;
if (!zone->zoom.frame) return;
if (ready)
evas_object_show(zone->zoom.frame);
else
evas_object_hide(zone->zoom.frame);
}
void
zone_geometry_get(const Zone *zone, int *x, int *y, int *w, int *h)
{
if (x) *x = 0;
if (y) *y = 0;
if (w) *w = 0;
if (h) *h = 0;
EINA_SAFETY_ON_NULL_RETURN(zone);
if (x) *x = zone->x;
if (y) *y = zone->y;
if (w) *w = zone->w;
if (h) *h = zone->h;
}
int
zone_index_get(const Zone *zone)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(zone, -1);
return zone->idx;
}
ELM_MAIN();