# include "private.h"
# include <assert.h>
# include <Elementary.h>
# include <Ecore_Input.h>
# include <Ecore_IMF.h>
# include <Ecore_IMF_Evas.h>
# include "win.h"
# include "termcmd.h"
# include "config.h"
# include "main.h"
# include "miniview.h"
# include "gravatar.h"
# include "media.h"
# include "termio.h"
# include "utils.h"
# include "sel.h"
# include "controls.h"
# include "keyin.h"
# include "term_container.h"
/**
* Design :
* A terminal widget is Term . It hosts various Evas_Object , like a ` termio `
* handling the textgrid .
* It is hosted in a Term_Container of type Solo .
* On Term_Container :
* It is a generic structure with a set of function pointers . It is a simple
* way to objectify and have genericity between a Window , a Split or Tabs .
* Solo , Win , Split , Tabs have a Term_Container as their first field and thus
* can be casted to Term_Container to have access to those APIs .
*
* Solo is the simplest container , hosting just a Term .
* Win is a window and has only one container child .
* Split is a widget to separate an area of the screen in 2 and thus has 2
* children that can be either Solo or Tabs .
* Tabs is a Term_Container containing many containers ( at the moment , only
* Solo ones ) and have a system of tabs .
*
* All the windows are in the ` wins ` list .
*/
/* specific log domain to help debug code in that file */
int _win_log_dom = - 1 ;
# undef CRITICAL
# undef ERR
# undef WRN
# undef INF
# undef DBG
# define CRITICAL(...) EINA_LOG_DOM_CRIT(_win_log_dom, __VA_ARGS__)
# define ERR(...) EINA_LOG_DOM_ERR(_win_log_dom, __VA_ARGS__)
# define WRN(...) EINA_LOG_DOM_WARN(_win_log_dom, __VA_ARGS__)
# define INF(...) EINA_LOG_DOM_INFO(_win_log_dom, __VA_ARGS__)
# define DBG(...) EINA_LOG_DOM_DBG(_win_log_dom, __VA_ARGS__)
# define PANES_TOP "top"
# define PANES_BOTTOM "bottom"
# define DRAG_TIMEOUT 0.4
/* {{{ Structs */
typedef struct _Split Split ;
typedef struct _Tabbar Tabbar ;
typedef struct _Solo Solo ;
typedef struct _Tabs Tabs ;
typedef struct _Tab_Item Tab_Item ;
typedef struct _Tab_Drag Tab_Drag ;
struct _Tab_Drag
{
Evas_Coord mdx ; /* Mouse-down x */
Evas_Coord mdy ; /* Mouse-down y */
Split_Direction split_direction ;
Term * term_over ;
Term * term ;
Evas_Object * icon ;
Evas_Object * img ;
Evas * e ;
Ecore_Timer * timer ;
/* To be able to restore */
Term_Container_Type parent_type ;
union {
struct {
int previous_position ;
Term_Container * tabs_child ;
} ;
struct {
Term_Container * other ;
Eina_Bool is_horizontal ;
Eina_Bool is_first_child ;
} ; } ;
} ;
struct _Tabbar
{
struct {
Evas_Object * box ;
} l , r ;
} ;
struct _Term
{
Win * wn ;
Config * config ;
Term_Container * container ;
Evas_Object * bg ;
Evas_Object * bg_edj ;
Evas_Object * core ;
Evas_Object * termio ;
Evas_Object * media ;
Evas_Object * popmedia ;
Evas_Object * miniview ;
Evas_Object * sel ;
Evas_Object * sendfile_request ;
Evas_Object * sendfile_progress ;
Evas_Object * sendfile_progress_bar ;
Evas_Object * tab_spacer ;
Evas_Object * tab_region_base ;
Evas_Object * tab_region_bg ;
Evas_Object * tab_inactive ;
Tab_Item * tab_item ;
Eina_List * popmedia_queue ;
Ecore_Timer * sendfile_request_hide_timer ;
Ecore_Timer * sendfile_progress_hide_timer ;
const char * sendfile_dir ;
Media_Type poptype , mediatype ;
Tabbar tabbar ;
int step_x , step_y , min_w , min_h , req_w , req_h ;
struct {
int x , y ;
} down ;
int refcnt ;
unsigned char hold : 1 ;
unsigned char unswallowed : 1 ;
unsigned char missed_bell : 1 ;
unsigned char miniview_shown : 1 ;
unsigned char popmedia_deleted : 1 ;
Eina_Bool sendfile_request_enabled : 1 ;
Eina_Bool sendfile_progress_enabled : 1 ;
} ;
struct _Solo {
Term_Container tc ;
Term * term ;
} ;
struct _Tab_Item {
Term_Container * tc ;
Evas_Object * obj ;
void * selector_entry ;
} ;
struct _Tabs {
Term_Container tc ;
Evas_Object * selector ;
Evas_Object * selector_bg ;
Eina_List * tabs ; // Tab_Item
Tab_Item * current ;
double v1_orig ;
double v2_orig ;
} ;
struct _Split
{
Term_Container tc ;
Term_Container * tc1 , * tc2 ; // left/right or top/bottom child splits, null if leaf
Evas_Object * panes ;
Term_Container * last_focus ;
unsigned char is_horizontal : 1 ;
} ;
struct _Win
{
Term_Container tc ; /* has to be first field */
Keys_Handler khdl ;
const char * preedit_str ;
Term_Container * child ;
Evas_Object * win ;
Evas_Object * conform ;
Evas_Object * backbg ;
Evas_Object * base ;
Config * config ;
Eina_List * terms ;
Split * split ;
Ecore_Job * size_job ;
Evas_Object * cmdbox ;
Ecore_Timer * cmdbox_del_timer ;
Ecore_Timer * hide_cursor_timer ;
unsigned char focused : 1 ;
unsigned char cmdbox_up : 1 ;
unsigned char group_input : 1 ;
unsigned char group_only_visible : 1 ;
unsigned char group_once_handled : 1 ;
unsigned char translucent : 1 ;
unsigned int on_popover ;
} ;
/* }}} */
/* {{{ static */
static Eina_List * wins = NULL ;
static Tab_Drag * _tab_drag = NULL ;
static Eina_Bool _win_is_focused ( Win * wn ) ;
static Term_Container * _solo_new ( Term * term , Win * wn ) ;
static Term_Container * _split_new ( Term_Container * tc1 , Term_Container * tc2 , Eina_Bool is_horizontal ) ;
static Term_Container * _tabs_new ( Term_Container * child , Term_Container * parent ) ;
static void _term_free ( Term * term ) ;
static void _term_media_update ( Term * term , const Config * config ) ;
static void _term_miniview_check ( Term * term ) ;
static void _popmedia_queue_process ( Term * term ) ;
static void _cb_size_hint ( void * data , Evas * _e EINA_UNUSED , Evas_Object * obj , void * _event EINA_UNUSED ) ;
static void _tab_new_cb ( void * data , Evas_Object * _obj EINA_UNUSED , void * _event_info EINA_UNUSED ) ;
static Tab_Item * tab_item_new ( Tabs * tabs , Term_Container * child ) ;
static void _tabs_recreate ( Tabs * tabs ) ;
static void _tab_drag_free ( void ) ;
static void _term_tabregion_free ( Term * term ) ;
static void _imf_event_commit_cb ( void * data , Ecore_IMF_Context * _ctx EINA_UNUSED , void * event ) ;
/* }}} */
/* {{{ Utils */
# ifndef NDEBUG
static void
_focus_validator ( void )
{
Win * wn ;
Term * term ;
Eina_List * l , * ll ;
EINA_LIST_FOREACH ( wins , l , wn )
{
Term_Container * focused_found = NULL ;
if ( wn - > group_input )
continue ;
EINA_LIST_FOREACH ( wn - > terms , ll , term )
{
Term_Container * tc = term - > container ;
if ( focused_found )
{
assert ( ! tc - > is_focused ) ;
}
else
{
if ( tc - > is_focused )
{
Term * term_focused ;
Term_Container * tc_parent = tc ;
focused_found = tc ;
do
{
assert ( tc_parent - > is_focused ) ;
tc_parent = tc_parent - > parent ;
}
while ( tc_parent - > type ! = TERM_CONTAINER_TYPE_WIN ) ;
assert ( tc_parent - > is_focused ) ;
term_focused = tc_parent - > focused_term_get ( tc_parent ) ;
assert ( term_focused = = term ) ;
}
}
}
}
}
# else
static void
_focus_validator ( void )
{ }
# endif
/* }}} */
/* {{{ Scale */
static void
_scale_round ( void * data EINA_UNUSED ,
Evas_Object * obj ,
void * event_info EINA_UNUSED )
{
double val = elm_slider_value_get ( obj ) ;
double v ;
v = ( ( double ) ( ( int ) ( val * 10.0 ) ) ) / 10.0 ;
if ( v ! = val ) elm_slider_value_set ( obj , v ) ;
}
static void
_scale_change ( void * data EINA_UNUSED ,
Evas_Object * obj ,
void * event_info EINA_UNUSED )
{
double scale = elm_config_scale_get ( ) ;
double val = elm_slider_value_get ( obj ) ;
if ( scale = = val )
return ;
elm_config_scale_set ( val ) ;
elm_config_all_flush ( ) ;
}
typedef struct _Scale_Ctx
{
Evas_Object * hv ;
Term * term ;
} Scale_Ctx ;
static void
_scale_done ( void * data ,
Evas_Object * obj EINA_UNUSED ,
void * event_info EINA_UNUSED )
{
Scale_Ctx * ctx = data ;
evas_object_smart_callback_del_full ( ctx - > hv , " dismissed " ,
_scale_done , ctx ) ;
evas_object_del ( ctx - > hv ) ;
ctx - > term - > wn - > on_popover - - ;
term_unref ( ctx - > term ) ;
elm_config_save ( ) ;
config_save ( ctx - > term - > config ) ;
free ( ctx ) ;
}
void
win_scale_wizard ( Evas_Object * win , Term * term )
{
Evas_Object * bx , * lbl , * sl , * fr , * bt ;
const char * txt ;
Scale_Ctx * ctx ;
EINA_SAFETY_ON_NULL_RETURN ( term ) ;
ctx = calloc ( 1 , sizeof ( * ctx ) ) ;
if ( ! ctx )
return ;
ctx - > term = term ;
term - > wn - > on_popover + + ;
term_ref ( term ) ;
ctx - > hv = elm_hover_add ( win ) ;
evas_object_size_hint_weight_set ( ctx - > hv , EVAS_HINT_EXPAND , 0.0 ) ;
evas_object_size_hint_align_set ( ctx - > hv , EVAS_HINT_FILL , 0.5 ) ;
elm_hover_parent_set ( ctx - > hv , win ) ;
elm_hover_target_set ( ctx - > hv , win ) ;
evas_object_smart_callback_add ( ctx - > hv , " dismissed " , _scale_done , ctx ) ;
fr = elm_frame_add ( win ) ;
evas_object_size_hint_weight_set ( fr , EVAS_HINT_EXPAND , 0.0 ) ;
evas_object_size_hint_align_set ( fr , EVAS_HINT_FILL , 0.5 ) ;
elm_object_text_set ( fr , _ ( " Scale " ) ) ;
elm_object_part_content_set ( ctx - > hv , " middle " , fr ) ;
evas_object_show ( fr ) ;
bx = elm_box_add ( win ) ;
evas_object_size_hint_weight_set ( bx , EVAS_HINT_EXPAND , 0.0 ) ;
evas_object_size_hint_align_set ( bx , EVAS_HINT_FILL , 0.5 ) ;
elm_object_content_set ( fr , bx ) ;
evas_object_show ( bx ) ;
fr = elm_frame_add ( win ) ;
evas_object_size_hint_weight_set ( fr , EVAS_HINT_EXPAND , 0.0 ) ;
evas_object_size_hint_align_set ( fr , EVAS_HINT_FILL , 0.5 ) ;
elm_object_style_set ( fr , " pad_medium " ) ;
elm_box_pack_end ( bx , fr ) ;
evas_object_show ( fr ) ;
lbl = elm_label_add ( win ) ;
evas_object_size_hint_weight_set ( lbl , EVAS_HINT_EXPAND , 0.0 ) ;
evas_object_size_hint_align_set ( lbl , EVAS_HINT_FILL , 0.5 ) ;
txt = eina_stringshare_printf ( " <hilight>%s</> " , _ ( " Scale " ) ) ;
elm_object_text_set ( lbl , txt ) ;
eina_stringshare_del ( txt ) ;
elm_object_content_set ( fr , lbl ) ;
elm_box_pack_end ( bx , lbl ) ;
evas_object_show ( lbl ) ;
sl = elm_slider_add ( win ) ;
evas_object_size_hint_weight_set ( sl , EVAS_HINT_EXPAND , 0.0 ) ;
evas_object_size_hint_align_set ( sl , EVAS_HINT_FILL , 0.5 ) ;
elm_slider_span_size_set ( sl , 120 ) ;
elm_slider_unit_format_set ( sl , " %1.2f " ) ;
elm_slider_indicator_format_set ( sl , " %1.2f " ) ;
elm_slider_min_max_set ( sl , 0.25 , 5.0 ) ;
elm_slider_value_set ( sl , elm_config_scale_get ( ) ) ;
elm_box_pack_end ( bx , sl ) ;
evas_object_show ( sl ) ;
evas_object_smart_callback_add ( sl , " changed " , _scale_round , NULL ) ;
evas_object_smart_callback_add ( sl , " delay,changed " , _scale_change , NULL ) ;
lbl = elm_label_add ( win ) ;
evas_object_size_hint_weight_set ( lbl , EVAS_HINT_EXPAND , 0.0 ) ;
evas_object_size_hint_align_set ( lbl , EVAS_HINT_FILL , 0.5 ) ;
elm_object_text_set ( lbl , _ ( " Select prefered size so that this text is readable. " ) ) ;
elm_box_pack_end ( bx , lbl ) ;
evas_object_show ( lbl ) ;
lbl = elm_label_add ( win ) ;
evas_object_size_hint_weight_set ( lbl , EVAS_HINT_EXPAND , 0.0 ) ;
evas_object_size_hint_align_set ( lbl , EVAS_HINT_FILL , 0.5 ) ;
elm_object_text_set ( lbl , _ ( " The scale configuration can be changed in the Settings (right click on the terminal) → Toolkit, or by starting the command <keyword>elementary_config</keyword>. " ) ) ;
elm_box_pack_end ( bx , lbl ) ;
evas_object_show ( lbl ) ;
bt = elm_button_add ( win ) ;
elm_object_text_set ( bt , _ ( " Done " ) ) ;
elm_box_pack_end ( bx , bt ) ;
evas_object_smart_callback_add ( bt , " clicked " , _scale_done , ctx ) ;
evas_object_show ( bt ) ;
evas_object_show ( ctx - > hv ) ;
elm_object_focus_set ( ctx - > hv , EINA_TRUE ) ;
}
/* }}} */
/* {{{ Solo */
static Evas_Object *
_solo_get_evas_object ( const Term_Container * tc )
{
Solo * solo ;
assert ( tc - > type = = TERM_CONTAINER_TYPE_SOLO ) ;
solo = ( Solo * ) tc ;
return solo - > term - > bg ;
}
static Term *
_solo_focused_term_get ( const Term_Container * tc )
{
Solo * solo ;
Term * term = NULL ;
assert ( tc - > type = = TERM_CONTAINER_TYPE_SOLO ) ;
solo = ( Solo * ) tc ;
if ( tc - > is_focused )
term = solo - > term ;
return term ;
}
static Term *
_solo_find_term_at_coords ( const Term_Container * tc ,
Evas_Coord _mx EINA_UNUSED ,
Evas_Coord _my EINA_UNUSED )
{
Solo * solo ;
assert ( tc - > type = = TERM_CONTAINER_TYPE_SOLO ) ;
solo = ( Solo * ) tc ;
return solo - > term ;
}
static void
_solo_size_eval ( Term_Container * container , Sizeinfo * info )
{
Term * term ;
int mw = 0 , mh = 0 ;
Solo * solo ;
assert ( container - > type = = TERM_CONTAINER_TYPE_SOLO ) ;
solo = ( Solo * ) container ;
term = solo - > term ;
info - > min_w = term - > min_w ;
info - > min_h = term - > min_h ;
info - > step_x = term - > step_x ;
info - > step_y = term - > step_y ;
info - > req_w = term - > req_w ;
info - > req_h = term - > req_h ;
if ( ! evas_object_data_get ( term - > termio , " sizedone " ) )
{
evas_object_data_set ( term - > termio , " sizedone " , term - > termio ) ;
info - > req = 1 ;
}
evas_object_size_hint_min_get ( term - > bg , & mw , & mh ) ;
info - > bg_min_w = mw ;
info - > bg_min_h = mh ;
}
static void
_solo_close ( Term_Container * tc ,
Term_Container * _child EINA_UNUSED )
{
Solo * solo ;
Term * term ;
DBG ( " close " ) ;
assert ( tc - > type = = TERM_CONTAINER_TYPE_SOLO ) ;
solo = ( Solo * ) tc ;
tc - > parent - > close ( tc - > parent , tc ) ;
eina_stringshare_del ( tc - > title ) ;
term = solo - > term ;
term - > container = NULL ;
free ( tc ) ;
}
static void
_solo_tabs_new ( Term_Container * tc )
{
if ( tc - > parent - > type ! = TERM_CONTAINER_TYPE_TABS )
_tabs_new ( tc , tc - > parent ) ;
_tab_new_cb ( tc - > parent , NULL , NULL ) ;
}
static void
_solo_split ( Term_Container * tc ,
Term_Container * _child EINA_UNUSED ,
Term * from ,
const char * cmd ,
Eina_Bool is_horizontal )
{
tc - > parent - > split ( tc - > parent , tc , from , cmd , is_horizontal ) ;
}
static int
_solo_split_direction ( Term_Container * tc ,
Term_Container * child_orig EINA_UNUSED ,
Term_Container * child_new ,
Split_Direction direction )
{
return tc - > parent - > split_direction ( tc - > parent , tc , child_new , direction ) ;
}
static Term *
_solo_term_next ( const Term_Container * tc ,
const Term_Container * _child EINA_UNUSED )
{
return tc - > parent - > term_next ( tc - > parent , tc ) ;
}
static Term *
_solo_term_prev ( const Term_Container * tc ,
const Term_Container * _child EINA_UNUSED )
{
return tc - > parent - > term_prev ( tc - > parent , tc ) ;
}
static Term *
_solo_term_up ( const Term_Container * tc ,
const Term_Container * _child EINA_UNUSED )
{
return tc - > parent - > term_up ( tc - > parent , tc ) ;
}
static Term *
_solo_term_down ( const Term_Container * tc ,
const Term_Container * _child EINA_UNUSED )
{
return tc - > parent - > term_down ( tc - > parent , tc ) ;
}
static Term *
_solo_term_left ( const Term_Container * tc ,
const Term_Container * _child EINA_UNUSED )
{
return tc - > parent - > term_left ( tc - > parent , tc ) ;
}
static Term *
_solo_term_right ( const Term_Container * tc ,
const Term_Container * _child EINA_UNUSED )
{
return tc - > parent - > term_right ( tc - > parent , tc ) ;
}
static Term *
_solo_term_first ( const Term_Container * tc )
{
Solo * solo ;
assert ( tc - > type = = TERM_CONTAINER_TYPE_SOLO ) ;
solo = ( Solo * ) tc ;
return solo - > term ;
}
static Term *
_solo_term_last ( const Term_Container * tc )
{
Solo * solo ;
assert ( tc - > type = = TERM_CONTAINER_TYPE_SOLO ) ;
solo = ( Solo * ) tc ;
return solo - > term ;
}
static void
_solo_set_title ( Term_Container * tc ,
Term_Container * _child EINA_UNUSED ,
const char * title )
{
Solo * solo ;
Term * term ;
assert ( tc - > type = = TERM_CONTAINER_TYPE_SOLO ) ;
solo = ( Solo * ) tc ;
term = solo - > term ;
eina_stringshare_del ( tc - > title ) ;
tc - > title = eina_stringshare_add ( title ) ;
if ( term - > config - > show_tabs )
{
elm_layout_text_set ( term - > bg , " terminology.tab.title " , title ) ;
}
if ( _tab_drag & & _tab_drag - > term = = term & & _tab_drag - > icon )
{
elm_layout_text_set ( _tab_drag - > icon ,
" terminology.title " , title ) ;
}
tc - > parent - > set_title ( tc - > parent , tc , title ) ;
}
static void
_solo_bell ( Term_Container * tc ,
Term_Container * _child EINA_UNUSED )
{
Solo * solo ;
Term * term ;
assert ( tc - > type = = TERM_CONTAINER_TYPE_SOLO ) ;
solo = ( Solo * ) tc ;
term = solo - > term ;
if ( ! tc - > is_focused )
term - > missed_bell = EINA_TRUE ;
if ( ! tc - > wn - > config - > disable_visual_bell )
{
elm_layout_signal_emit ( term - > bg , " bell " , " terminology " ) ;
elm_layout_signal_emit ( term - > core , " bell " , " terminology " ) ;
if ( tc - > wn - > config - > bell_rings )
{
elm_layout_signal_emit ( term - > bg , " bell,ring " , " terminology " ) ;
elm_layout_signal_emit ( term - > core , " bell,ring " , " terminology " ) ;
}
if ( ( _tab_drag ! = NULL ) & & ( _tab_drag - > term = = term ) )
{
elm_layout_signal_emit ( _tab_drag - > icon , " bell " , " terminology " ) ;
}
}
if ( ( term - > missed_bell ) & & ( term - > config - > show_tabs )
& & ( tc - > parent - > type = = TERM_CONTAINER_TYPE_SPLIT ) )
{
elm_layout_signal_emit ( term - > bg , " tab,bell,on " , " terminology " ) ;
}
edje_object_message_signal_process ( term - > bg_edj ) ;
tc - > parent - > bell ( tc - > parent , tc ) ;
}
static void
_solo_unfocus ( Term_Container * tc , Term_Container * relative )
{
Solo * solo ;
Term * term ;
assert ( tc - > type = = TERM_CONTAINER_TYPE_SOLO ) ;
solo = ( Solo * ) tc ;
term = solo - > term ;
DBG ( " tc:%p tc->is_focused:%d from_parent:%d term:%p " ,
tc , tc - > is_focused , tc - > parent = = relative , term ) ;
if ( ! tc - > is_focused )
return ;
tc - > is_focused = EINA_FALSE ;
termio_focus_out ( term - > termio ) ;
if ( tc - > parent ! = relative )
tc - > parent - > unfocus ( tc - > parent , tc ) ;
if ( ! term - > config - > disable_focus_visuals )
{
elm_layout_signal_emit ( term - > bg , " focus,out " , " terminology " ) ;
elm_layout_signal_emit ( term - > core , " focus,out " , " terminology " ) ;
}
}
static void
_solo_focus ( Term_Container * tc , Term_Container * relative )
{
Solo * solo ;
Term * term ;
const char * title ;
assert ( tc - > type = = TERM_CONTAINER_TYPE_SOLO ) ;
solo = ( Solo * ) tc ;
term = solo - > term ;
if ( ! tc - > parent )
return ;
DBG ( " tc:%p tc->is_focused:%d from_parent:%d term:%p " ,
tc , tc - > is_focused , tc - > parent = = relative , term ) ;
if ( tc - > is_focused )
return ;
term - > missed_bell = EINA_FALSE ;
if ( ( term - > config - > show_tabs )
& & ( tc - > parent - > type = = TERM_CONTAINER_TYPE_SPLIT ) )
{
elm_layout_signal_emit ( term - > bg , " tab,bell,off " , " terminology " ) ;
}
if ( tc - > parent ! = relative )
{
DBG ( " focus tc:%p term:%p " , tc , solo - > term ) ;
tc - > parent - > focus ( tc - > parent , tc ) ;
}
if ( term - > config - > disable_focus_visuals )
{
elm_layout_signal_emit ( term - > bg , " focused,set " , " terminology " ) ;
elm_layout_signal_emit ( term - > core , " focused,set " , " terminology " ) ;
}
else
{
elm_layout_signal_emit ( term - > bg , " focus,in " , " terminology " ) ;
elm_layout_signal_emit ( term - > core , " focus,in " , " terminology " ) ;
}
termio_event_feed_mouse_in ( term - > termio ) ;
termio_focus_in ( term - > termio ) ;
title = termio_title_get ( term - > termio ) ;
if ( title )
tc - > set_title ( tc , tc , title ) ;
if ( term - > missed_bell )
term - > missed_bell = EINA_FALSE ;
edje_object_message_signal_process ( term - > bg_edj ) ;
if ( ! tc - > is_focused & & relative ! = tc - > parent )
{
tc - > is_focused = EINA_TRUE ;
tc - > parent - > focus ( tc - > parent , tc ) ;
}
tc - > is_focused = EINA_TRUE ;
_focus_validator ( ) ;
}
static Eina_Bool
_solo_is_visible ( const Term_Container * tc , const Term_Container * _child EINA_UNUSED )
{
assert ( tc - > type = = TERM_CONTAINER_TYPE_SOLO ) ;
return tc - > parent - > is_visible ( tc - > parent , tc ) ;
}