diff --git a/configure.ac b/configure.ac index c15d108..b1d5641 100644 --- a/configure.ac +++ b/configure.ac @@ -164,6 +164,7 @@ Makefile ephoto.spec data/Makefile data/desktop/Makefile +data/images/Makefile data/themes/Makefile data/themes/default/Makefile data/themes/default/images/Makefile diff --git a/data/Makefile.am b/data/Makefile.am index c37a4b6..b9e3741 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -1,2 +1,2 @@ MAINTAINERCLEANFILES = Makefile.in Makefile -SUBDIRS = themes desktop +SUBDIRS = desktop images themes diff --git a/data/images/Makefile.am b/data/images/Makefile.am new file mode 100644 index 0000000..f1dbc96 --- /dev/null +++ b/data/images/Makefile.am @@ -0,0 +1,20 @@ +MAINTAINERCLEANFILES = Makefile.in Makefile + +IMAGES = \ +back.png \ +back_grid.png \ +first.png \ +folder.png \ +forward.png \ +grid.png \ +last.png \ +single.png \ +slideshow.png \ +up.png \ +zoom-fit.png \ +zoom-in.png \ +zoom-original.png \ +zoom-out.png + +imagesdir = $(pkgdatadir)/images +images_DATA = $(IMAGES) diff --git a/data/images/back.png b/data/images/back.png new file mode 100644 index 0000000..964d853 Binary files /dev/null and b/data/images/back.png differ diff --git a/data/images/back_grid.png b/data/images/back_grid.png new file mode 100644 index 0000000..a19eabd Binary files /dev/null and b/data/images/back_grid.png differ diff --git a/data/images/first.png b/data/images/first.png new file mode 100644 index 0000000..0c99ca1 Binary files /dev/null and b/data/images/first.png differ diff --git a/data/images/folder.png b/data/images/folder.png new file mode 100644 index 0000000..64e2f53 Binary files /dev/null and b/data/images/folder.png differ diff --git a/data/images/forward.png b/data/images/forward.png new file mode 100644 index 0000000..3e4516c Binary files /dev/null and b/data/images/forward.png differ diff --git a/data/images/grid.png b/data/images/grid.png new file mode 100644 index 0000000..19353bd Binary files /dev/null and b/data/images/grid.png differ diff --git a/data/images/last.png b/data/images/last.png new file mode 100644 index 0000000..78825d8 Binary files /dev/null and b/data/images/last.png differ diff --git a/data/images/single.png b/data/images/single.png new file mode 100644 index 0000000..ef05423 Binary files /dev/null and b/data/images/single.png differ diff --git a/data/images/slideshow.png b/data/images/slideshow.png new file mode 100644 index 0000000..77f9365 Binary files /dev/null and b/data/images/slideshow.png differ diff --git a/data/images/up.png b/data/images/up.png new file mode 100644 index 0000000..9d1cbe4 Binary files /dev/null and b/data/images/up.png differ diff --git a/data/images/zoom-fit.png b/data/images/zoom-fit.png new file mode 100644 index 0000000..b253d22 Binary files /dev/null and b/data/images/zoom-fit.png differ diff --git a/data/images/zoom-in.png b/data/images/zoom-in.png new file mode 100644 index 0000000..dc53d9e Binary files /dev/null and b/data/images/zoom-in.png differ diff --git a/data/images/zoom-original.png b/data/images/zoom-original.png new file mode 100644 index 0000000..de57e90 Binary files /dev/null and b/data/images/zoom-original.png differ diff --git a/data/images/zoom-out.png b/data/images/zoom-out.png new file mode 100644 index 0000000..295b7ef Binary files /dev/null and b/data/images/zoom-out.png differ diff --git a/data/themes/default/ephoto.edc b/data/themes/default/ephoto.edc index 7bdabc5..38204b9 100644 --- a/data/themes/default/ephoto.edc +++ b/data/themes/default/ephoto.edc @@ -1,212 +1,603 @@ externals { -external: "elm"; + external: "elm"; } collections { - images - { - } - group - { - name: "ephoto/layout/single/browser"; - parts - { - part - { - name: "ephoto.swallow.content"; + group { name: "elm/layout/ephoto/orient"; + parts { + part { name: "elm.swallow.content"; type: SWALLOW; - description - { - rel1.relative: 0.0 0.0; - rel1.offset: 0 0; - rel2.relative: 1.0 1.0; - rel2.offset: -1 -1; + description { state: "default" 0.0; } - } - } - } - group - { - name: "elm/layout/ephoto/orient"; - parts - { - part - { - name: "elm.swallow.content"; - type: SWALLOW; - description - { - state: "default" 0.0; - } - description - { - state: "rotate_0" 0.0; + description { state: "rotate_0" 0.0; inherit: "default" 0.0; - map - { + map { on: 1; - rotation.z : 0; + rotation.z: 0; } } - description - { - state: "rotate_90" 0.0; + description { state: "rotate_90" 0.0; inherit: "default" 0.0; - map - { + map { on: 1; - rotation.z : 90; + rotation.z: 90; } } - description - { - state: "rotate_180" 0.0; + description { state: "rotate_180" 0.0; inherit: "default" 0.0; - map - { + map { on: 1; - rotation.z : 180; + rotation.z: 180; } } - description - { - state: "rotate_270" 0.0; + description { state: "rotate_270" 0.0; inherit: "default" 0.0; - map - { + map { on: 1; - rotation.z : 270; + rotation.z: 270; } } - description - { - state: "flip_horiz" 0.0; + description { state: "flip_horiz" 0.0; inherit: "default" 0.0; - map - { + map { on: 1; - rotation.y : 180; + rotation.y: 180; } } - description - { - state: "flip_vert" 0.0; + description { state: "flip_vert" 0.0; inherit: "default" 0.0; - map - { + map { on: 1; - rotation.x : 180; + rotation.x: 180; } } - description - { - state: "flip_horiz_90" 0.0; + description { state: "flip_horiz_90" 0.0; inherit: "default" 0.0; - map - { + map { on: 1; - rotation - { + rotation { z: 90; y: 180; } } } - description - { - state: "flip_vert_90" 0.0; + description { state: "flip_vert_90" 0.0; inherit: "default" 0.0; - map - { + map { on: 1; - rotation - { + rotation { z: 90; x: 180; } } } } - programs - { - program - { - signal: "state,rotate,0"; - source: "ephoto"; - action: STATE_SET "rotate_0" 0.0; - target: "elm.swallow.content"; + programs { + program { + signal: "state,rotate,0"; + source: "ephoto"; + action: STATE_SET "rotate_0" 0.0; + target: "elm.swallow.content"; } - program - { - signal: "state,rotate,90"; - source: "ephoto"; - action: STATE_SET "rotate_90" 0.0; - target: "elm.swallow.content"; + program { + signal: "state,rotate,90"; + source: "ephoto"; + action: STATE_SET "rotate_90" 0.0; + target: "elm.swallow.content"; } - program - { - signal: "state,rotate,180"; - source: "ephoto"; - action: STATE_SET "rotate_180" 0.0; - target: "elm.swallow.content"; + program { + signal: "state,rotate,180"; + source: "ephoto"; + action: STATE_SET "rotate_180" 0.0; + target: "elm.swallow.content"; } - program - { - signal: "state,rotate,270"; - source: "ephoto"; - action: STATE_SET "rotate_270" 0.0; - target: "elm.swallow.content"; + program { + signal: "state,rotate,270"; + source: "ephoto"; + action: STATE_SET "rotate_270" 0.0; + target: "elm.swallow.content"; } - program - { - signal: "state,flip,horiz"; - source: "ephoto"; - action: STATE_SET "flip_horiz" 0.0; - target: "elm.swallow.content"; + program { + signal: "state,flip,horiz"; + source: "ephoto"; + action: STATE_SET "flip_horiz" 0.0; + target: "elm.swallow.content"; } - program - { - signal: "state,flip,vert"; - source: "ephoto"; - action: STATE_SET "flip_vert" 0.0; - target: "elm.swallow.content"; + program { + signal: "state,flip,vert"; + source: "ephoto"; + action: STATE_SET "flip_vert" 0.0; + target: "elm.swallow.content"; } - program - { - signal: "state,flip,horiz,90"; - source: "ephoto"; - action: STATE_SET "flip_horiz_90" 0.0; - target: "elm.swallow.content"; + program { + signal: "state,flip,horiz,90"; + source: "ephoto"; + action: STATE_SET "flip_horiz_90" 0.0; + target: "elm.swallow.content"; } - program - { - signal: "state,flip,vert,90"; - source: "ephoto"; - action: STATE_SET "flip_vert_90" 0.0; - target: "elm.swallow.content"; + program { + signal: "state,flip,vert,90"; + source: "ephoto"; + action: STATE_SET "flip_vert_90" 0.0; + target: "elm.swallow.content"; } } } } - group - { - name: "ephoto,thumb,grid"; - parts - { - part - { - name: "ephoto.swallow.content.thumb"; - type: SWALLOW; - mouse_events: 1; - description - { - state: "default" 0.0; - rel1.relative : 0.0 0.0; - rel1.offset : 0 0; - rel2.relative : 1.0 1.0; - rel2.offset : -1 -1; + group { name: "elm/gengrid/item/ephoto-album-preview/default"; + data.item: "labels" "elm.text"; + data.item: "icons" "elm.swallow.icon.1 elm.swallow.icon.2 elm.swallow.icon.3"; + data.item: "states" "have_files"; + images { + image: "bt_sm_base1.png" COMP; + image: "bt_sm_shine.png" COMP; + image: "bt_sm_hilight.png" COMP; + image: "directory-512.png" COMP; + } + parts { + part { name: "event"; + type: RECT; + repeat_events: 1; + description { state: "default" 0.0; + color: 0 0 0 0; } } + part { name: "bg"; + clip_to: "disclip"; + mouse_events: 0; + description { state: "default" 0.0; + visible: 0; + color: 255 255 255 0; + rel1.offset: -3 -3; + rel2.offset: 2 2; + image { + normal: "bt_sm_base1.png"; + border: 6 6 6 6; + middle: SOLID; + } + } + description { state: "selected" 0.0; + inherit: "default" 0.0; + visible: 1; + color: 255 255 255 255; + } + } + part { name: "image"; + type: IMAGE; + mouse_events: 0; + description { state: "default" 0.0; + aspect_preference: BOTH; + aspect: 1.0 1.0; + image.normal: "directory-512.png"; + rel2 { + to_y: "elm.text"; + relative: 1.0 0.0; + offset: -1 -2; + } + } + } + part { name: "have-files-clipper"; + type: RECT; + description { state: "default" 0.0; + color: 255 255 255 0; + visible: 0; + } + description { state: "visible" 0.0; + inherit: "default" 0.0; + color: 255 255 255 255; + visible: 1; + } + } + part { name: "icon_box_margin"; + type: RECT; + mouse_events: 0; + clip_to: "have-files-clipper"; + description { state: "default" 0.0; + color: 255 255 255 0; + rel1 { + to: "icon_box"; + offset: -1 -1; + } + rel2 { + to: "icon_box"; + offset: 0 0; + } + } + } + part { name: "icon_box"; + type: RECT; + mouse_events: 0; + clip_to: "have-files-clipper"; + description { state: "default" 0.0; + color: 255 255 255 0; + align: 1.0 1.0; + min: 32 32; + + rel1 { + to: "image"; + relative: 0.0 0.0; + offset: 0 20; + } + rel2 { + to: "image"; + relative: 1.0 1.0; + offset: -1 -1; + } + } + } + part { name: "elm.swallow.icon.1"; + type: SWALLOW; + mouse_events: 0; + clip_to: "have-files-clipper"; + description { state: "default" 0.0; + color: 255 0 0 255; + map { + on: 0; + rotation { + z: 0; + } + } + rel1 { + relative: 0.2 0.2; + to: "icon_box"; + } + rel2 { + relative: 0.8 0.8; + offset: -1 -1; + to: "icon_box"; + } + } + } + part { name: "elm.swallow.icon.2"; + type: SWALLOW; + mouse_events: 0; + clip_to: "have-files-clipper"; + description { state: "default" 0.0; + color: 255 0 0 255; + map { + on: 1; + rotation { + z: 15; + } + } + rel1 { + relative: 0.2 0.2; + to: "icon_box"; + } + rel2 { + relative: 0.8 0.8; + offset: -1 -1; + to: "icon_box"; + } + } + } + part { name: "elm.swallow.icon.3"; + type: SWALLOW; + mouse_events: 0; + clip_to: "have-files-clipper"; + description { state: "default" 0.0; + color: 255 0 0 255; + map { + on: 1; + rotation { + z: -15; + } + } + rel1 { + relative: 0.2 0.2; + to: "icon_box"; + } + rel2 { + relative: 0.8 0.8; + offset: -1 -1; + to: "icon_box"; + } + } + } + part { name: "elm.text"; + clip_to: "disclip"; + type: TEXT; + effect: SOFT_SHADOW; + mouse_events: 0; + scale: 1; + description { state: "default" 0.0; + rel1 { + relative: 0.0 1.0; + offset: 20 -30; + } + rel2 { + relative: 1.0 1.0; + offset: -21 -15; + } + color: 0 0 0 255; + color3: 0 0 0 0; + text { + font: "Sans"; + size: 10; + min: 0 1; + align: 0.5 0.0; + text_class: "grid_item"; + } + } + description { state: "selected" 0.0; + inherit: "default" 0.0; + color: 255 255 255 255; + } + } + part { name: "fg1"; + clip_to: "disclip"; + mouse_events: 0; + description { state: "default" 0.0; + visible: 0; + color: 255 255 255 0; + rel1.offset: -3 -3; + rel2 { + relative: 1.0 0.5; + offset: 2 -1; + } + image { + normal: "bt_sm_hilight.png"; + border: 6 6 6 0; + } + } + description { state: "selected" 0.0; + inherit: "default" 0.0; + visible: 1; + color: 255 255 255 255; + } + } + part { name: "fg2"; + clip_to: "disclip"; + mouse_events: 0; + description { state: "default" 0.0; + visible: 0; + color: 255 255 255 0; + rel1.offset: -3 -3; + rel2.offset: 2 2; + image { + normal: "bt_sm_shine.png"; + border: 6 6 6 0; + } + } + description { state: "selected" 0.0; + inherit: "default" 0.0; + visible: 1; + color: 255 255 255 255; + } + } + part { name: "disclip"; + type: RECT; + description { state: "default" 0.0; + rel1.to: "bg"; + rel2.to: "bg"; + } + description { state: "disabled" 0.0; + inherit: "default" 0.0; + color: 255 255 255 64; + } + } + } + programs { + program { name: "load"; + signal: "load"; + // script { + // new Float:val; + // custom_state(PART:"elm.swallow.icon.3", "default", 0.0); + // custom_state(PART:"elm.swallow.icon.1", "default", 0.0); + // val = randf(); + // val = 40 * val - 20; + // set_state_val(PART:"elm.swallow.icon.3", STATE_MAP_ROT_Z, val); + // val = randf(); + // val = 40 * val - 20; + // set_state_val(PART:"elm.swallow.icon.1", STATE_MAP_ROT_Z, val); + //set_state(PART:"elm.swallow.icon.1", "custom", 0.0); + // set_state(PART:"elm.swallow.icon.3", "custom", 0.0); + //} + } + program { name: "go_active"; + signal: "elm,state,selected"; + source: "elm"; + action: STATE_SET "selected" 0.0; + target: "bg"; + target: "fg1"; + target: "fg2"; + target: "elm.text"; + } + program { name: "go_passive"; + signal: "elm,state,unselected"; + source: "elm"; + action: STATE_SET "default" 0.0; + target: "bg"; + target: "fg1"; + target: "fg2"; + target: "elm.text"; + transition: LINEAR 0.1; + } + program { name: "go_disabled"; + signal: "elm,state,disabled"; + source: "elm"; + action: STATE_SET "disabled" 0.0; + target: "disclip"; + } + program { name: "go_enabled"; + signal: "elm,state,enabled"; + source: "elm"; + action: STATE_SET "default" 0.0; + target: "disclip"; + } + program { + signal: "elm,state,have_files,active"; + source: "elm"; + action: STATE_SET "visible" 0.0; + target: "have-files-clipper"; + } + } + } + group { name: "elm/gengrid/item/ephoto-up/default"; + data.item: "labels" "elm.text"; + images { + image: "bt_sm_base1.png" COMP; + image: "bt_sm_shine.png" COMP; + image: "bt_sm_hilight.png" COMP; + image: "directory-up-512.png" COMP; + } + parts { + part { name: "event"; + type: RECT; + repeat_events: 1; + description { state: "default" 0.0; + color: 0 0 0 0; + } + } + part { name: "bg"; + clip_to: "disclip"; + mouse_events: 0; + description { state: "default" 0.0; + visible: 0; + color: 255 255 255 0; + rel1.offset: -3 -3; + rel2.offset: 2 2; + image { + normal: "bt_sm_base1.png"; + border: 6 6 6 6; + middle: SOLID; + } + } + description { state: "selected" 0.0; + inherit: "default" 0.0; + visible: 1; + color: 255 255 255 255; + } + } + part { name: "image"; + type: IMAGE; + mouse_events: 0; + description { state: "default" 0.0; + aspect_preference: BOTH; + aspect: 1.0 1.0; + image.normal: "directory-up-512.png"; + rel2 { + to_y: "elm.text"; + relative: 1.0 0.0; + offset: -1 -2; + } + } + } + part { name: "elm.text"; + clip_to: "disclip"; + type: TEXT; + effect: SOFT_SHADOW; + mouse_events: 0; + scale: 1; + description { state: "default" 0.0; + rel1 { + relative: 0.0 1.0; + offset: 20 -30; + } + rel2 { + relative: 1.0 1.0; + offset: -21 -15; + } + color: 0 0 0 255; + color3: 0 0 0 0; + text { + font: "Sans"; + size: 10; + min: 0 1; + align: 0.5 0.0; + text_class: "grid_item"; + } + } + description { state: "selected" 0.0; + inherit: "default" 0.0; + color: 255 255 255 255; + } + } + part { name: "fg1"; + clip_to: "disclip"; + mouse_events: 0; + description { state: "default" 0.0; + visible: 0; + color: 255 255 255 0; + rel1.offset: -3 -3; + rel2 { + relative: 1.0 0.5; + offset: 2 -1; + } + image { + normal: "bt_sm_hilight.png"; + border: 6 6 6 0; + } + } + description { state: "selected" 0.0; + inherit: "default" 0.0; + visible: 1; + color: 255 255 255 255; + } + } + part { name: "fg2"; + clip_to: "disclip"; + mouse_events: 0; + description { state: "default" 0.0; + visible: 0; + color: 255 255 255 0; + rel1.offset: -3 -3; + rel2.offset: 2 2; + image { + normal: "bt_sm_shine.png"; + border: 6 6 6 0; + } + } + description { state: "selected" 0.0; + inherit: "default" 0.0; + visible: 1; + color: 255 255 255 255; + } + } + part { name: "disclip"; + type: RECT; + description { state: "default" 0.0; + rel1.to: "bg"; + rel2.to: "bg"; + } + description { state: "disabled" 0.0; + inherit: "default" 0.0; + color: 255 255 255 64; + } + } + } + programs { + program { name: "go_active"; + signal: "elm,state,selected"; + source: "elm"; + action: STATE_SET "selected" 0.0; + target: "bg"; + target: "fg1"; + target: "fg2"; + target: "elm.text"; + } + program { name: "go_passive"; + signal: "elm,state,unselected"; + source: "elm"; + action: STATE_SET "default" 0.0; + target: "bg"; + target: "fg1"; + target: "fg2"; + target: "elm.text"; + transition: LINEAR 0.1; + } + program { name: "go_disabled"; + signal: "elm,state,disabled"; + source: "elm"; + action: STATE_SET "disabled" 0.0; + target: "disclip"; + } + program { name: "go_enabled"; + signal: "elm,state,enabled"; + source: "elm"; + action: STATE_SET "default" 0.0; + target: "disclip"; + } } } } diff --git a/data/themes/default/images/Makefile.am b/data/themes/default/images/Makefile.am index 3b8551d..086e670 100644 --- a/data/themes/default/images/Makefile.am +++ b/data/themes/default/images/Makefile.am @@ -1,2 +1,9 @@ MAINTAINERCLEANFILES = Makefile.in +EXTRA_DIST = \ +thumb_shadow.png \ +bt_sm_base1.png \ +bt_sm_shine.png \ +bt_sm_hilight.png \ +directory-512.png \ +directory-up-512.png diff --git a/data/themes/default/images/bt_sm_base1.png b/data/themes/default/images/bt_sm_base1.png new file mode 100644 index 0000000..c37e182 Binary files /dev/null and b/data/themes/default/images/bt_sm_base1.png differ diff --git a/data/themes/default/images/bt_sm_hilight.png b/data/themes/default/images/bt_sm_hilight.png new file mode 100644 index 0000000..63899ca Binary files /dev/null and b/data/themes/default/images/bt_sm_hilight.png differ diff --git a/data/themes/default/images/bt_sm_shine.png b/data/themes/default/images/bt_sm_shine.png new file mode 100644 index 0000000..23bf311 Binary files /dev/null and b/data/themes/default/images/bt_sm_shine.png differ diff --git a/data/themes/default/images/directory-512.png b/data/themes/default/images/directory-512.png new file mode 100644 index 0000000..7beeb3d Binary files /dev/null and b/data/themes/default/images/directory-512.png differ diff --git a/data/themes/default/images/directory-up-512.png b/data/themes/default/images/directory-up-512.png new file mode 100644 index 0000000..fc860e2 Binary files /dev/null and b/data/themes/default/images/directory-up-512.png differ diff --git a/src/bin/Makefile.am b/src/bin/Makefile.am index 90809a4..acf6b58 100644 --- a/src/bin/Makefile.am +++ b/src/bin/Makefile.am @@ -17,9 +17,10 @@ _sources = \ ephoto.c \ ephoto_main.c \ ephoto_config.c \ - ephoto_list_browser.c \ + ephoto_directory_thumb.c \ ephoto_thumb_browser.c \ - ephoto_single_browser.c + ephoto_single_browser.c \ + ephoto_slideshow.c _libs = @ELEMENTARY_LIBS@ @EFREET_MIME_LIBS@ @EIO_LIBS@ @EXIF_LIBS@ @EET_LIBS@ diff --git a/src/bin/ephoto.c b/src/bin/ephoto.c index d7a2bb7..b0b5cdc 100644 --- a/src/bin/ephoto.c +++ b/src/bin/ephoto.c @@ -29,7 +29,7 @@ elm_main(int argc, char **argv) elm_need_ethumb(); elm_init(argc, argv); - elm_theme_extension_add(NULL, THEME_FILE); + elm_theme_extension_add(NULL, PACKAGE_DATA_DIR"/themes/default/ephoto.edj"); if (!efreet_mime_init()) fprintf(stderr, "Could not init efreet_mime!\n"); diff --git a/src/bin/ephoto.h b/src/bin/ephoto.h index 819c9f0..0bacfaa 100644 --- a/src/bin/ephoto.h +++ b/src/bin/ephoto.h @@ -24,7 +24,6 @@ #define THEME_FILE PACKAGE_DATA_DIR"/themes/default/ephoto.edj" -/*Typedefs*/ typedef struct _Ephoto_Config Ephoto_Config; typedef struct _Ephoto Ephoto; typedef struct _Ephoto_Entry Ephoto_Entry; @@ -33,50 +32,48 @@ typedef struct _Ephoto_Event_Entry_Create Ephoto_Event_Entry_Create; typedef enum _Ephoto_State Ephoto_State; typedef enum _Ephoto_Orient Ephoto_Orient; -/*Main Gui Functions/Callbacks*/ Evas_Object *ephoto_window_add(const char *path); void ephoto_title_set(Ephoto *ephoto, const char *title); void ephoto_thumb_size_set(Ephoto *ephoto, int size); Evas_Object *ephoto_thumb_add(Ephoto *ephoto, Evas_Object *parent, const char *path); void ephoto_thumb_path_set(Evas_Object *o, const char *path); -Evas_Object *ephoto_list_icon_add(Ephoto *ephoto, Evas_Object *parent, const char *standard); void ephoto_directory_set(Ephoto *ephoto, const char *path); -void ephoto_promote_list_browser(Ephoto *ephoto); -void ephoto_promote_thumb_browser(Ephoto *ephoto); -void ephoto_promote_single_browser(Ephoto *ephoto, Ephoto_Entry *e); -/*Get the exif orientation of a JPEG*/ Ephoto_Orient ephoto_file_orient_get(const char *path); -/*Config Functions/Callbacks*/ Eina_Bool ephoto_config_init(Ephoto *em); void ephoto_config_save(Ephoto *em, Eina_Bool instant); void ephoto_config_free(Ephoto *em); -/*List Browser*/ -Evas_Object *ephoto_list_browser_add(Ephoto *ephoto, Evas_Object *parent); -void ephoto_list_browser_entry_set(Evas_Object *obj, Ephoto_Entry *entry); - -/*Thumb Browser*/ -Evas_Object *ephoto_thumb_browser_add(Ephoto *ephoto, Evas_Object *parent); -void ephoto_thumb_browser_entry_set(Evas_Object *obj, Ephoto_Entry *entry); - -/*Single Browser*/ Evas_Object *ephoto_single_browser_add(Ephoto *ephoto, Evas_Object *parent); void ephoto_single_browser_entry_set(Evas_Object *obj, Ephoto_Entry *entry); void ephoto_single_browser_path_pending_set(Evas_Object *obj, const char *path); + /* smart callbacks called: + * "back" - the user want to go back to the previous screen. + */ + +Evas_Object *ephoto_slideshow_add(Ephoto *ephoto, Evas_Object *parent); +void ephoto_slideshow_entry_set(Evas_Object *obj, Ephoto_Entry *entry); + /* smart callbacks called: + * "back" - the user want to go back to the previous screen. + */ + +Evas_Object *ephoto_directory_thumb_add(Evas_Object *parent, Ephoto_Entry *e); + +Evas_Object *ephoto_thumb_browser_add(Ephoto *ephoto, Evas_Object *parent); + +/* smart callbacks called: + * "selected" - an item in the thumb browser is selected. The selected Ephoto_Entry is passed as event_info argument. + */ -/*Ephoto View*/ enum _Ephoto_State { - EPHOTO_STATE_LIST, EPHOTO_STATE_THUMB, EPHOTO_STATE_SINGLE, EPHOTO_STATE_SLIDESHOW }; -/*Ephoto Orientation*/ -enum _Ephoto_Orient +enum _Ephoto_Orient /* matches with exif orientation tag */ { EPHOTO_ORIENT_0 = 1, EPHOTO_ORIENT_FLIP_HORIZ = 2, @@ -88,40 +85,41 @@ enum _Ephoto_Orient EPHOTO_ORIENT_270 = 8 }; -/*Ephoto Config*/ +/* TODO: split into window & global config, allow multi window + * + * This also requires single instance, as 2 instances changing the + * same configuration will lead to problems. + * + * Single instance is better done as DBus, using FDO standard methods. + */ struct _Ephoto_Config { int config_version; - int autohide_toolbar; - int best_fit_images; + const char *editor; + double slideshow_timeout; + const char *slideshow_transition; + + /* these should be per-window */ int thumb_size; int thumb_gen_size; - double slideshow_timeout; const char *directory; - const char *slideshow_transition; + }; -/*Ephoto Main*/ struct _Ephoto { Evas_Object *win; Evas_Object *bg; - Evas_Object *overlay; - Evas_Object *layout; - Evas_Object *edje; Evas_Object *pager; - Evas_Object *help_but; - Evas_Object *list_browser; Evas_Object *thumb_browser; Evas_Object *single_browser; Evas_Object *slideshow; Eina_List *entries; - Eina_List *thumbs; - Eina_List *dirs; + Eina_List *thumbs; /* live thumbs that need to be regenerated on changes */ - int thumb_gen_size; + int thumb_gen_size; /* pending value for thumb_regen */ struct { Ecore_Timer *thumb_regen; } timer; @@ -137,16 +135,18 @@ struct _Ephoto Ephoto_Config *config; }; -/*Ephoto Entry*/ struct _Ephoto_Entry { const char *path; - const char *basename; + const char *basename; /* pointer inside path */ const char *label; Ephoto *ephoto; Elm_Gengrid_Item *item; - Elm_Genlist_Item *list_item; Eina_List *free_listeners; + Eina_List *dir_files; /* if dir, here contain files with preview */ + Eina_Bool dir_files_checked : 1; + Eina_Bool is_dir : 1; + Eina_Bool is_up : 1; }; struct _Ephoto_Event_Entry_Create @@ -154,24 +154,21 @@ struct _Ephoto_Event_Entry_Create Ephoto_Entry *entry; }; -/*Ephoto Entry Functions/Callbacks*/ Ephoto_Entry *ephoto_entry_new(Ephoto *ephoto, const char *path, const char *label); void ephoto_entry_free(Ephoto_Entry *entry); void ephoto_entry_free_listener_add(Ephoto_Entry *entry, void (*cb)(void *data, const Ephoto_Entry *entry), const void *data); void ephoto_entry_free_listener_del(Ephoto_Entry *entry, void (*cb)(void *data, const Ephoto_Entry *entry), const void *data); void ephoto_entries_free(Ephoto *ephoto); -/*Ephoto Logging*/ extern int __log_domain; #define DBG(...) EINA_LOG_DOM_DBG(__log_domain, __VA_ARGS__) #define INF(...) EINA_LOG_DOM_INFO(__log_domain, __VA_ARGS__) #define ERR(...) EINA_LOG_DOM_ERR(__log_domain, __VA_ARGS__) -/*Checks to determine if a file is an image*/ static inline Eina_Bool _ephoto_eina_file_direct_info_image_useful(const Eina_File_Direct_Info *info) { - const char *bname, *ext; + const char /* *type, */ *bname, *ext; bname = info->path + info->name_start; if (bname[0] == '.') return EINA_FALSE; @@ -185,23 +182,17 @@ _ephoto_eina_file_direct_info_image_useful(const Eina_File_Direct_Info *info) ext++; if ((strcasecmp(ext, "jpg") == 0) || (strcasecmp(ext, "jpeg") == 0) || - (strcasecmp(ext, "png") == 0) || - (strcasecmp(ext, "svg") == 0) || - (strcasecmp(ext, "svgz") == 0) || - (strcasecmp(ext, "bmp") == 0) || - (strcasecmp(ext, "dib") == 0) || - (strcasecmp(ext, "tiff") == 0) || - (strcasecmp(ext, "tif") == 0) || - (strcasecmp(ext, "psd") == 0)) + (strcasecmp(ext, "png") == 0)) return EINA_TRUE; } return EINA_FALSE; + /* seems that this does not play nice with threads */ + //if (!(type = efreet_mime_type_get(info->path))) return EINA_FALSE; + //return strncmp(type, "image/", sizeof("image/") - 1) == 0; } -/*Ephoto Event Handlers*/ -extern int EPHOTO_EVENT_ENTRY_CREATE_DIR; -extern int EPHOTO_EVENT_ENTRY_CREATE_THUMB; +extern int EPHOTO_EVENT_ENTRY_CREATE; extern int EPHOTO_EVENT_POPULATE_START; extern int EPHOTO_EVENT_POPULATE_END; extern int EPHOTO_EVENT_POPULATE_ERROR; diff --git a/src/bin/ephoto_config.c b/src/bin/ephoto_config.c index 3eb5db4..602e2f3 100644 --- a/src/bin/ephoto_config.c +++ b/src/bin/ephoto_config.c @@ -1,6 +1,6 @@ #include "ephoto.h" -#define CONFIG_VERSION 6 +#define CONFIG_VERSION 5 static int _ephoto_config_load(Ephoto *ephoto); static Eina_Bool _ephoto_on_config_save(void *data); @@ -13,42 +13,52 @@ ephoto_config_init(Ephoto *ephoto) { Eet_Data_Descriptor_Class eddc; - if (!eet_eina_stream_data_descriptor_class_set - (&eddc, sizeof (eddc), "Ephoto_Config", sizeof(Ephoto_Config))) + if (!eet_eina_stream_data_descriptor_class_set(&eddc, sizeof (eddc), "Ephoto_Config", sizeof(Ephoto_Config))) { ERR("Unable to create the config data descriptor!"); return EINA_FALSE; } if (!edd) edd = eet_data_descriptor_stream_new(&eddc); - EET_DATA_DESCRIPTOR_ADD_BASIC - (edd, Ephoto_Config, "config_version", config_version, EET_T_INT); - EET_DATA_DESCRIPTOR_ADD_BASIC - (edd, Ephoto_Config, "autohide_toolbar", autohide_toolbar, EET_T_INT); - EET_DATA_DESCRIPTOR_ADD_BASIC - (edd, Ephoto_Config, "best_fit_images", best_fit_images, EET_T_INT); - EET_DATA_DESCRIPTOR_ADD_BASIC - (edd, Ephoto_Config, "thumb_gen_size", thumb_gen_size, EET_T_INT); - EET_DATA_DESCRIPTOR_ADD_BASIC - (edd, Ephoto_Config, "thumb_size", thumb_size, EET_T_INT); - EET_DATA_DESCRIPTOR_ADD_BASIC - (edd, Ephoto_Config, "slideshow_timeout", slideshow_timeout, EET_T_INT); - EET_DATA_DESCRIPTOR_ADD_BASIC - (edd, Ephoto_Config, "directory", directory, EET_T_STRING); - EET_DATA_DESCRIPTOR_ADD_BASIC - (edd, Ephoto_Config, "slideshow_transition", slideshow_transition, EET_T_STRING); +#undef T +#undef D +#define T Ephoto_Config +#define D edd +#define C_VAL(edd, type, member, dtype) EET_DATA_DESCRIPTOR_ADD_BASIC(edd, type, #member, member, dtype) + C_VAL(D, T, config_version, EET_T_INT); + C_VAL(D, T, thumb_size, EET_T_INT); + C_VAL(D, T, thumb_gen_size, EET_T_INT); + C_VAL(D, T, directory, EET_T_STRING); + C_VAL(D, T, slideshow_timeout, EET_T_DOUBLE); + C_VAL(D, T, slideshow_transition, EET_T_STRING); + C_VAL(D, T, editor, EET_T_STRING); + switch (_ephoto_config_load(ephoto)) { case 0: /* Start a new config */ ephoto->config->config_version = CONFIG_VERSION; - ephoto->config->autohide_toolbar = 0; - ephoto->config->best_fit_images = 0; - ephoto->config->thumb_gen_size = 256; ephoto->config->thumb_size = 256; ephoto->config->thumb_gen_size = 256; ephoto->config->slideshow_timeout = 4.0; ephoto->config->slideshow_transition = eina_stringshare_add("fade"); + ephoto->config->editor = eina_stringshare_add("gimp %s"); + break; + + case -1: + /* Incremental additions */ + if (ephoto->config->config_version < 2) + { + ephoto->config->slideshow_timeout = 4.0; + ephoto->config->slideshow_transition = eina_stringshare_add("fade"); + } + if (ephoto->config->config_version < 3) + ephoto->config->editor = eina_stringshare_add("gimp %s"); + + if (ephoto->config->config_version < 5) + ephoto->config->thumb_gen_size = 256; + + ephoto->config->config_version = CONFIG_VERSION; break; default: @@ -102,25 +112,16 @@ _ephoto_config_load(Ephoto *ephoto) ephoto->config = eet_data_read(ef, edd, "config"); eet_close(ef); - if (!ephoto->config) - { - DBG("Warning! No configuration found! Writing a new" - "default configuration!\n"); - return 0; - } - else if (ephoto->config->config_version > CONFIG_VERSION) + if (ephoto->config->config_version > CONFIG_VERSION) { ephoto_config_free(ephoto); ephoto->config = calloc(1, sizeof(Ephoto_Config)); return 0; } - else if (ephoto->config->config_version < CONFIG_VERSION) - { - ephoto_config_free(ephoto); - ephoto->config = calloc(1, sizeof(Ephoto_Config)); - return 0; - } + if (ephoto->config->config_version < CONFIG_VERSION) + return -1; + return 1; } diff --git a/src/bin/ephoto_directory_thumb.c b/src/bin/ephoto_directory_thumb.c new file mode 100644 index 0000000..eeda248 --- /dev/null +++ b/src/bin/ephoto_directory_thumb.c @@ -0,0 +1,176 @@ +#include "ephoto.h" + +typedef struct _Ephoto_Directory_Thumb Ephoto_Directory_Thumb; +struct _Ephoto_Directory_Thumb +{ + Eio_File *ls; + Eina_List *objs; + Ephoto_Entry *entry; + Eina_Bool canceled:1; +}; + +static Eina_Hash *_pending_dirs = NULL; + +static void +_entry_free(void *data, const Ephoto_Entry *entry __UNUSED__) +{ + Ephoto_Directory_Thumb *dt = data; + dt->entry = NULL; +} + +static void +_ephoto_directory_thumb_free(Ephoto_Directory_Thumb *dt) +{ + if (dt->entry) + { + ephoto_entry_free_listener_del(dt->entry, _entry_free, dt); + eina_hash_del(_pending_dirs, dt->entry->path, dt); + dt->entry = NULL; + } + + if (dt->ls) + { + dt->canceled = EINA_TRUE; + eio_file_cancel(dt->ls); + return; + } + + free(dt); + + if (_pending_dirs) + { + if (!eina_hash_population(_pending_dirs)) + { + eina_hash_free(_pending_dirs); + _pending_dirs = NULL; + } + } +} + +static void +_obj_del(void *data, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__) +{ + Ephoto_Directory_Thumb *dt = data; + dt->objs = eina_list_remove(dt->objs, obj); + if (!dt->objs) + { + dt->canceled = EINA_TRUE; + _ephoto_directory_thumb_free(dt); + } +} + +static Eina_Bool +_populate_filter(void *data __UNUSED__, Eio_File *handler __UNUSED__, const Eina_File_Direct_Info *info) +{ + return _ephoto_eina_file_direct_info_image_useful(info); +} + +static void +_populate_end(void *data, Eio_File *handler __UNUSED__) +{ + Ephoto_Directory_Thumb *dt = data; + Evas_Object *obj; + dt->ls = NULL; + + EINA_LIST_FREE(dt->objs, obj) + evas_object_event_callback_del_full(obj, EVAS_CALLBACK_DEL, _obj_del, dt); + + if (dt->entry) + { + dt->entry->dir_files_checked = EINA_TRUE; + if ((dt->entry->item) && (!dt->canceled)) + elm_gengrid_item_update(dt->entry->item); + } + + _ephoto_directory_thumb_free(dt); +} + +static void +_populate_error(void *data, Eio_File *handler, int error) +{ + Ephoto_Directory_Thumb *dt = data; + if (error) ERR("could not populate: %s", strerror(error)); + _populate_end(dt, handler); +} + +static void +_populate_main(void *data, Eio_File *handler __UNUSED__, const Eina_File_Direct_Info *info) +{ + Ephoto_Directory_Thumb *dt = data; + Evas_Object *obj; + const char *file; + + if (!dt->objs) return; + if (!dt->entry) return; + + obj = dt->objs->data; + file = eina_stringshare_add(info->path); + + DBG("populate thumbnail %p with path '%s'", obj, file); + + dt->objs = eina_list_remove_list(dt->objs, dt->objs); + dt->entry->dir_files = eina_list_append(dt->entry->dir_files, file); + ephoto_thumb_path_set(obj, file); + + evas_object_event_callback_del_full(obj, EVAS_CALLBACK_DEL, _obj_del, dt); + if ((!dt->objs) && (dt->ls)) + { + eio_file_cancel(dt->ls); + dt->ls = NULL; + } +} + +Evas_Object * +ephoto_directory_thumb_add(Evas_Object *parent, Ephoto_Entry *entry) +{ + Ephoto_Directory_Thumb *dt; + Evas_Object *obj; + + if (_pending_dirs) + dt = eina_hash_find(_pending_dirs, entry->path); + else + { + dt = NULL; + _pending_dirs = eina_hash_stringshared_new(NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(_pending_dirs, NULL); + } + + obj = ephoto_thumb_add(entry->ephoto, parent, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(obj, NULL); + + if (!dt) + { + dt = calloc(1, sizeof(Ephoto_Directory_Thumb)); + if (!dt) + { + ERR("could not allocate memory for Ephoto_Directory_Thumb"); + evas_object_del(obj); + return NULL; + } + ephoto_entry_free_listener_add(entry, _entry_free, dt); + dt->entry = entry; + dt->ls = eio_file_direct_ls(entry->path, + _populate_filter, + _populate_main, + _populate_end, + _populate_error, + dt); + if (!dt->ls) + { + ERR("could not create eio_file_direct_ls(%s)", entry->path); + evas_object_del(obj); + free(dt); + return NULL; + } + + eina_hash_add(_pending_dirs, entry->path, dt); + DBG("start thread to lookup inside '%s' for thumbnails.", entry->path); + } + else + DBG("thread already started, wait for thumbnails in '%s'", entry->path); + + dt->objs = eina_list_append(dt->objs, obj); + + evas_object_event_callback_add(obj, EVAS_CALLBACK_DEL, _obj_del, dt); + return obj; +} diff --git a/src/bin/ephoto_main.c b/src/bin/ephoto_main.c index 2d9c7e5..368c3d5 100644 --- a/src/bin/ephoto_main.c +++ b/src/bin/ephoto_main.c @@ -1,7 +1,6 @@ #include "ephoto.h" -int EPHOTO_EVENT_ENTRY_CREATE_DIR = 0; -int EPHOTO_EVENT_ENTRY_CREATE_THUMB = 0; +int EPHOTO_EVENT_ENTRY_CREATE = 0; int EPHOTO_EVENT_POPULATE_START = 0; int EPHOTO_EVENT_POPULATE_END = 0; int EPHOTO_EVENT_POPULATE_ERROR = 0; @@ -21,18 +20,93 @@ _ephoto_state_set(Ephoto *ephoto, Ephoto_State state) } static void -_back_clicked(void *data, Evas_Object *o __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__) +_ephoto_thumb_browser_show(Ephoto *ephoto, Ephoto_Entry *entry) +{ + DBG("entry '%s'", entry ? entry->path : ""); + + ephoto_single_browser_entry_set(ephoto->single_browser, NULL); + ephoto_slideshow_entry_set(ephoto->slideshow, NULL); + elm_pager_content_promote(ephoto->pager, ephoto->thumb_browser); + _ephoto_state_set(ephoto, EPHOTO_STATE_THUMB); + + if ((entry) && (entry->item)) elm_gengrid_item_bring_in(entry->item); +} + +static void +_ephoto_single_browser_show(Ephoto *ephoto, Ephoto_Entry *entry) +{ + DBG("entry '%s'", entry->path); + ephoto_single_browser_entry_set(ephoto->single_browser, entry); + elm_pager_content_promote(ephoto->pager, ephoto->single_browser); + _ephoto_state_set(ephoto, EPHOTO_STATE_SINGLE); +} + +static void +_ephoto_slideshow_show(Ephoto *ephoto, Ephoto_Entry *entry) +{ + DBG("entry '%s'", entry->path); + ephoto_slideshow_entry_set(ephoto->slideshow, entry); + elm_pager_content_promote(ephoto->pager, ephoto->slideshow); + _ephoto_state_set(ephoto, EPHOTO_STATE_SLIDESHOW); +} + +static void +_ephoto_single_browser_back(void *data, Evas_Object *obj __UNUSED__, void *event_info) { Ephoto *ephoto = data; + Ephoto_Entry *entry = event_info; + _ephoto_thumb_browser_show(ephoto, entry); +} - switch (ephoto->state) +static void +_ephoto_slideshow_back(void *data, Evas_Object *obj __UNUSED__, void *event_info) +{ + Ephoto *ephoto = data; + Ephoto_Entry *entry = event_info; + switch (ephoto->prev_state) { - case EPHOTO_STATE_SINGLE : ephoto_promote_thumb_browser(ephoto); break; - case EPHOTO_STATE_THUMB : ephoto_promote_list_browser(ephoto); break; - case EPHOTO_STATE_LIST : break; - default : break; + case EPHOTO_STATE_SINGLE: + _ephoto_single_browser_show(ephoto, entry); + break; + case EPHOTO_STATE_THUMB: + _ephoto_thumb_browser_show(ephoto, entry); + break; + default: + ERR("unhandled previous state %d", ephoto->prev_state); } - ephoto_title_set(ephoto, ephoto->config->directory); + elm_win_fullscreen_set(ephoto->win, EINA_FALSE); +} + +static void +_ephoto_thumb_browser_view(void *data, Evas_Object *obj __UNUSED__, void *event_info) +{ + Ephoto *ephoto = data; + Ephoto_Entry *entry = event_info; + _ephoto_single_browser_show(ephoto, entry); +} + +static void +_ephoto_thumb_browser_changed_directory(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) +{ + Ephoto *ephoto = data; + ephoto_single_browser_entry_set(ephoto->single_browser, NULL); + ephoto_slideshow_entry_set(ephoto->slideshow, NULL); +} + +static void +_ephoto_thumb_browser_slideshow(void *data, Evas_Object *obj __UNUSED__, void *event_info) +{ + Ephoto *ephoto = data; + Ephoto_Entry *entry = event_info; + _ephoto_slideshow_show(ephoto, entry); +} + +static void +_ephoto_single_browser_slideshow(void *data, Evas_Object *obj __UNUSED__, void *event_info) +{ + Ephoto *ephoto = data; + Ephoto_Entry *entry = event_info; + _ephoto_slideshow_show(ephoto, entry); } static void @@ -48,13 +122,10 @@ ephoto_window_add(const char *path) { Ephoto *ephoto = calloc(1, sizeof(Ephoto)); Ethumb_Client *client = elm_thumb_ethumb_client_get(); - Evas_Object *o; char buf[PATH_MAX]; - int w, h; EINA_SAFETY_ON_NULL_RETURN_VAL(ephoto, NULL); - EPHOTO_EVENT_ENTRY_CREATE_DIR = ecore_event_type_new(); - EPHOTO_EVENT_ENTRY_CREATE_THUMB = ecore_event_type_new(); + EPHOTO_EVENT_ENTRY_CREATE = ecore_event_type_new(); EPHOTO_EVENT_POPULATE_START = ecore_event_type_new(); EPHOTO_EVENT_POPULATE_END = ecore_event_type_new(); EPHOTO_EVENT_POPULATE_ERROR = ecore_event_type_new(); @@ -76,6 +147,7 @@ ephoto_window_add(const char *path) evas_object_del(ephoto->win); return NULL; } + if ((ephoto->config->thumb_gen_size != 128) && (ephoto->config->thumb_gen_size != 256) && (ephoto->config->thumb_gen_size != 512)) @@ -91,72 +163,14 @@ ephoto_window_add(const char *path) elm_win_resize_object_add(ephoto->win, ephoto->bg); evas_object_show(ephoto->bg); - ephoto->overlay = elm_bg_add(ephoto->bg); - evas_object_size_hint_weight_set - (ephoto->overlay, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); - evas_object_size_hint_fill_set(ephoto->overlay, EVAS_HINT_FILL, EVAS_HINT_FILL); - elm_bg_color_set(ephoto->overlay, 0, 0, 0); - - elm_win_resize_object_add(ephoto->win, ephoto->bg); - evas_object_show(ephoto->bg); - - ephoto->layout = elm_layout_add(ephoto->win); - if (!ephoto->layout) - { - evas_object_del(ephoto->win); - return NULL; - } - if (!elm_layout_theme_set - (ephoto->layout, "layout", "application", "content-back")) - { - ERR("Content-back"); - evas_object_del(ephoto->win); - return NULL; - } - evas_object_size_hint_weight_set - (ephoto->layout, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); - evas_object_size_hint_fill_set(ephoto->layout, EVAS_HINT_FILL, EVAS_HINT_FILL); - elm_win_resize_object_add(ephoto->win, ephoto->layout); - ephoto->edje = elm_layout_edje_get(ephoto->layout); - edje_object_signal_callback_add(ephoto->edje, "elm,action,back", "", - _back_clicked, ephoto); - evas_object_show(ephoto->layout); - - o = edje_object_part_external_object_get(ephoto->edje, "back"); - evas_object_geometry_get(o, 0, 0, &w, &h); - ephoto->pager = elm_pager_add(ephoto->win); elm_object_style_set(ephoto->pager, "fade_invisible"); evas_object_size_hint_weight_set (ephoto->pager, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); evas_object_size_hint_fill_set (ephoto->pager, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_win_resize_object_add(ephoto->win, ephoto->pager); evas_object_show(ephoto->pager); - elm_layout_content_set(ephoto->layout, "elm.swallow.content", ephoto->pager); - - ephoto->help_but = elm_button_add(ephoto->win); - o = elm_icon_add(ephoto->help_but); - elm_icon_standard_set(o, "help-browser"); - evas_object_size_hint_aspect_set(o, EVAS_ASPECT_CONTROL_BOTH, 1, 1); - elm_button_icon_set(ephoto->help_but, o); - evas_object_size_hint_weight_set - (ephoto->help_but, 0.0, 0.0); - evas_object_size_hint_fill_set - (ephoto->help_but, 0.0, 0.0); - evas_object_size_hint_min_set(ephoto->help_but, w, h); - evas_object_size_hint_max_set(ephoto->help_but, w, h); - evas_object_show(ephoto->help_but); - elm_layout_content_set(ephoto->layout, "elm.swallow.end", ephoto->help_but); - - - ephoto->list_browser = ephoto_list_browser_add(ephoto, ephoto->pager); - if (!ephoto->list_browser) - { - ERR("could not add list browser"); - evas_object_del(ephoto->win); - return NULL; - } - elm_pager_content_push(ephoto->pager, ephoto->list_browser); ephoto->thumb_browser = ephoto_thumb_browser_add(ephoto, ephoto->pager); if (!ephoto->thumb_browser) @@ -166,6 +180,14 @@ ephoto_window_add(const char *path) return NULL; } elm_pager_content_push(ephoto->pager, ephoto->thumb_browser); + evas_object_smart_callback_add + (ephoto->thumb_browser, "view", _ephoto_thumb_browser_view, ephoto); + evas_object_smart_callback_add + (ephoto->thumb_browser, "changed,directory", + _ephoto_thumb_browser_changed_directory, ephoto); + evas_object_smart_callback_add + (ephoto->thumb_browser, "slideshow", + _ephoto_thumb_browser_slideshow, ephoto); ephoto->single_browser = ephoto_single_browser_add(ephoto, ephoto->pager); if (!ephoto->single_browser) @@ -175,12 +197,26 @@ ephoto_window_add(const char *path) return NULL; } elm_pager_content_push(ephoto->pager, ephoto->single_browser); + evas_object_smart_callback_add + (ephoto->single_browser, "back", _ephoto_single_browser_back, ephoto); + evas_object_smart_callback_add + (ephoto->single_browser, "slideshow", + _ephoto_single_browser_slideshow, ephoto); + + ephoto->slideshow = ephoto_slideshow_add(ephoto, ephoto->pager); + if (!ephoto->slideshow) + { + ERR("could not add single browser"); + evas_object_del(ephoto->win); + return NULL; + } + elm_pager_content_push(ephoto->pager, ephoto->slideshow); + evas_object_smart_callback_add + (ephoto->slideshow, "back", _ephoto_slideshow_back, ephoto); if ((!path) || (!ecore_file_exists(path))) { - char p[PATH_MAX]; - snprintf(p, PATH_MAX, "%s/.e/e/fileman/favorites", getenv("HOME")); - path = p; + path = ephoto->config->directory; if ((path) && (!ecore_file_exists(path))) path = NULL; if (!path) { @@ -193,20 +229,22 @@ ephoto_window_add(const char *path) if (ecore_file_is_dir(path)) { - ephoto_promote_list_browser(ephoto); ephoto_directory_set(ephoto, path); + _ephoto_thumb_browser_show(ephoto, NULL); } else { - ephoto_single_browser_path_pending_set(ephoto->single_browser, path); - ephoto_promote_single_browser(ephoto, NULL); char *dir = ecore_file_dir_get(path); ephoto_directory_set(ephoto, dir); free(dir); + ephoto_single_browser_path_pending_set(ephoto->single_browser, path); + + elm_pager_content_promote(ephoto->pager, ephoto->single_browser); + ephoto->state = EPHOTO_STATE_single; } /* TODO restore size from last run as well? */ - evas_object_resize(ephoto->win, 800, 600); + evas_object_resize(ephoto->win, 900, 600); evas_object_show(ephoto->win); return ephoto->win; @@ -217,56 +255,20 @@ ephoto_title_set(Ephoto *ephoto, const char *title) { char buf[1024] = "Ephoto"; - if (title) snprintf(buf, sizeof(buf), "Ephoto - %s", title); + if (title) snprintf(buf, sizeof(buf), "%s - Ephoto", title); elm_win_title_set(ephoto->win, buf); - edje_object_part_text_set(ephoto->edje, "elm.text.title", title); -} - -void -ephoto_promote_list_browser(Ephoto *ephoto) -{ - elm_pager_content_promote(ephoto->pager, ephoto->list_browser); - edje_object_signal_emit(ephoto->edje, "elm,back,hide", "elm"); - _ephoto_state_set(ephoto, EPHOTO_STATE_LIST); -} - -void -ephoto_promote_thumb_browser(Ephoto *ephoto) -{ - if (elm_bg_overlay_get(ephoto->bg)) - { - evas_object_hide(ephoto->overlay); - elm_bg_overlay_unset(ephoto->bg); - } - elm_pager_content_promote(ephoto->pager, ephoto->thumb_browser); - edje_object_signal_emit(ephoto->edje, "elm,back,show", "elm"); - _ephoto_state_set(ephoto, EPHOTO_STATE_THUMB); -} - -void -ephoto_promote_single_browser(Ephoto *ephoto, Ephoto_Entry *e) -{ - elm_pager_content_promote(ephoto->pager, ephoto->single_browser); - elm_bg_overlay_set(ephoto->bg, ephoto->overlay); - if (e) - ephoto_single_browser_entry_set(ephoto->single_browser, e); - _ephoto_state_set(ephoto, EPHOTO_STATE_SINGLE); } static int _entry_cmp(const void *pa, const void *pb) { const Ephoto_Entry *a = pa, *b = pb; - int ret, s; - - s = strcmp(a->basename, b->basename); - if (s > 0) - ret = 1; - else if (s < 0) - ret = -1; + if (a->is_dir == b->is_dir) + return strcoll(a->basename, b->basename); + else if (a->is_dir) + return -1; else - ret = 0; - return ret; + return 1; } static void @@ -277,19 +279,30 @@ _ephoto_populate_main(void *data, Eio_File *handler __UNUSED__, const Eina_File_ Ephoto_Event_Entry_Create *ev; e = ephoto_entry_new(ephoto, info->path, info->path + info->name_start); - + if (info->type == EINA_FILE_DIR) e->is_dir = EINA_TRUE; + else if (info->type == EINA_FILE_REG) e->is_dir = EINA_FALSE; + else e->is_dir = !_ephoto_eina_file_direct_info_image_useful(info); + + if (!ephoto->entries) + ephoto->entries = eina_list_append(ephoto->entries, e); + else + { + int near_cmp; + Eina_List *near_node = eina_list_search_sorted_near_list + (ephoto->entries, _entry_cmp, e, &near_cmp); + + if (near_cmp < 0) + ephoto->entries = eina_list_append_relative_list + (ephoto->entries, e, near_node); + else + ephoto->entries = eina_list_prepend_relative_list + (ephoto->entries, e, near_node); + } + ev = calloc(1, sizeof(Ephoto_Event_Entry_Create)); ev->entry = e; - if (ecore_file_is_dir(info->path)) - ecore_event_add(EPHOTO_EVENT_ENTRY_CREATE_DIR, ev, NULL, NULL); - else - { - ephoto->entries = eina_list_sorted_insert(ephoto->entries, _entry_cmp, e); - ecore_event_add(EPHOTO_EVENT_ENTRY_CREATE_THUMB, ev, NULL, NULL); - if (ephoto->state == EPHOTO_STATE_LIST) - ephoto_promote_thumb_browser(ephoto); - } + ecore_event_add(EPHOTO_EVENT_ENTRY_CREATE, ev, NULL, NULL); } static Eina_Bool @@ -298,11 +311,9 @@ _ephoto_populate_filter(void *data __UNUSED__, Eio_File *handler __UNUSED__, con const char *bname = info->path + info->name_start; if (bname[0] == '.') return EINA_FALSE; + if (info->type == EINA_FILE_DIR) return EINA_TRUE; - if (ecore_file_is_dir(info->path)) - return EINA_TRUE; - else - return _ephoto_eina_file_direct_info_image_useful(info); + return _ephoto_eina_file_direct_info_image_useful(info); } static void @@ -356,7 +367,7 @@ ephoto_directory_set(Ephoto *ephoto, const char *path) { EINA_SAFETY_ON_NULL_RETURN(ephoto); - ephoto_title_set(ephoto, basename(path)); + ephoto_title_set(ephoto, path); eina_stringshare_replace(&ephoto->config->directory, path); if (ephoto->job.change_dir) ecore_job_del(ephoto->job.change_dir); ephoto->job.change_dir = ecore_job_add(_ephoto_change_dir, ephoto); @@ -461,23 +472,6 @@ ephoto_thumb_path_set(Evas_Object *o, const char *path) elm_thumb_file_set(o, path, NULL); } -Evas_Object * -ephoto_list_icon_add(Ephoto *ephoto, Evas_Object *parent, const char *standard) -{ - Evas_Object *o; - - EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL); - - o = elm_icon_add(parent); - if (!o) return NULL; - - if (standard) elm_icon_standard_set(o, standard); - evas_object_size_hint_aspect_set(o, EVAS_ASPECT_CONTROL_VERTICAL, 1, 1); - ephoto->dirs = eina_list_append(ephoto->dirs, o); - - return o; -} - Ephoto_Entry * ephoto_entry_new(Ephoto *ephoto, const char *path, const char *label) { @@ -495,6 +489,7 @@ ephoto_entry_new(Ephoto *ephoto, const char *path, const char *label) void ephoto_entry_free(Ephoto_Entry *entry) { + const char *s; Ephoto_Entry_Free_Listener *fl; EINA_LIST_FREE(entry->free_listeners, fl) @@ -505,6 +500,7 @@ ephoto_entry_free(Ephoto_Entry *entry) EINA_SAFETY_ON_NULL_RETURN(entry); eina_stringshare_del(entry->path); eina_stringshare_del(entry->label); + EINA_LIST_FREE(entry->dir_files, s) eina_stringshare_del(s); free(entry); } @@ -545,4 +541,3 @@ ephoto_entries_free(Ephoto *ephoto) Ephoto_Entry *entry; EINA_LIST_FREE(ephoto->entries, entry) ephoto_entry_free(entry); } - diff --git a/src/bin/ephoto_single_browser.c b/src/bin/ephoto_single_browser.c index 8e64c9b..d6af5b3 100644 --- a/src/bin/ephoto_single_browser.c +++ b/src/bin/ephoto_single_browser.c @@ -8,53 +8,52 @@ * elm_photocam and elm_image rotates their images internally. * Rotating the scroller is not correct and was rejected by Raster and others. */ +//#define ROTATION -#define ROTATION 0 #define ZOOM_STEP 0.2 -typedef struct _Ephoto_Single_Browser Ephoto_Single_Browser; +typedef struct _Ephoto_single_Browser Ephoto_Single_Browser; typedef struct _Ephoto_Viewer Ephoto_Viewer; struct _Ephoto_Single_Browser { Ephoto *ephoto; - Evas_Object *layout; + Evas_Object *main; + Evas_Object *bar; + Evas_Object *label; Evas_Object *viewer; const char *pending_path; Ephoto_Entry *entry; Ephoto_Orient orient; Eina_List *handlers; - Eina_List *entries; - Eina_List *current_index; }; struct _Ephoto_Viewer { - Evas_Object *scroller; - Evas_Object *box; - Evas_Object *image; Evas_Object *photocam; + Evas_Object *scroller; + Evas_Object *image; double zoom; Eina_Bool fit:1; }; +static void _zoom_set(Ephoto_Single_Browser *sb, double zoom); +static void _zoom_in(Ephoto_Single_Browser *sb); +static void _zoom_out(Ephoto_Single_Browser *sb); + static Eina_Bool _path_is_jpeg(const char *path_stringshared) { size_t len = eina_stringshare_strlen(path_stringshared); const char *ext; - if (len < sizeof(".jpg")) - return EINA_FALSE; + if (len < sizeof(".jpg")) return EINA_FALSE; ext = path_stringshared + len - (sizeof(".jpg") - 1); - if (strcasecmp(ext, ".jpg") == 0) - return EINA_TRUE; + if (strcasecmp(ext, ".jpg") == 0) return EINA_TRUE; - if (len < sizeof(".jpeg")) - return EINA_FALSE; + if (len < sizeof(".jpeg")) return EINA_FALSE; ext = path_stringshared + len - (sizeof(".jpeg") - 1); - if (strcasecmp(ext, ".jpeg") == 0) - return EINA_TRUE; + if (strcasecmp(ext, ".jpeg") == 0) return EINA_TRUE; return EINA_FALSE; } @@ -69,7 +68,6 @@ static void _viewer_del(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) { Ephoto_Viewer *v = data; - free(v); } @@ -86,8 +84,7 @@ _viewer_add(Evas_Object *parent, const char *path) obj = v->photocam = elm_photocam_add(parent); EINA_SAFETY_ON_NULL_GOTO(obj, error); err = elm_photocam_file_set(obj, path); - if (err != EVAS_LOAD_ERROR_NONE) - goto load_error; + if (err != EVAS_LOAD_ERROR_NONE) goto load_error; elm_photocam_paused_set(obj, EINA_TRUE); evas_object_smart_callback_add (obj, "loaded", _viewer_photocam_loaded, v); @@ -97,25 +94,14 @@ _viewer_add(Evas_Object *parent, const char *path) Evas_Coord w, h; obj = v->scroller = elm_scroller_add(parent); EINA_SAFETY_ON_NULL_GOTO(obj, error); - - v->box = elm_box_add(obj); - evas_object_size_hint_weight_set(v->box, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); - evas_object_size_hint_align_set(v->box, EVAS_HINT_FILL, EVAS_HINT_FILL); - evas_object_show(v->box); - v->image = evas_object_image_filled_add(evas_object_evas_get(parent)); evas_object_image_file_set(v->image, path, NULL); err = evas_object_image_load_error_get(v->image); if (err != EVAS_LOAD_ERROR_NONE) goto load_error; evas_object_image_size_get(v->image, &w, &h); evas_object_size_hint_align_set(v->image, 0.5, 0.5); - evas_object_size_hint_weight_set(v->image, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); evas_object_size_hint_min_set(v->image, w, h); evas_object_size_hint_max_set(v->image, w, h); - evas_object_resize(v->image, w, h); - evas_object_show(v->image); - elm_box_pack_end(v->box, v->image); - elm_scroller_content_set(obj, v->box); } evas_object_size_hint_weight_set(obj, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); evas_object_size_hint_align_set(obj, EVAS_HINT_FILL, EVAS_HINT_FILL); @@ -135,20 +121,15 @@ static void _viewer_zoom_apply(Ephoto_Viewer *v, double zoom) { v->zoom = zoom; - if (v->photocam) - elm_photocam_zoom_set(v->photocam, 1.0 / zoom); + if (v->photocam) elm_photocam_zoom_set(v->photocam, 1.0 / zoom); else { Evas_Coord w, h; evas_object_image_size_get(v->image, &w, &h); w *= zoom; - h *= zoom; + h *= zoom; evas_object_size_hint_min_set(v->image, w, h); evas_object_size_hint_max_set(v->image, w, h); - elm_box_unpack(v->box, v->image); - elm_scroller_content_unset(v->scroller); - elm_box_pack_end(v->box, v->image); - elm_scroller_content_set(v->scroller, v->box); } } @@ -218,10 +199,10 @@ _viewer_zoom_fit(Evas_Object *obj) { Ephoto_Viewer *v = evas_object_data_get(obj, "viewer"); EINA_SAFETY_ON_NULL_RETURN(v); - if (v->fit) return; v->fit = EINA_TRUE; + if (v->photocam) evas_object_event_callback_add (v->photocam, EVAS_CALLBACK_RESIZE, _viewer_resized, v); @@ -232,18 +213,288 @@ _viewer_zoom_fit(Evas_Object *obj) _viewer_zoom_fit_apply(v); } +static void +_orient_apply(Ephoto_Single_Browser *sb) +{ +#ifdef ROTATION + const char *sig; + switch (sb->orient) + { + case EPHOTO_ORIENT_0: + sig = "state,rotate,0"; + break; + case EPHOTO_ORIENT_90: + sig = "state,rotate,90"; + break; + case EPHOTO_ORIENT_180: + sig = "state,rotate,180"; + break; + case EPHOTO_ORIENT_270: + sig = "state,rotate,270"; + break; + case EPHOTO_ORIENT_FLIP_HORIZ: + sig = "state,flip,horiz"; + break; + case EPHOTO_ORIENT_FLIP_VERT: + sig = "state,flip,vert"; + break; + case EPHOTO_ORIENT_FLIP_HORIZ_90: + sig = "state,flip,horiz,90"; + break; + case EPHOTO_ORIENT_FLIP_VERT_90: + sig = "state,flip,vert,90"; + break; + default: + return; + } + DBG("orient: %d, signal '%s'", sb->orient, sig); + edje_object_signal_emit(sb->orient_edje, sig, "ephoto"); +#else + (void)sb; +#endif +} + +#ifdef ROTATION +static void +_rotate_counterclock(Ephoto_Single_Browser *sb) +{ + switch (sb->orient) + { + case EPHOTO_ORIENT_0: + sb->orient = EPHOTO_ORIENT_270; + break; + case EPHOTO_ORIENT_90: + sb->orient = EPHOTO_ORIENT_0; + break; + case EPHOTO_ORIENT_180: + sb->orient = EPHOTO_ORIENT_90; + break; + case EPHOTO_ORIENT_270: + sb->orient = EPHOTO_ORIENT_180; + break; + case EPHOTO_ORIENT_FLIP_HORIZ: + sb->orient = EPHOTO_ORIENT_FLIP_HORIZ_90; + break; + case EPHOTO_ORIENT_FLIP_VERT: + sb->orient = EPHOTO_ORIENT_FLIP_VERT_90; + break; + case EPHOTO_ORIENT_FLIP_HORIZ_90: + sb->orient = EPHOTO_ORIENT_FLIP_VERT; + break; + case EPHOTO_ORIENT_FLIP_VERT_90: + sb->orient = EPHOTO_ORIENT_FLIP_HORIZ; + break; + } + _orient_apply(sb); +} + +static void +_rotate_clock(Ephoto_Single_Browser *sb) +{ + switch (sb->orient) + { + case EPHOTO_ORIENT_0: + sb->orient = EPHOTO_ORIENT_90; + break; + case EPHOTO_ORIENT_90: + sb->orient = EPHOTO_ORIENT_180; + break; + case EPHOTO_ORIENT_180: + sb->orient = EPHOTO_ORIENT_270; + break; + case EPHOTO_ORIENT_270: + sb->orient = EPHOTO_ORIENT_0; + break; + case EPHOTO_ORIENT_FLIP_HORIZ: + sb->orient = EPHOTO_ORIENT_FLIP_VERT_90; + break; + case EPHOTO_ORIENT_FLIP_VERT: + sb->orient = EPHOTO_ORIENT_FLIP_HORIZ_90; + break; + case EPHOTO_ORIENT_FLIP_HORIZ_90: + sb->orient = EPHOTO_ORIENT_FLIP_HORIZ; + break; + case EPHOTO_ORIENT_FLIP_VERT_90: + sb->orient = EPHOTO_ORIENT_FLIP_VERT; + break; + } + _orient_apply(sb); +} + +static void +_flip_horiz(Ephoto_Single_Browser *sb) +{ + switch (sb->orient) + { + case EPHOTO_ORIENT_0: + sb->orient = EPHOTO_ORIENT_FLIP_HORIZ; + break; + case EPHOTO_ORIENT_90: + sb->orient = EPHOTO_ORIENT_FLIP_HORIZ_90; + break; + case EPHOTO_ORIENT_180: + sb->orient = EPHOTO_ORIENT_FLIP_VERT; + break; + case EPHOTO_ORIENT_270: + sb->orient = EPHOTO_ORIENT_FLIP_VERT_90; + break; + case EPHOTO_ORIENT_FLIP_HORIZ: + sb->orient = EPHOTO_ORIENT_0; + break; + case EPHOTO_ORIENT_FLIP_VERT: + sb->orient = EPHOTO_ORIENT_180; + break; + case EPHOTO_ORIENT_FLIP_HORIZ_90: + sb->orient = EPHOTO_ORIENT_90; + break; + case EPHOTO_ORIENT_FLIP_VERT_90: + sb->orient = EPHOTO_ORIENT_270; + break; + } + _orient_apply(sb); +} + +static void +_flip_vert(Ephoto_Single_Browser *sb) +{ + switch (sb->orient) + { + case EPHOTO_ORIENT_0: + sb->orient = EPHOTO_ORIENT_FLIP_VERT; + break; + case EPHOTO_ORIENT_90: + sb->orient = EPHOTO_ORIENT_FLIP_VERT_90; + break; + case EPHOTO_ORIENT_180: + sb->orient = EPHOTO_ORIENT_FLIP_HORIZ; + break; + case EPHOTO_ORIENT_270: + sb->orient = EPHOTO_ORIENT_FLIP_HORIZ_90; + break; + case EPHOTO_ORIENT_FLIP_HORIZ: + sb->orient = EPHOTO_ORIENT_180; + break; + case EPHOTO_ORIENT_FLIP_VERT: + sb->orient = EPHOTO_ORIENT_0; + break; + case EPHOTO_ORIENT_FLIP_HORIZ_90: + sb->orient = EPHOTO_ORIENT_270; + break; + case EPHOTO_ORIENT_FLIP_VERT_90: + sb->orient = EPHOTO_ORIENT_90; + break; + } + _orient_apply(sb); +} +#endif + +static void +_mouse_wheel(void *data, Evas *e __UNUSED__, Evas_Object *o __UNUSED__, void *event_info) +{ + Ephoto_Single_Browser *sb = data; + Evas_Event_Mouse_Wheel *ev = event_info; + if (!evas_key_modifier_is_set(ev->modifiers, "Control")) return; + + if (ev->z > 0) _zoom_in(sb); + else _zoom_out(sb); +} + +static Ephoto_Entry * +_first_entry_find(Ephoto_Single_Browser *sb) +{ + const Eina_List *l; + Ephoto_Entry *entry; + EINA_SAFETY_ON_NULL_RETURN_VAL(sb->ephoto, NULL); + + EINA_LIST_FOREACH(sb->ephoto->entries, l, entry) + if (!entry->is_dir) return entry; + return NULL; +} + +static Ephoto_Entry * +_last_entry_find(Ephoto_Single_Browser *sb) +{ + const Eina_List *l; + Ephoto_Entry *entry; + EINA_SAFETY_ON_NULL_RETURN_VAL(sb->ephoto, NULL); + + EINA_LIST_REVERSE_FOREACH(sb->ephoto->entries, l, entry) + if (!entry->is_dir) return entry; + return NULL; +} + +Ephoto_Orient +ephoto_file_orient_get(const char *path) +{ +#ifndef HAVE_LIBEXIF + return EPHOTO_ORIENT_0; +#else + Ephoto_Orient orient = EPHOTO_ORIENT_0; + ExifData *exif; + ExifEntry *entry; + ExifByteOrder bo; + + if (!_path_is_jpeg(path)) return orient; + + exif = exif_data_new_from_file(path); + if (!exif) goto end; + bo = exif_data_get_byte_order(exif); + entry = exif_data_get_entry(exif, EXIF_TAG_ORIENTATION); + if (!entry) goto end_entry; + + orient = exif_get_short(entry->data, bo); + DBG("orient=%d", orient); + if ((orient < 1) || (orient > 8)) + { + ERR("exif orient not supported: %d", orient); + orient = EPHOTO_ORIENT_0; + } + + end_entry: + exif_data_free(exif); + end: + return orient; +#endif +} + +static void +_ephoto_single_browser_recalc(Ephoto_Single_Browser *sb) +{ + if (sb->viewer) + { + evas_object_del(sb->viewer); + sb->viewer = NULL; + } + + if (sb->entry) + { + const char *bname = ecore_file_file_get(sb->entry->path); + sb->viewer = _viewer_add(sb->main, sb->entry->path); + elm_box_pack_end(sb->main, sb->viewer); + evas_object_show(sb->viewer); + evas_object_event_callback_add + (sb->viewer, EVAS_CALLBACK_MOUSE_WHEEL, _mouse_wheel, sb); + elm_label_label_set(sb->label, bname); + ephoto_title_set(sb->ephoto, bname); + sb->orient = ephoto_file_orient_get(sb->entry->path); + _orient_apply(sb); + } + + elm_object_focus(sb->main); +} + static void _zoom_set(Ephoto_Single_Browser *sb, double zoom) { + DBG("zoom %f", zoom); if (zoom <= 0.0) return; - _viewer_zoom_set(sb->viewer, zoom); + _viewer_zoom_set(sb->viewer, zoom); } static void _zoom_fit(Ephoto_Single_Browser *sb) { - if (sb->viewer) - _viewer_zoom_fit(sb->viewer); + if (sb->viewer) _viewer_zoom_fit(sb->viewer); } static void @@ -260,6 +511,34 @@ _zoom_out(Ephoto_Single_Browser *sb) _viewer_zoom_set(sb->viewer, _viewer_zoom_get(sb->viewer) * change); } +static void +_zoom_in_cb(void *data, Evas_Object *o __UNUSED__, void *event_info __UNUSED__) +{ + Ephoto_Single_Browser *sb = data; + _zoom_in(sb); +} + +static void +_zoom_out_cb(void *data, Evas_Object *o __UNUSED__, void *event_info __UNUSED__) +{ + Ephoto_Single_Browser *sb = data; + _zoom_out(sb); +} + +static void +_zoom_1_cb(void *data, Evas_Object *o __UNUSED__, void *event_info __UNUSED__) +{ + Ephoto_Single_Browser *sb = data; + _zoom_set(sb, 1.0); +} + +static void +_zoom_fit_cb(void *data, Evas_Object *o __UNUSED__, void *event_info __UNUSED__) +{ + Ephoto_Single_Browser *sb = data; + _zoom_fit(sb); +} + static int _entry_cmp(const void *pa, const void *pb) { @@ -272,20 +551,6 @@ _entry_cmp(const void *pa, const void *pb) return strcoll(a->path, path); } -static Ephoto_Entry * -_first_entry_find(Ephoto_Single_Browser *sb) -{ - EINA_SAFETY_ON_NULL_RETURN_VAL(sb->ephoto, NULL); - return eina_list_nth(sb->ephoto->entries, 0); -} - -static Ephoto_Entry * -_last_entry_find(Ephoto_Single_Browser *sb) -{ - EINA_SAFETY_ON_NULL_RETURN_VAL(sb->ephoto, NULL); - return eina_list_data_get(eina_list_last(sb->ephoto->entries)); -} - static void _next_entry(Ephoto_Single_Browser *sb) { @@ -294,90 +559,135 @@ _next_entry(Ephoto_Single_Browser *sb) EINA_SAFETY_ON_NULL_RETURN(sb->entry); node = eina_list_search_sorted_list(sb->ephoto->entries, _entry_cmp, sb->entry->path); - if (!node) - return; - if ((node = node->next)) + if (!node) return; + while ((node = node->next)) { entry = node->data; + if (!entry->is_dir) + break; } if (!entry) entry = _first_entry_find(sb); if (entry) - ephoto_single_browser_entry_set(sb->layout, entry); + { + DBG("next is '%s'", entry->path); + ephoto_single_browser_entry_set(sb->main, entry); + } } static void _prev_entry(Ephoto_Single_Browser *sb) { - Ephoto_Entry *entry = NULL; Eina_List *node; + Ephoto_Entry *entry = NULL; EINA_SAFETY_ON_NULL_RETURN(sb->entry); node = eina_list_search_sorted_list(sb->ephoto->entries, _entry_cmp, sb->entry->path); - if (!node) - return; - if ((node = node->prev)) + if (!node) return; + while ((node = node->prev)) { entry = node->data; + if (!entry->is_dir) + break; } if (!entry) entry = _last_entry_find(sb); if (entry) - ephoto_single_browser_entry_set(sb->layout, entry); + { + DBG("prev is '%s'", entry->path); + ephoto_single_browser_entry_set(sb->main, entry); + } } static void _first_entry(Ephoto_Single_Browser *sb) { Ephoto_Entry *entry = _first_entry_find(sb); - - if (!entry) - return; - ephoto_single_browser_entry_set(sb->layout, entry); + if (!entry) return; + DBG("first is '%s'", entry->path); + ephoto_single_browser_entry_set(sb->main, entry); } static void _last_entry(Ephoto_Single_Browser *sb) { Ephoto_Entry *entry = _last_entry_find(sb); - - if (!entry) - return; - ephoto_single_browser_entry_set(sb->layout, entry); + if (!entry) return; + DBG("last is '%s'", entry->path); + ephoto_single_browser_entry_set(sb->main, entry); } static void -_viewer_scrolled(void *data, Evas_Object *o __UNUSED__, void *event_info __UNUSED__) +_go_first(void *data, Evas_Object *o __UNUSED__, void *event_info __UNUSED__) { Ephoto_Single_Browser *sb = data; - Ephoto_Viewer *v = evas_object_data_get(sb->viewer, "viewer"); - int sx, sw, cx; - if (!v->fit) - return; - evas_object_geometry_get(sb->viewer, &sx, 0, &sw, 0); - if (v->photocam) - { - Evas_Object *i = elm_photocam_internal_image_get(v->photocam); - evas_object_geometry_get(i, &cx, 0, 0, 0); - } - else - evas_object_geometry_get(v->image, &cx, 0, 0, 0); - if (cx < (sx-(sw/2))) - _next_entry(sb); - else if (cx > (sw/2)) - _prev_entry(sb); + _first_entry(sb); } static void -_mouse_wheel(void *data, Evas *e __UNUSED__, Evas_Object *o __UNUSED__, void *event_info) +_go_prev(void *data, Evas_Object *o __UNUSED__, void *event_info __UNUSED__) { Ephoto_Single_Browser *sb = data; - Ephoto_Viewer *v = evas_object_data_get(sb->viewer, "viewer"); - Evas_Event_Mouse_Wheel *ev = event_info; - if (!evas_key_modifier_is_set(ev->modifiers, "Control")) return; + _prev_entry(sb); +} - if (ev->z > 0) _zoom_set(sb, v->zoom + ZOOM_STEP); - else _zoom_set(sb, v->zoom - ZOOM_STEP); +static void +_go_next(void *data, Evas_Object *o __UNUSED__, void *event_info __UNUSED__) +{ + Ephoto_Single_Browser *sb = data; + _next_entry(sb); +} + +static void +_go_last(void *data, Evas_Object *o __UNUSED__, void *event_info __UNUSED__) +{ + Ephoto_Single_Browser *sb = data; + _last_entry(sb); +} + +#ifdef ROTATION +static void +_go_rotate_counterclock(void *data, Evas_Object *o __UNUSED__, void *event_info __UNUSED__) +{ + Ephoto_Single_Browser *sb = data; + _rotate_counterclock(sb); +} + +static void +_go_rotate_clock(void *data, Evas_Object *o __UNUSED__, void *event_info __UNUSED__) +{ + Ephoto_Single_Browser *sb = data; + _rotate_clock(sb); +} + +static void +_go_flip_horiz(void *data, Evas_Object *o __UNUSED__, void *event_info __UNUSED__) +{ + Ephoto_Single_Browser *sb = data; + _flip_horiz(sb); +} + +static void +_go_flip_vert(void *data, Evas_Object *o __UNUSED__, void *event_info __UNUSED__) +{ + Ephoto_Single_Browser *sb = data; + _flip_vert(sb); +} +#endif + +static void +_slideshow(void *data, Evas_Object *o __UNUSED__, void *event_info __UNUSED__) +{ + Ephoto_Single_Browser *sb = data; + if (sb->entry) + evas_object_smart_callback_call(sb->main, "slideshow", sb->entry); +} + +static void +_back(void *data, Evas_Object *o __UNUSED__, void *event_info __UNUSED__) +{ + Ephoto_Single_Browser *sb = data; + evas_object_smart_callback_call(sb->main, "back", sb->entry); } static void @@ -389,6 +699,7 @@ _key_down(void *data, Evas *e __UNUSED__, Evas_Object *o __UNUSED__, void *event Eina_Bool shift = evas_key_modifier_is_set(ev->modifiers, "Shift"); const char *k = ev->keyname; + DBG("key pressed '%s'", k); if (ctrl) { if ((!strcmp(k, "plus")) || (!strcmp(k, "equal"))) @@ -403,7 +714,10 @@ _key_down(void *data, Evas *e __UNUSED__, Evas_Object *o __UNUSED__, void *event return; } - if (!strcmp(k, "Left") || !strcmp(k, "BackSpace")) + + if (!strcmp(k, "Escape")) + evas_object_smart_callback_call(sb->main, "back", sb->entry); + else if (!strcmp(k, "Left") || !strcmp(k, "BackSpace")) _prev_entry(sb); else if (!strcmp(k, "Right") || !strcmp(k, "space")) _next_entry(sb); @@ -411,31 +725,23 @@ _key_down(void *data, Evas *e __UNUSED__, Evas_Object *o __UNUSED__, void *event _first_entry(sb); else if (!strcmp(k, "End")) _last_entry(sb); -} - -static void -_ephoto_single_browser_recalc(Ephoto_Single_Browser *sb) -{ - if (sb->viewer) +#if ROTATION + else if (!strcmp(k, "bracketleft")) { - evas_object_del(sb->viewer); - sb->viewer = NULL; + if (!shift) _rotate_counterclock(sb); + else _flip_horiz(sb); } - - if (sb->entry) + else if (!strcmp(k, "bracketright")) { - const char *bname = ecore_file_file_get(sb->entry->path); - sb->viewer = _viewer_add(sb->layout, sb->entry->path); - elm_layout_content_set(sb->layout, "ephoto.swallow.content", sb->viewer); - evas_object_event_callback_add - (sb->viewer, EVAS_CALLBACK_MOUSE_WHEEL, _mouse_wheel, sb); - evas_object_smart_callback_add - (sb->viewer, "scroll", _viewer_scrolled, sb); - evas_object_show(sb->viewer); - ephoto_title_set(sb->ephoto, bname); + if (!shift) _rotate_clock(sb); + else _flip_vert(sb); + } +#endif + else if (!strcmp(k, "F5")) + { + if (sb->entry) + evas_object_smart_callback_call(sb->main, "slideshow", sb->entry); } - - elm_object_focus(sb->layout); } static void @@ -446,7 +752,7 @@ _entry_free(void *data, const Ephoto_Entry *entry __UNUSED__) } static void -_layout_del(void *data, Evas *e __UNUSED__, Evas_Object *o __UNUSED__, void *event_info __UNUSED__) +_main_del(void *data, Evas *e __UNUSED__, Evas_Object *o __UNUSED__, void *event_info __UNUSED__) { Ephoto_Single_Browser *sb = data; Ecore_Event_Handler *handler; @@ -476,6 +782,8 @@ _ephoto_single_entry_create(void *data, int type __UNUSED__, void *event __UNUSE e = ev->entry; if (!sb->entry && sb->pending_path && e->path == sb->pending_path) { + DBG("Adding entry %p for path %s", e, sb->pending_path); + eina_stringshare_del(sb->pending_path); sb->pending_path = NULL; ephoto_single_browser_entry_set(sb->ephoto->single_browser, e); @@ -487,26 +795,161 @@ _ephoto_single_entry_create(void *data, int type __UNUSED__, void *event __UNUSE Evas_Object * ephoto_single_browser_add(Ephoto *ephoto, Evas_Object *parent) { + Evas_Object *box = elm_box_add(parent); + Evas_Object *but, *ic, *sep; Ephoto_Single_Browser *sb; + EINA_SAFETY_ON_NULL_RETURN_VAL(box, NULL); + sb = calloc(1, sizeof(Ephoto_Single_Browser)); EINA_SAFETY_ON_NULL_GOTO(sb, error); sb->ephoto = ephoto; + sb->main = box; + elm_box_horizontal_set(sb->main, EINA_FALSE); + elm_box_homogeneous_set(sb->main, EINA_FALSE); + evas_object_event_callback_add(sb->main, EVAS_CALLBACK_DEL, _main_del, sb); + evas_object_event_callback_add + (sb->main, EVAS_CALLBACK_KEY_DOWN, _key_down, sb); + evas_object_data_set(sb->main, "single_browser", sb); - sb->layout = elm_layout_add(parent); - if (!elm_layout_file_set(sb->layout, THEME_FILE, "ephoto/layout/single/browser")) - { - ERR("Could not load style 'ephoto/layout/single/browser' from theme"); - goto error; - } - evas_object_size_hint_weight_set - (sb->layout, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); - evas_object_size_hint_fill_set - (sb->layout, EVAS_HINT_FILL, EVAS_HINT_FILL); - evas_object_event_callback_add(sb->layout, EVAS_CALLBACK_KEY_DOWN, _key_down, sb); - evas_object_event_callback_add(sb->layout, EVAS_CALLBACK_DEL, _layout_del, sb); - evas_object_data_set(sb->layout, "single_browser", sb); - evas_object_show(sb->layout); + sb->bar = elm_box_add(sb->main); + elm_box_horizontal_set(sb->bar, EINA_TRUE); + elm_box_homogeneous_set(sb->bar, EINA_FALSE); + evas_object_size_hint_weight_set(sb->bar, EVAS_HINT_EXPAND, 0.0); + evas_object_size_hint_align_set(sb->bar, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_show(sb->bar); + elm_box_pack_end(sb->main, sb->bar); + + but = elm_button_add(sb->bar); + + ic = elm_icon_add(but); + elm_icon_file_set(ic, PACKAGE_DATA_DIR "/images/back_grid.png", NULL); + + elm_button_icon_set(but, ic); + evas_object_size_hint_align_set(but, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_smart_callback_add(but, "clicked", _back, sb); + elm_box_pack_end(sb->bar, but); + evas_object_show(but); + + sep = elm_separator_add(sb->bar); + elm_box_pack_end(sb->bar, sep); + evas_object_show(sep); + + but = elm_button_add(sb->bar); + + ic = elm_icon_add(but); + elm_icon_file_set(ic, PACKAGE_DATA_DIR "/images/zoom-in.png", NULL); + + elm_button_icon_set(but, ic); + evas_object_size_hint_align_set(but, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_smart_callback_add(but, "clicked", _zoom_in_cb, sb); + elm_box_pack_end(sb->bar, but); + evas_object_show(but); + + but = elm_button_add(sb->bar); + + ic = elm_icon_add(but); + elm_icon_file_set(ic, PACKAGE_DATA_DIR "/images/zoom-out.png", NULL); + + elm_button_icon_set(but, ic); + evas_object_size_hint_align_set(but, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_smart_callback_add(but, "clicked", _zoom_out_cb, sb); + elm_box_pack_end(sb->bar, but); + evas_object_show(but); + + but = elm_button_add(sb->bar); + + ic = elm_icon_add(but); + elm_icon_file_set(ic, PACKAGE_DATA_DIR "/images/zoom-fit.png", NULL); + + elm_button_icon_set(but, ic); + evas_object_size_hint_align_set(but, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_smart_callback_add(but, "clicked", _zoom_fit_cb, sb); + elm_box_pack_end(sb->bar, but); + evas_object_show(but); + + but = elm_button_add(sb->bar); + + ic = elm_icon_add(but); + elm_icon_file_set(ic, PACKAGE_DATA_DIR "/images/zoom-original.png", NULL); + + elm_button_icon_set(but, ic); + evas_object_size_hint_align_set(but, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_smart_callback_add(but, "clicked", _zoom_1_cb, sb); + elm_box_pack_end(sb->bar, but); + evas_object_show(but); + + sep = elm_separator_add(sb->bar); + elm_box_pack_end(sb->bar, sep); + evas_object_show(sep); + + sb->label = elm_label_add(sb->bar); + evas_object_size_hint_align_set(sb->label, 0.5, 0.5); + elm_box_pack_end(sb->bar, sb->label); + evas_object_show(sb->label); + + sep = elm_separator_add(sb->bar); + elm_box_pack_end(sb->bar, sep); + evas_object_show(sep); + + but = elm_button_add(sb->bar); + + ic = elm_icon_add(but); + elm_icon_file_set(ic, PACKAGE_DATA_DIR "/images/first.png", NULL); + + elm_button_icon_set(but, ic); + evas_object_size_hint_align_set(but, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_smart_callback_add(but, "clicked", _go_first, sb); + elm_box_pack_end(sb->bar, but); + evas_object_show(but); + + but = elm_button_add(sb->bar); + + ic = elm_icon_add(but); + elm_icon_file_set(ic, PACKAGE_DATA_DIR "/images/back.png", NULL); + + elm_button_icon_set(but, ic); + evas_object_size_hint_align_set(but, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_smart_callback_add(but, "clicked", _go_prev, sb); + elm_box_pack_end(sb->bar, but); + evas_object_show(but); + + but = elm_button_add(sb->bar); + + ic = elm_icon_add(but); + elm_icon_file_set(ic, PACKAGE_DATA_DIR "/images/forward.png", NULL); + + elm_button_icon_set(but, ic); + evas_object_size_hint_align_set(but, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_smart_callback_add(but, "clicked", _go_next, sb); + elm_box_pack_end(sb->bar, but); + evas_object_show(but); + + but = elm_button_add(sb->bar); + + ic = elm_icon_add(but); + elm_icon_file_set(ic, PACKAGE_DATA_DIR "/images/last.png", NULL); + + elm_button_icon_set(but, ic); + evas_object_size_hint_align_set(but, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_smart_callback_add(but, "clicked", _go_last, sb); + elm_box_pack_end(sb->bar, but); + evas_object_show(but); + + sep = elm_separator_add(sb->bar); + elm_box_pack_end(sb->bar, sep); + evas_object_show(sep); + + but = elm_button_add(sb->bar); + + ic = elm_icon_add(but); + elm_icon_file_set(ic, PACKAGE_DATA_DIR "/images/slideshow.png", NULL); + + elm_button_icon_set(but, ic); + evas_object_size_hint_align_set(but, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_smart_callback_add(but, "clicked", _slideshow, sb); + elm_box_pack_end(sb->bar, but); + evas_object_show(but); sb->handlers = eina_list_append (sb->handlers, ecore_event_handler_add @@ -514,12 +957,12 @@ ephoto_single_browser_add(Ephoto *ephoto, Evas_Object *parent) sb->handlers = eina_list_append (sb->handlers, ecore_event_handler_add - (EPHOTO_EVENT_ENTRY_CREATE_THUMB, _ephoto_single_entry_create, sb)); + (EPHOTO_EVENT_ENTRY_CREATE, _ephoto_single_entry_create, sb)); - return sb->layout; + return sb->main; error: - evas_object_del(sb->layout); + evas_object_del(sb->main); return NULL; } @@ -527,26 +970,19 @@ void ephoto_single_browser_entry_set(Evas_Object *obj, Ephoto_Entry *entry) { Ephoto_Single_Browser *sb = evas_object_data_get(obj, "single_browser"); - Eina_Bool same_file = EINA_FALSE; EINA_SAFETY_ON_NULL_RETURN(sb); - if (sb->entry) - { - ephoto_entry_free_listener_del(sb->entry, _entry_free, sb); - if (entry && entry->path == sb->entry->path) - same_file = EINA_TRUE; - } + DBG("entry %p, was %p", entry, sb->entry); + if (sb->entry) + ephoto_entry_free_listener_del(sb->entry, _entry_free, sb); + sb->entry = entry; if (entry) ephoto_entry_free_listener_add(entry, _entry_free, sb); - - if (sb->entry || !same_file) - { - _ephoto_single_browser_recalc(sb); - _zoom_fit(sb); - } + _ephoto_single_browser_recalc(sb); + _zoom_fit(sb); } void @@ -558,4 +994,3 @@ ephoto_single_browser_path_pending_set(Evas_Object *obj, const char *path) DBG("Setting pending path '%s'", path); sb->pending_path = eina_stringshare_add(path); } - diff --git a/src/bin/ephoto_slideshow.c b/src/bin/ephoto_slideshow.c new file mode 100644 index 0000000..5bfbed6 --- /dev/null +++ b/src/bin/ephoto_slideshow.c @@ -0,0 +1,193 @@ +#include "ephoto.h" + +typedef struct _Ephoto_Slideshow Ephoto_Slideshow; + +struct _Ephoto_Slideshow +{ + Ephoto *ephoto; + Evas_Object *slideshow; + Ephoto_Entry *entry; +}; + +static void +_key_down(void *data, Evas *e __UNUSED__, Evas_Object *o __UNUSED__, void *event_info) +{ + Ephoto_Slideshow *ss = data; + Evas_Event_Key_Down *ev = event_info; + const char *k = ev->keyname; + + if (!strcmp(k, "Escape")) + { + Evas_Object *win = ss->ephoto->win; + Elm_Slideshow_Item *item; + Ephoto_Entry *entry; + + if (elm_win_fullscreen_get(win)) + elm_win_fullscreen_set(win, EINA_FALSE); + + item = elm_slideshow_item_current_get(ss->slideshow); + if (item) entry = elm_slideshow_item_data_get(item); + else entry = ss->entry; + evas_object_smart_callback_call(ss->slideshow, "back", entry); + } + else if (!strcmp(k, "F11")) + { + Evas_Object *win = ss->ephoto->win; + elm_win_fullscreen_set(win, !elm_win_fullscreen_get(win)); + } +} + +static void +_mouse_down(void *data, Evas *e __UNUSED__, Evas_Object *o __UNUSED__, void *event_info __UNUSED__) +{ + Ephoto_Slideshow *ss = data; + evas_object_smart_callback_call(ss->slideshow, "back", ss->entry); +} + +static void +_entry_free(void *data, const Ephoto_Entry *entry __UNUSED__) +{ + Ephoto_Slideshow *ss = data; + ss->entry = NULL; +} + +static void +_slideshow_del(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) +{ + Ephoto_Slideshow *ss = data; + if (ss->entry) + ephoto_entry_free_listener_del(ss->entry, _entry_free, ss); + free(ss); +} + +Evas_Object * +ephoto_slideshow_add(Ephoto *ephoto, Evas_Object *parent) +{ + Evas_Object *slideshow = elm_slideshow_add(parent); + Ephoto_Slideshow *ss; + + EINA_SAFETY_ON_NULL_RETURN_VAL(slideshow, NULL); + + ss = calloc(1, sizeof(Ephoto_Slideshow)); + EINA_SAFETY_ON_NULL_GOTO(ss, error); + ss->ephoto = ephoto; + ss->slideshow = slideshow; + evas_object_event_callback_add + (slideshow, EVAS_CALLBACK_DEL, _slideshow_del, ss); + evas_object_event_callback_add + (slideshow, EVAS_CALLBACK_KEY_DOWN, _key_down, ss); + evas_object_event_callback_add + (slideshow, EVAS_CALLBACK_MOUSE_DOWN, _mouse_down, ss); + evas_object_data_set(slideshow, "slideshow", ss); + + elm_slideshow_loop_set(slideshow, EINA_TRUE); + elm_slideshow_layout_set(slideshow, "fullscreen"); + evas_object_size_hint_weight_set + (slideshow, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(slideshow, EVAS_HINT_FILL, EVAS_HINT_FILL); + + return ss->slideshow; + + error: + evas_object_del(slideshow); + return NULL; +} + +static void +_image_resized(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *image, void *event_info __UNUSED__) +{ + Evas_Map *map = evas_map_new(4); + Evas_Coord cx, cy, x, y, w, h; + int orient = (long)evas_object_data_get(image, "orient"); + + evas_object_geometry_get(image, &x, &y, &w, &h); + evas_map_util_points_populate_from_geometry(map, x, y, w, h, 0); + cx = x + w / 2; + cy = y + h / 2; + + switch (orient) + { + case EPHOTO_ORIENT_0: + break; + case EPHOTO_ORIENT_90: + evas_map_util_rotate(map, 90.0, cx, cy); + evas_object_map_enable_set(image, EINA_TRUE); + evas_object_map_set(image, map); + printf("rotated 90 around %d,%d (%dx%d)\n", cx, cy, w, h); + break; + case EPHOTO_ORIENT_180: + evas_map_util_rotate(map, 180.0, cx, cy); + evas_object_map_enable_set(image, EINA_TRUE); + evas_object_map_set(image, map); + printf("rotated 180 around %d,%d (%d,%d %dx%d)\n", cx, cy, x, y, w, h); + break; + case EPHOTO_ORIENT_270: + evas_map_util_rotate(map, 270.0, cx, cy); + evas_object_map_enable_set(image, EINA_TRUE); + evas_object_map_set(image, map); + printf("rotated 270 around %d,%d (%dx%d)\n", cx, cy, w, h); + break; + default: + ERR("unknown orient %d", orient); + } + + evas_map_free(map); +} + +static Evas_Object * +_slideshow_item_get(void *data, Evas_Object *obj) +{ + Ephoto_Entry *entry = data; + /* TODO use viewer from ephoto_single_browser.c */ + /* TODO consider using exif rotation, see ephoto_single_browser.c */ + Evas_Object *image = elm_photo_add(obj); + elm_photo_file_set(image, entry->path); + elm_photo_fill_inside_set(image, EINA_TRUE); + elm_object_style_set(image, "shadow"); + + evas_object_data_set + (image, "orient", (void*)(long)ephoto_file_orient_get(entry->path)); + evas_object_event_callback_add + (image, EVAS_CALLBACK_RESIZE, _image_resized, NULL); + + return image; +} + +static const Elm_Slideshow_Item_Class _item_cls = {{_slideshow_item_get, NULL}}; + +void +ephoto_slideshow_entry_set(Evas_Object *obj, Ephoto_Entry *entry) +{ + Ephoto_Slideshow *ss = evas_object_data_get(obj, "slideshow"); + Ephoto_Config *conf; + Ephoto_Entry *itr; + const Eina_List *l; + EINA_SAFETY_ON_NULL_RETURN(ss); + + conf = ss->ephoto->config; + + DBG("entry %p, was %p", entry, ss->entry); + + if (ss->entry) + ephoto_entry_free_listener_del(ss->entry, _entry_free, ss); + + ss->entry = entry; + + if (entry) + ephoto_entry_free_listener_add(entry, _entry_free, ss); + + elm_slideshow_loop_set(ss->slideshow, EINA_TRUE); /* move to config? */ + elm_slideshow_transition_set(ss->slideshow, conf->slideshow_transition); + elm_slideshow_timeout_set(ss->slideshow, conf->slideshow_timeout); + elm_slideshow_clear(ss->slideshow); + if (!entry) return; + + elm_win_fullscreen_set(ss->ephoto->win, EINA_TRUE); + EINA_LIST_FOREACH(ss->ephoto->entries, l, itr) + { + Elm_Slideshow_Item *item; + if (itr->is_dir) continue; + item = elm_slideshow_item_add(ss->slideshow, &_item_cls, itr); + if (itr == entry) elm_slideshow_show(item); + } +} diff --git a/src/bin/ephoto_thumb_browser.c b/src/bin/ephoto_thumb_browser.c index 6242e46..b55cd32 100644 --- a/src/bin/ephoto_thumb_browser.c +++ b/src/bin/ephoto_thumb_browser.c @@ -6,21 +6,31 @@ #define TODO_ITEM_MIN_BATCH 16 +#define PARENT_DIR "Up" + typedef struct _Ephoto_Thumb_Browser Ephoto_Thumb_Browser; struct _Ephoto_Thumb_Browser { Ephoto *ephoto; - Ephoto_Entry *entry; + Evas_Object *main; + Evas_Object *bar; + Evas_Object *fsel; Evas_Object *grid; Eio_File *ls; Eina_List *todo_items; Eina_List *grid_items; Eina_List *handlers; + struct { + Elm_Toolbar_Item *zoom_in; + Elm_Toolbar_Item *zoom_out; + Elm_Toolbar_Item *view_single; + Elm_Toolbar_Item *slideshow; + } action; struct { Ecore_Animator *todo_items; } animator; - Eina_Bool layout_deleted : 1; + Eina_Bool main_deleted : 1; }; static void @@ -37,6 +47,16 @@ _grid_items_free(Ephoto_Thumb_Browser *tb) tb->grid_items = NULL; } +static Ephoto_Entry * +_first_file_entry_find(Ephoto_Thumb_Browser *tb) +{ + const Eina_List *l; + Ephoto_Entry *entry; + EINA_LIST_FOREACH(tb->ephoto->entries, l, entry) + if (!entry->is_dir) return entry; + return NULL; +} + static char * _ephoto_thumb_item_label_get(void *data, Evas_Object *obj __UNUSED__, const char *part __UNUSED__) { @@ -44,6 +64,54 @@ _ephoto_thumb_item_label_get(void *data, Evas_Object *obj __UNUSED__, const char return strdup(e->label); } +static Evas_Object * +_ephoto_thumb_dir_icon_get(void *data, Evas_Object *obj, const char *part) +{ + Ephoto_Entry *e = data; + const char *f; + int n; + + if (strncmp(part, "elm.swallow.icon.", sizeof("elm.swallow.icon.") - 1) != 0) + return NULL; + + n = atoi(part + sizeof("elm.swallow.icon.") - 1); + if (n < 1) + return NULL; + n--; + + f = eina_list_nth(e->dir_files, n); + if (f) + { + Evas_Object *o; + o = ephoto_thumb_add(e->ephoto, obj, f); + //elm_object_style_set(o, "default"); + return o; + } + + if (e->dir_files_checked) + return NULL; + + return ephoto_directory_thumb_add(obj, e); +} + +static Eina_Bool +_ephoto_thumb_dir_state_get(void *data, Evas_Object *obj __UNUSED__, const char *part) +{ + Ephoto_Entry *e = data; + int n; + + if (strcmp(part, "have_files") == 0) + return !!e->dir_files; + + if (strncmp(part, "have_file.", sizeof("have_file.") - 1) != 0) + return EINA_FALSE; + + n = atoi(part + sizeof("have_file.") - 1); + if (n < 1) + return EINA_FALSE; + return n <= (int)eina_list_count(e->dir_files); +} + static Evas_Object * _ephoto_thumb_file_icon_get(void *data, Evas_Object *obj, const char *part __UNUSED__) { @@ -62,6 +130,26 @@ _ephoto_thumb_item_del(void *data __UNUSED__, Evas_Object *obj __UNUSED__) */ } +static const Elm_Gengrid_Item_Class _ephoto_thumb_up_class = { + "ephoto-up", + { + _ephoto_thumb_item_label_get, + NULL, + NULL, + _ephoto_thumb_item_del + } +}; + +static const Elm_Gengrid_Item_Class _ephoto_thumb_dir_class = { + "ephoto-album-preview", + { + _ephoto_thumb_item_label_get, + _ephoto_thumb_dir_icon_get, + _ephoto_thumb_dir_state_get, + _ephoto_thumb_item_del + } +}; + static const Elm_Gengrid_Item_Class _ephoto_thumb_file_class = { "thumb", { @@ -75,56 +163,59 @@ static const Elm_Gengrid_Item_Class _ephoto_thumb_file_class = { static int _entry_cmp(const void *pa, const void *pb) { - const Ephoto_Entry *a = pa, *b = pb; - int ret, s; - - s = strcmp(a->basename, b->basename); - if (s > 0) - ret = 1; - else if (s < 0) - ret = -1; + const Elm_Gengrid_Item *ia = pa; + const Ephoto_Entry *a, *b = pb; + + a = elm_gengrid_item_data_get(ia); + + if (a->is_dir == b->is_dir) + return strcoll(a->basename, b->basename); + else if (a->is_dir) + return -1; else - ret = 0; - return ret; + return 1; } static void _entry_item_add(Ephoto_Thumb_Browser *tb, Ephoto_Entry *e) { const Elm_Gengrid_Item_Class *ic; - - ic = &_ephoto_thumb_file_class; + int near_cmp; + Elm_Gengrid_Item *near_item = NULL; + Eina_List *near_node = NULL; + + near_node = eina_list_search_sorted_near_list + (tb->grid_items, _entry_cmp, e, &near_cmp); - if (!tb->grid_items) + if (near_node) + near_item = near_node->data; + + if (e->is_dir) ic = &_ephoto_thumb_dir_class; + else ic = &_ephoto_thumb_file_class; + + if (!near_item) { e->item = elm_gengrid_item_append(tb->grid, ic, e, NULL, NULL); - tb->grid_items = eina_list_append(tb->grid_items, e); + tb->grid_items = eina_list_append(tb->grid_items, e->item); } else { - int near_cmp; - Ephoto_Entry *near_entry; - Elm_Gengrid_Item *near_item; - Eina_List *near_node = eina_list_search_sorted_near_list - (tb->grid_items, _entry_cmp, e, &near_cmp); - - near_entry = near_node->data; - near_item = near_entry->item; if (near_cmp < 0) { e->item = elm_gengrid_item_insert_after - (tb->grid, ic, e, near_item, NULL, NULL); - tb->grid_items = eina_list_append_relative_list - (tb->grid_items, e, near_node); + (tb->grid, ic, e, near_item, NULL, NULL); + tb->grid_items = eina_list_append_relative + (tb->grid_items, e->item, near_item); } else { e->item = elm_gengrid_item_insert_before - (tb->grid, ic, e, near_item, NULL, NULL); - tb->grid_items = eina_list_prepend_relative_list - (tb->grid_items, e, near_node); + (tb->grid, ic, e, near_item, NULL, NULL); + tb->grid_items = eina_list_prepend_relative + (tb->grid_items, e->item, near_item); } } + if (e->item) elm_gengrid_item_data_set(e->item, e); else @@ -135,6 +226,27 @@ _entry_item_add(Ephoto_Thumb_Browser *tb, Ephoto_Entry *e) } } +static void +_up_item_add_if_required(Ephoto_Thumb_Browser *tb) +{ + Ephoto_Entry *entry; + char *parent_dir; + + if ((strcmp(tb->ephoto->config->directory, "/") == 0)) + return; + + parent_dir = ecore_file_dir_get(tb->ephoto->config->directory); + if (!parent_dir) return; + + entry = ephoto_entry_new(tb->ephoto, parent_dir, PARENT_DIR); + free(parent_dir); + EINA_SAFETY_ON_NULL_RETURN(entry); + entry->is_up = EINA_TRUE; + entry->is_dir = EINA_TRUE; + entry->item = elm_gengrid_item_append + (tb->grid, &_ephoto_thumb_up_class, entry, NULL, NULL); +} + static Eina_Bool _todo_items_process(void *data) { @@ -144,6 +256,8 @@ _todo_items_process(void *data) if ((tb->ls) && (eina_list_count(tb->todo_items) < TODO_ITEM_MIN_BATCH)) return EINA_TRUE; + _up_item_add_if_required(tb); + tb->animator.todo_items = NULL; EINA_LIST_FREE(tb->todo_items, entry) @@ -161,7 +275,28 @@ _ephoto_thumb_selected(void *data, Evas_Object *o __UNUSED__, void *event_info) elm_gengrid_item_selected_set(it, EINA_FALSE); - ephoto_promote_single_browser(tb->ephoto, e); + if (e->is_dir) + ephoto_directory_set(tb->ephoto, e->path); + else + evas_object_smart_callback_call(tb->main, "view", e); +} + +static void +_changed_dir(void *data, Evas_Object *o __UNUSED__, void *event_info) +{ + Ephoto_Thumb_Browser *tb = data; + const char *path = event_info; + if (!path) return; + ephoto_directory_set(tb->ephoto, path); +} + +static void +_changed_dir_text(void *data, Evas_Object *o __UNUSED__, void *event_info __UNUSED__) +{ + Ephoto_Thumb_Browser *tb = data; + const char *path = elm_fileselector_entry_path_get(tb->fsel); + if (ecore_file_is_dir(path)) + ephoto_directory_set(tb->ephoto, path); } static void @@ -175,7 +310,98 @@ _zoom_set(Ephoto_Thumb_Browser *tb, int zoom) } static void -_grid_del(void *data, Evas *e __UNUSED__, Evas_Object *o __UNUSED__, void *event_info __UNUSED__) +_zoom_in(void *data, Evas_Object *o, void *event_info __UNUSED__) +{ + Ephoto_Thumb_Browser *tb = data; + Evas_Object *min = evas_object_data_get(o, "min"); + int zoom = tb->ephoto->config->thumb_size + ZOOM_STEP; + _zoom_set(tb, zoom); + if (zoom >= ZOOM_MAX) elm_object_disabled_set(o, EINA_TRUE); + if (zoom > ZOOM_MIN) elm_object_disabled_set(min, EINA_FALSE); +} + +static void +_zoom_out(void *data, Evas_Object *o, void *event_info __UNUSED__) +{ + Ephoto_Thumb_Browser *tb = data; + Evas_Object *max = evas_object_data_get(o, "max"); + int zoom = tb->ephoto->config->thumb_size - ZOOM_STEP; + _zoom_set(tb, zoom); + if (zoom <= ZOOM_MIN) elm_object_disabled_set(o, EINA_TRUE); + if (zoom < ZOOM_MAX) elm_object_disabled_set(max, EINA_FALSE); +} + +static void +_view_single(void *data, Evas_Object *o __UNUSED__, void *event_info __UNUSED__) +{ + Ephoto_Thumb_Browser *tb = data; + Elm_Gengrid_Item *it = elm_gengrid_selected_item_get(tb->grid); + Ephoto_Entry *entry; + + if (it) entry = elm_gengrid_item_data_get(it); + else entry = _first_file_entry_find(tb); + + if (!entry) return; + if (entry->is_dir) + ephoto_directory_set(tb->ephoto, entry->path); + else + evas_object_smart_callback_call(tb->main, "view", entry); +} + +static void +_slideshow(void *data, Evas_Object *o __UNUSED__, void *event_info __UNUSED__) +{ + Ephoto_Thumb_Browser *tb = data; + Elm_Gengrid_Item *it = elm_gengrid_selected_item_get(tb->grid); + Ephoto_Entry *entry; + + if (it) entry = elm_gengrid_item_data_get(it); + else entry = _first_file_entry_find(tb); + + if (!entry) return; + evas_object_smart_callback_call(tb->main, "slideshow", entry); +} + +static void +_key_down(void *data, Evas *e __UNUSED__, Evas_Object *o __UNUSED__, void *event_info) +{ + Ephoto_Thumb_Browser *tb = data; + Evas_Event_Key_Down *ev = event_info; + Eina_Bool alt = evas_key_modifier_is_set(ev->modifiers, "Alt"); + const char *k = ev->keyname; + + if (alt) + { + if (!strcmp(k, "Up")) + { + if (strcmp(tb->ephoto->config->directory, "/") != 0) + { + char *parent = ecore_file_dir_get + (tb->ephoto->config->directory); + if (parent) + ephoto_directory_set(tb->ephoto, parent); + free(parent); + } + } + + return; + } + + if (!strcmp(k, "F5")) + { + Elm_Gengrid_Item *it = elm_gengrid_selected_item_get(tb->grid); + Ephoto_Entry *entry; + if (it) entry = elm_gengrid_item_data_get(it); + else entry = _first_file_entry_find(tb); + + if (entry) + evas_object_smart_callback_call(tb->main, "slideshow", entry); + } +} + + +static void +_main_del(void *data, Evas *e __UNUSED__, Evas_Object *o __UNUSED__, void *event_info __UNUSED__) { Ephoto_Thumb_Browser *tb = data; Ecore_Event_Handler *handler; @@ -192,7 +418,7 @@ _grid_del(void *data, Evas *e __UNUSED__, Evas_Object *o __UNUSED__, void *event } if (tb->ls) { - tb->layout_deleted = EINA_TRUE; + tb->main_deleted = EINA_TRUE; eio_file_cancel(tb->ls); return; } @@ -204,9 +430,12 @@ _ephoto_thumb_populate_start(void *data, int type __UNUSED__, void *event __UNUS { Ephoto_Thumb_Browser *tb = data; + evas_object_smart_callback_call(tb->main, "changed,directory", NULL); + _todo_items_free(tb); _grid_items_free(tb); elm_gengrid_clear(tb->grid); + elm_fileselector_entry_path_set(tb->fsel, tb->ephoto->config->directory); return ECORE_CALLBACK_PASS_ON; } @@ -217,12 +446,14 @@ _ephoto_thumb_populate_end(void *data, int type __UNUSED__, void *event __UNUSED Ephoto_Thumb_Browser *tb = data; tb->ls = NULL; - if (tb->layout_deleted) + if (tb->main_deleted) { free(tb); return ECORE_CALLBACK_PASS_ON; } + if (!tb->animator.todo_items) _up_item_add_if_required(tb); + return ECORE_CALLBACK_PASS_ON; } @@ -251,26 +482,116 @@ _ephoto_thumb_entry_create(void *data, int type __UNUSED__, void *event) Evas_Object * ephoto_thumb_browser_add(Ephoto *ephoto, Evas_Object *parent) { + Evas_Object *box = elm_box_add(parent); + Evas_Object *ic, *but, *min, *max; Ephoto_Thumb_Browser *tb; + EINA_SAFETY_ON_NULL_RETURN_VAL(box, NULL); + tb = calloc(1, sizeof(Ephoto_Thumb_Browser)); EINA_SAFETY_ON_NULL_GOTO(tb, error); - tb->ephoto = ephoto; - tb->grid = elm_gengrid_add(parent); + elm_theme_extension_add(NULL, PACKAGE_DATA_DIR "/themes/default/ephoto.edj"); + + tb->ephoto = ephoto; + tb->main = box; + elm_box_horizontal_set(tb->main, EINA_FALSE); + elm_box_homogeneous_set(tb->main, EINA_FALSE); + evas_object_event_callback_add(tb->main, EVAS_CALLBACK_DEL, _main_del, tb); + evas_object_event_callback_add + (tb->main, EVAS_CALLBACK_KEY_DOWN, _key_down, tb); + evas_object_data_set(tb->main, "thumb_browser", tb); + + tb->bar = elm_box_add(tb->main); + elm_box_horizontal_set(tb->bar, EINA_TRUE); + elm_box_homogeneous_set(tb->bar, EINA_FALSE); + evas_object_size_hint_weight_set(tb->bar, EVAS_HINT_EXPAND, 0.0); + evas_object_size_hint_align_set(tb->bar, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_show(tb->bar); + elm_box_pack_end(tb->main, tb->bar); + + but = elm_button_add(tb->bar); + + ic = elm_icon_add(but); + elm_icon_file_set(ic, PACKAGE_DATA_DIR "/images/single.png", NULL); + + elm_button_icon_set(but, ic); + evas_object_size_hint_align_set(but, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_smart_callback_add(but, "clicked", _view_single, tb); + elm_box_pack_end(tb->bar, but); + evas_object_show(but); + + but = elm_button_add(tb->bar); + + ic = elm_icon_add(but); + elm_icon_file_set(ic, PACKAGE_DATA_DIR "/images/slideshow.png", NULL); + + elm_button_icon_set(but, ic); + evas_object_size_hint_align_set(but, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_smart_callback_add(but, "clicked", _slideshow, tb); + elm_box_pack_end(tb->bar, but); + evas_object_show(but); + + tb->fsel = elm_fileselector_entry_add(tb->bar); + EINA_SAFETY_ON_NULL_GOTO(tb->fsel, error); + evas_object_size_hint_weight_set(tb->fsel, EVAS_HINT_EXPAND, 0.0); + evas_object_size_hint_align_set(tb->fsel, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_fileselector_entry_button_label_set(tb->fsel, "Choose..."); + elm_fileselector_entry_folder_only_set(tb->fsel, EINA_TRUE); + evas_object_smart_callback_add + (tb->fsel, "file,chosen", _changed_dir, tb); + evas_object_smart_callback_add + (tb->fsel, "activated", _changed_dir_text, tb); + evas_object_show(tb->fsel); + elm_box_pack_end(tb->bar, tb->fsel); + + but = elm_button_add(tb->bar); + + ic = elm_icon_add(but); + elm_icon_file_set(ic, PACKAGE_DATA_DIR "/images/zoom-in.png", NULL); + + elm_button_icon_set(but, ic); + evas_object_size_hint_align_set(but, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_smart_callback_add(but, "clicked", _zoom_in, tb); + elm_box_pack_end(tb->bar, but); + evas_object_show(but); + max = but; + + but = elm_button_add(tb->bar); + + ic = elm_icon_add(but); + elm_icon_file_set(ic, PACKAGE_DATA_DIR "/images/zoom-out.png", NULL); + + elm_button_icon_set(but, ic); + evas_object_size_hint_align_set(but, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_smart_callback_add(but, "clicked", _zoom_out, tb); + elm_box_pack_end(tb->bar, but); + evas_object_show(but); + min = but; + + evas_object_data_set(max, "min", min); + evas_object_data_set(min, "max", max); + + tb->grid = elm_gengrid_add(tb->main); EINA_SAFETY_ON_NULL_GOTO(tb->grid, error); + evas_object_size_hint_weight_set + (tb->grid, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(tb->grid, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_gengrid_align_set(tb->grid, 0.5, 0.5); elm_gengrid_bounce_set(tb->grid, EINA_FALSE, EINA_TRUE); evas_object_size_hint_align_set (tb->grid, EVAS_HINT_FILL, EVAS_HINT_FILL); evas_object_size_hint_weight_set (tb->grid, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_smart_callback_add (tb->grid, "selected", _ephoto_thumb_selected, tb); + _zoom_set(tb, tb->ephoto->config->thumb_size); - evas_object_event_callback_add(tb->grid, EVAS_CALLBACK_DEL, _grid_del, tb); - evas_object_data_set(tb->grid, "thumb_browser", tb); + evas_object_show(tb->grid); + elm_box_pack_end(tb->main, tb->grid); tb->handlers = eina_list_append (tb->handlers, ecore_event_handler_add @@ -286,18 +607,11 @@ ephoto_thumb_browser_add(Ephoto *ephoto, Evas_Object *parent) tb->handlers = eina_list_append (tb->handlers, ecore_event_handler_add - (EPHOTO_EVENT_ENTRY_CREATE_THUMB, _ephoto_thumb_entry_create, tb)); + (EPHOTO_EVENT_ENTRY_CREATE, _ephoto_thumb_entry_create, tb)); - return tb->grid; + return tb->main; error: - evas_object_del(tb->grid); + evas_object_del(tb->main); return NULL; } - -void -ephoto_thumb_browser_entry_set(Evas_Object *obj, Ephoto_Entry *entry) -{ - Ephoto_Thumb_Browser *tb = evas_object_data_get(obj, "thumb_browser"); - tb->entry = entry; -}