tk 0.1.5

Rust bindings for Tk GUI library
Documentation
use crate::{
    InterpError,
    InterpResult,
    OptPair,
    PathOptsWidgets,
    TkInstance,
    TkOption,
    TkRectangle,
    Widget,
    WmManage,
    error::{
        DeError,
        DeKind,
        NotList,
        TkCanvasItemTypeParseError
    },
    event::TkEventSeq,
    opt::{
        TkCanvasArcOpt,
        TkCanvasBitmapOpt,
        TkCanvasImageOpt,
        TkCanvasItemOpt,
        TkCanvasLineOpt,
        TkCanvasOvalOpt,
        TkCanvasPolygonOpt,
        TkCanvasPostscriptOpt,
        TkCanvasRectangleOpt,
        TkCanvasTextOpt,
        TkCanvasWindowOpt,
    },
    range::{
        TkDefaultStart,
        TkDefaultEnd,
        TkRange,
    },
};

use std::{
    fmt::{self, Display},
    ops::{
        RangeFrom,
        RangeInclusive,
        RangeToInclusive,
    },
    os::raw::{c_int, c_double},
    str::FromStr,
};

use tcl::{
    Obj,
    from_obj,
};

use tuplex::{IntoHomoTuple, NonZeroLen};

use enumx::export::*;
use enumx::predefined::*;
use cex::*;

#[derive( Copy, Clone )]
pub struct TkCanvas<Inst:TkInstance>( pub(crate) Widget<Inst> );

impl<Inst:TkInstance> WmManage<Inst> for TkCanvas<Inst> {}

pub enum ItemType {
    Arc, Bitmap, Image, Line, Oval, Polygon, Rectangle, Text, Window,
    ApplicationDefined( String ),
}

impl Display for ItemType {
    fn fmt( &self, f: &mut fmt::Formatter ) -> fmt::Result {
        use ItemType::*;
        match self {
            Arc                     => "arc"        .fmt( f ),
            Bitmap                  => "bitmap"     .fmt( f ),
            Image                   => "image"      .fmt( f ),
            Line                    => "line"       .fmt( f ),
            Oval                    => "oval"       .fmt( f ),
            Polygon                 => "polygon"    .fmt( f ),
            Rectangle               => "rectangle"  .fmt( f ),
            Text                    => "text"       .fmt( f ),
            Window                  => "window"     .fmt( f ),
            ApplicationDefined( s ) => s            .fmt( f ),
        }
    }
}

impl FromStr for ItemType {
    type Err = TkCanvasItemTypeParseError;

    fn from_str( s: &str ) -> Result<Self, Self::Err> {
        Ok( match s {
            "arc"       => ItemType::Arc,
            "bitmap"    => ItemType::Bitmap,
            "image"     => ItemType::Image,
            "line"      => ItemType::Line,
            "oval"      => ItemType::Oval,
            "polygon"   => ItemType::Polygon,
            "rectangle" => ItemType::Rectangle,
            "text"      => ItemType::Text,
            "window"    => ItemType::Window,
            _ => ItemType::ApplicationDefined( s.to_owned() ),
        })
    }
}

#[derive( Debug, Clone )]
pub struct ItemTag( pub String );
impl From<ItemTag> for Obj { fn from( tag: ItemTag ) -> Obj { tag.0.into() }}

#[derive( Debug, Clone )]
pub struct ItemId(  pub String );
impl From<ItemId> for Obj { fn from( id: ItemId ) -> Obj { id.0.into() }}

pub fn item_tag( name: &str ) -> ItemTag { ItemTag( name.to_owned() )}
pub fn item_id( name: &str ) -> ItemId { ItemId( name.to_owned() )}

pub enum TagOrId {
    Tag( String ),
    Id(  String ),
}

impl From<ItemTag> for TagOrId { fn from( tag: ItemTag ) -> Self { TagOrId::Tag( tag.0 )}}
impl From<ItemId> for TagOrId { fn from( id: ItemId ) -> Self { TagOrId::Id( id.0 )}}

impl From<TagOrId> for Obj {
    fn from( tag_or_id: TagOrId ) -> Obj {
        match tag_or_id {
            TagOrId::Tag(v) | TagOrId::Id(v) => v.into()
        }
    }
}

pub enum SearchSpec {
    Above( TagOrId ),
    All,
    Below( TagOrId ),
    Closest{ x: c_int, y: c_int, halo: Option<c_double>, start: Option<TagOrId> },
    Enclosed{ x1: c_int, y1: c_int, x2: c_int, y2: c_int },
    Overlapping{ x1: c_int, y1: c_int, x2: c_int, y2: c_int },
    WithTag( TagOrId ),
}

pub enum Index {
    Number( c_int ),
    End,
    Insert,
    SelFirst,
    SelLast,
    At( c_int, c_int ),
}

impl From<c_int> for Index {
    fn from( number: c_int ) -> Self { Index::Number( number )}
}

impl TkDefaultStart for Index {
    fn default_start() -> Self { Index::Number(0) }
}

impl TkDefaultEnd for Index {
    fn default_end() -> Self { Index::End }
}

impl From<RangeFrom<c_int>> for TkRange<Index> { // a..
    fn from( r: RangeFrom<c_int> ) -> Self {
        TkRange {
            start : Index::Number( r.start ),
            end   : Index::default_end()
        }
    }
}

impl From<RangeInclusive<c_int>> for TkRange<Index> { // a..=b
    fn from( r: RangeInclusive<c_int> ) -> Self {
        TkRange {
            start : Index::Number( *r.start() ),
            end   : Index::Number( *r.end() )
        }
    }
}

impl From<RangeToInclusive<c_int>> for TkRange<Index> { // ..=b
    fn from( r: RangeToInclusive<c_int> ) -> Self {
        TkRange {
            start : Index::default_start(),
            end   : Index::Number( r.end ),
        }
    }
}

impl From<Index> for Obj {
    fn from( index: Index ) -> Obj {
        use Index::*;
        match index {
            Number( number )    => number.into(),
            End                 => "end".into(),
            Insert              => "insert".into(),
            SelFirst            => "sel.first".into(),
            SelLast             => "sel.last".into(),
            At( x, y )          => format!( "@{},{}", x, y ).into(),
        }
    }
}

impl<Inst:TkInstance> TkCanvas<Inst> {
    pub fn addtag( &self, tag: &str, search_spec: SearchSpec ) -> InterpResult<()> {
        use SearchSpec::*;
        match search_spec {
            Above( tag_or_id ) => self.tk().run(( self.path, "addtag", tag, "above", tag_or_id )),
            All => self.tk().run(( self.path, "addtag", tag, "all" )),
            Below( tag_or_id ) => self.tk().run(( self.path, "addtag", tag, "below", tag_or_id )),
            Closest{ x, y, halo, start } => self.tk().run(( self.path, "addtag", tag, "closest", x, y, halo, start )),
            Enclosed{ x1, y1, x2, y2 } => self.tk().run(( self.path, "addtag", tag, "enclosed", x1, y1, x2, y2 )),
            Overlapping{ x1, y1, x2, y2 } => self.tk().run(( self.path, "addtag", tag, "overlapping", x1, y1, x2, y2 )),
            WithTag( tag_or_id ) => self.tk().run(( self.path, "addtag", tag, "withtag", tag_or_id )),
        }
    }

    #[cex]
    pub fn bbox<ListOfTagOrId,TupleOfTagOrId>( &self, list_of_tag_or_id: ListOfTagOrId )
        -> Result!( Option<TkRectangle> throws InterpError, DeError )
        where ListOfTagOrId: IntoHomoTuple<Obj> + NonZeroLen<TupleOfTagOrId>
            , <ListOfTagOrId as IntoHomoTuple<Obj>>::Output : Into<Obj>
    {
        let obj = self.tk().eval(( "eval", self.path, "bbox", list_of_tag_or_id.into_homo_tuple() ))?;
        if obj.is_empty() {
            return Ok( None );
        }

        let mut ints = Vec::with_capacity( 4 );
        let err_obj= obj.clone();
        let list = obj.get_elements().map_err( |_| DeError::new( DeKind::NotList, err_obj.clone() ))?;
        for elem in list {
            ints.push( self.tk().int( elem )? );
        }

        if ints.len() == 4 {
            return Ok( Some( TkRectangle{ left:ints[0], top:ints[1], right:ints[2], bottom:ints[3] }));
        } else {
            throw!( DeError::new( DeKind::ListLen{ expected:4, got: ints.len() }, err_obj ));
        }
    }

    pub fn bind( &self, tag_or_id: impl Into<TagOrId>, sequence: impl Into<TkEventSeq>, command: impl Into<Obj> )
        -> InterpResult<()>
    {
        self.tk().run(( self.path, "bind", tag_or_id.into(), sequence.into(), command ))
    }

    pub fn bind_more( &self, tag_or_id: impl Into<TagOrId>, sequence: impl Into<TkEventSeq>, command: impl Into<Obj> )
        -> InterpResult<()>
    {
        let command = format!( "+{}", command.into().to_string() );
        self.tk().run(( self.path, "bind", tag_or_id.into(), sequence.into(), command ))
    }

    pub fn canvasx( &self, screenx: c_double, gridspacing: Option<c_double> ) -> InterpResult<c_double> {
        let result = match gridspacing {
            Some( gridspacing ) => self.tk().eval(( self.path, "canvasx", screenx, gridspacing )),
            None => self.tk().eval(( self.path, "canvasx", screenx )),
        };
        self.tk().double( result? )
    }

    pub fn canvasy( &self, screenx: c_double, gridspacing: Option<c_double> ) -> InterpResult<c_double> {
        let result = match gridspacing {
            Some( gridspacing ) => self.tk().eval(( self.path, "canvasy", screenx, gridspacing )),
            None => self.tk().eval(( self.path, "canvasy", screenx )),
        };
        self.tk().double( result? )
    }

    pub fn coords( &self, tag_or_id: impl Into<TagOrId> ) -> InterpResult<Obj> {
        self.tk().eval(( self.path, "coords", tag_or_id.into() ))
    }

    pub fn set_coords( &self, tag_or_id: impl Into<TagOrId>, coords_list: Obj ) -> InterpResult<()> {
        self.tk().run(( self.path, "coords", tag_or_id.into(), coords_list ))
    }

    pub fn create_arc<Opts>( &self, x1: c_double, y1: c_double, x2: c_double, y2: c_double, opts: impl Into<PathOptsWidgets<Opts,()>> )
        -> InterpResult<ItemId>
        where Opts: IntoHomoTuple<TkCanvasArcOpt>
                  + IntoHomoTuple<OptPair>
    {
        let mut command = Vec::<Obj>::with_capacity( <<Opts as IntoHomoTuple<OptPair>>::Output as tuplex::Len>::LEN * 2 + 7 );
        command.push( self.path.into() );
        command.push( "create".into() );
        command.push( "arc".into() );
        command.push( x1.into() );
        command.push( y1.into() );
        command.push( x2.into() );
        command.push( y2.into() );
        crate::cmd::append_opts( &mut command, opts.into().opts );
        Ok( ItemId( self.tk().eval( command )?.to_string() ))
    }

    pub fn create_bitmap<Opts>( &self, x: c_double, y: c_double, opts: impl Into<PathOptsWidgets<Opts,()>> )
        -> InterpResult<ItemId>
        where Opts: IntoHomoTuple<TkCanvasBitmapOpt>
                  + IntoHomoTuple<OptPair>
    {
        let mut command = Vec::<Obj>::with_capacity( <<Opts as IntoHomoTuple<OptPair>>::Output as tuplex::Len>::LEN * 2 + 5 );
        command.push( self.path.into() );
        command.push( "create".into() );
        command.push( "bitmap".into() );
        command.push( x.into() );
        command.push( y.into() );
        crate::cmd::append_opts( &mut command, opts.into().opts );
        Ok( ItemId( self.tk().eval( command )?.to_string() ))
    }

    pub fn create_image<Opts>( &self, x: c_double, y: c_double, opts: impl Into<PathOptsWidgets<Opts,()>> )
        -> InterpResult<ItemId>
        where Opts: IntoHomoTuple<TkCanvasImageOpt>
                  + IntoHomoTuple<OptPair>
    {
        let mut command = Vec::<Obj>::with_capacity( <<Opts as IntoHomoTuple<OptPair>>::Output as tuplex::Len>::LEN * 2 + 5 );
        command.push( self.path.into() );
        command.push( "create".into() );
        command.push( "image".into() );
        command.push( x.into() );
        command.push( y.into() );
        crate::cmd::append_opts( &mut command, opts.into().opts );
        Ok( ItemId( self.tk().eval( command )?.to_string() ))
    }

    pub fn create_line<Opts>( &self, coord_list: &[(c_double,c_double)], opts: impl Into<PathOptsWidgets<Opts,()>> )
        -> InterpResult<ItemId>
        where Opts: IntoHomoTuple<TkCanvasLineOpt>
                  + IntoHomoTuple<OptPair>
    {
        let opts_len = <<Opts as IntoHomoTuple<OptPair>>::Output as tuplex::Len>::LEN;
        let mut command = Vec::<Obj>::with_capacity( coord_list.len() * 2 + opts_len * 2 + 3 );
        command.push( self.path.into() );
        command.push( "create".into() );
        command.push( "line".into() );
        coord_list.iter().for_each( |(x, y)| {
            command.push( (*x).into() );
            command.push( (*y).into() );
        });
        crate::cmd::append_opts( &mut command, opts.into().opts );
        Ok( ItemId( self.tk().eval( command )?.to_string() ))
    }

    pub fn create_oval<Opts>( &self, x1: c_double, y1: c_double, x2: c_double, y2: c_double, opts: impl Into<PathOptsWidgets<Opts,()>> )
        -> InterpResult<ItemId>
        where Opts: IntoHomoTuple<TkCanvasOvalOpt>
                  + IntoHomoTuple<OptPair>
    {
        let mut command = Vec::<Obj>::with_capacity( <<Opts as IntoHomoTuple<OptPair>>::Output as tuplex::Len>::LEN * 2 + 7 );
        command.push( self.path.into() );
        command.push( "create".into() );
        command.push( "oval".into() );
        command.push( x1.into() );
        command.push( y1.into() );
        command.push( x2.into() );
        command.push( y2.into() );
        crate::cmd::append_opts( &mut command, opts.into().opts );
        Ok( ItemId( self.tk().eval( command )?.to_string() ))
    }

    pub fn create_polygon<Opts>( &self, coord_list: &[(c_double,c_double)], opts: impl Into<PathOptsWidgets<Opts,()>> )
        -> InterpResult<ItemId>
        where Opts: IntoHomoTuple<TkCanvasPolygonOpt>
                  + IntoHomoTuple<OptPair>
    {
        let opts_len = <<Opts as IntoHomoTuple<OptPair>>::Output as tuplex::Len>::LEN;
        let mut command = Vec::<Obj>::with_capacity( coord_list.len() * 2 + opts_len * 2 + 3 );
        command.push( self.path.into() );
        command.push( "create".into() );
        command.push( "polygon".into() );
        coord_list.iter().for_each( |(x, y)| {
            command.push( (*x).into() );
            command.push( (*y).into() );
        });
        crate::cmd::append_opts( &mut command, opts.into().opts );
        Ok( ItemId( self.tk().eval( command )?.to_string() ))
    }

    pub fn create_rectangle<Opts>( &self,
        x1: c_double, y1: c_double, x2: c_double, y2: c_double,
        opts: impl Into<PathOptsWidgets<Opts,()>>
    )
        -> InterpResult<ItemId>
        where Opts: IntoHomoTuple<TkCanvasRectangleOpt>
                  + IntoHomoTuple<OptPair>
    {
        let mut command = Vec::<Obj>::with_capacity( <<Opts as IntoHomoTuple<OptPair>>::Output as tuplex::Len>::LEN * 2 + 7 );
        command.push( self.path.into() );
        command.push( "create".into() );
        command.push( "rectangle".into() );
        command.push( x1.into() );
        command.push( y1.into() );
        command.push( x2.into() );
        command.push( y2.into() );
        crate::cmd::append_opts( &mut command, opts.into().opts );
        Ok( ItemId( self.tk().eval( command )?.to_string() ))
    }

    pub fn create_text<Opts>( &self, x: c_double, y: c_double, opts: impl Into<PathOptsWidgets<Opts,()>> )
        -> InterpResult<ItemId>
        where Opts: IntoHomoTuple<TkCanvasTextOpt>
                  + IntoHomoTuple<OptPair>
    {
        let mut command = Vec::<Obj>::with_capacity( <<Opts as IntoHomoTuple<OptPair>>::Output as tuplex::Len>::LEN * 2 + 5 );
        command.push( self.path.into() );
        command.push( "create".into() );
        command.push( "text".into() );
        command.push( x.into() );
        command.push( y.into() );
        crate::cmd::append_opts( &mut command, opts.into().opts );
        Ok( ItemId( self.tk().eval( command )?.to_string() ))
    }

    pub fn create_window<Opts>( &self, x: c_double, y: c_double, opts: impl Into<PathOptsWidgets<Opts,()>> )
        -> InterpResult<ItemId>
        where Opts: IntoHomoTuple<TkCanvasWindowOpt>
                  + IntoHomoTuple<OptPair>
    {
        let mut command = Vec::<Obj>::with_capacity( <<Opts as IntoHomoTuple<OptPair>>::Output as tuplex::Len>::LEN * 2 + 5 );
        command.push( self.path.into() );
        command.push( "create".into() );
        command.push( "window".into() );
        command.push( x.into() );
        command.push( y.into() );
        crate::cmd::append_opts( &mut command, opts.into().opts );
        Ok( ItemId( self.tk().eval( command )?.to_string() ))
    }

    pub fn dchars( &self, tag_or_id: impl Into<TagOrId>, index: impl Into<Index> ) -> InterpResult<()> {
        self.tk().run(( self.path, "dchars", tag_or_id.into(), index.into() ))
    }

    pub fn dchars_range( &self, tag_or_id: impl Into<TagOrId>, range: impl Into<TkRange<Index>> ) -> InterpResult<()> {
        let range = range.into();
        self.tk().run(( self.path, "dchars", tag_or_id.into(), range.start, range.end ))
    }

    pub fn delete<ListOfTagOrId,TupleOfTagOrId>( &self, list_of_tag_or_id: ListOfTagOrId ) -> InterpResult<()>
        where ListOfTagOrId: IntoHomoTuple<Obj>
            , <ListOfTagOrId as IntoHomoTuple<Obj>>::Output : Into<Obj>
    {
        self.tk().run(( "eval", self.path, "delete", list_of_tag_or_id.into_homo_tuple() ))
    }

    pub fn dtag( &self, tag_or_id: impl Into<TagOrId>, tag_to_delete: Option<ItemTag> ) -> InterpResult<()> {
        match tag_to_delete {
            Some( tag ) => self.tk().run(( self.path, "dtag", tag_or_id.into(), tag )),
            None        => self.tk().run(( self.path, "dtag", tag_or_id.into() )),
        }
    }

    pub fn find( &self, search_spec: SearchSpec ) -> InterpResult<Obj> {
        use SearchSpec::*;
        match search_spec {
            Above( tag_or_id ) => self.tk().eval(( self.path, "find", "above", tag_or_id )),
            All => self.tk().eval(( self.path, "find", "all" )),
            Below( tag_or_id ) => self.tk().eval(( self.path, "find", "below", tag_or_id )),
            Closest{ x, y, halo, start } => self.tk().eval(( self.path, "find", "closest", x, y, halo, start )),
            Enclosed{ x1, y1, x2, y2 } => self.tk().eval(( self.path, "find", "enclosed", x1, y1, x2, y2 )),
            Overlapping{ x1, y1, x2, y2 } => self.tk().eval(( self.path, "find", "overlapping", x1, y1, x2, y2 )),
            WithTag( tag_or_id ) => self.tk().eval(( self.path, "find", "withtag", tag_or_id )),
        }
    }

    pub fn focus( &self ) -> InterpResult<Option<ItemId>> {
        let item_id = self.tk().eval(( self.path, "focus" ))?.to_string();
        if item_id.is_empty() {
            Ok( None )
        } else {
            Ok( Some( ItemId( item_id )))
        }
    }

    pub fn set_focus( &self, tag_or_id: impl Into<TagOrId> ) -> InterpResult<()> {
        self.tk().run(( self.path, "focus", tag_or_id.into() ))
    }

    pub fn clear_focus( &self ) -> InterpResult<()> {
        self.tk().run(( self.path, "focus", "" ))
    }

    #[cex]
    pub fn gettags( &self, tag_or_id: impl Into<TagOrId> ) -> Result!( Vec<ItemTag> throws InterpError, NotList ) {
        let obj = self.tk().eval(( self.path, "gettags", tag_or_id.into() ))?;
        Ok( obj .get_elements()?
                .map( |obj| ItemTag( obj.to_string() ))
                .collect::<Vec<_>>() )
    }

    pub fn icursor( &self, tag_or_id: impl Into<TagOrId>, index: Index ) -> InterpResult<()> {
        self.tk().run(( self.path, "icursor", tag_or_id.into(), index ))
    }

    pub fn imove( &self, tag_or_id: impl Into<TagOrId>, index: Index, x: c_double, y: c_double ) -> InterpResult<()> {
        self.tk().run(( self.path, "imove", tag_or_id.into(), index, x, y ))
    }

    pub fn index( &self, tag_or_id: impl Into<TagOrId>, index: Index ) -> InterpResult<c_int> {
        let obj = self.tk().eval(( self.path, "index", tag_or_id.into(), index ))?;
        self.tk().int( obj )
    }

    pub fn insert( &self, tag_or_id: impl Into<TagOrId>, before_this: Index, to_insert: impl Into<Obj> ) -> InterpResult<()> {
        self.tk().run(( self.path, "insert", tag_or_id.into(), before_this, to_insert ))
    }

    pub fn itemcget<Opt,Val>( &self, tag_or_id: impl Into<TagOrId>, _name_fn: fn(Val)->Opt ) -> InterpResult<Obj>
        where Opt : TkOption
                  + Into<TkCanvasItemOpt>
            , Val : Into<Obj>
    {
        self.tk().eval(( self.path, "itemcget", tag_or_id.into(), <Opt as TkOption>::NAME ))
    }

    pub fn itemconfigure<Opts>( &self, tag_or_id: impl Into<TagOrId>, opts: impl Into<PathOptsWidgets<Opts,()>> ) -> InterpResult<()>
        where Opts: IntoHomoTuple<TkCanvasItemOpt>
                  + IntoHomoTuple<OptPair>
    {
        let mut command = Vec::<Obj>::with_capacity( <<Opts as IntoHomoTuple<OptPair>>::Output as tuplex::Len>::LEN * 2 + 3 );
        command.push( self.path.into() );
        command.push( "itemconfigure".into() );
        command.push( tag_or_id.into().into() );
        crate::cmd::append_opts( &mut command, opts.into().opts );
        self.tk().run( command )
    }

    pub fn lower( &self, tag_or_id: impl Into<TagOrId>, below_this: Option<TagOrId> ) -> InterpResult<()> {
        match below_this {
            Some( below_this ) => self.tk().run(( self.path, "lower", tag_or_id.into(), below_this )),
            None => self.tk().run(( self.path, "lower", tag_or_id.into() )),
        }
    }

    pub fn move_( &self, tag_or_id: impl Into<TagOrId>, x_amount: c_double, y_amount: c_double ) -> InterpResult<()> {
        self.tk().run(( self.path, "move", tag_or_id.into(), x_amount, y_amount ))
    }

    pub fn move_to( &self, tag_or_id: impl Into<TagOrId>, x_pos: c_double, y_pos: c_double ) -> InterpResult<()> {
        self.tk().run(( self.path, "moveto", tag_or_id.into(), x_pos, y_pos ))
    }

    pub fn postscript<Opts>( &self, opts: impl Into<PathOptsWidgets<Opts,()>> ) -> InterpResult<Obj>
        where Opts: IntoHomoTuple<TkCanvasPostscriptOpt>
                  + IntoHomoTuple<OptPair>
    {
        let mut command = Vec::<Obj>::with_capacity( <<Opts as IntoHomoTuple<OptPair>>::Output as tuplex::Len>::LEN * 2 + 2 );
        command.push( self.path.into() );
        command.push( "postscript".into() );
        crate::cmd::append_opts( &mut command, opts.into().opts );
        self.tk().eval( command )
    }

    pub fn raise( &self, tag_or_id: impl Into<TagOrId>, above_this: Option<TagOrId> ) -> InterpResult<()> {
        match above_this {
            Some( above_this ) => self.tk().run(( self.path, "raise", tag_or_id.into(), above_this )),
            None => self.tk().run(( self.path, "raise", tag_or_id.into() )),
        }
    }

    pub fn rchars( &self, tag_or_id: impl Into<TagOrId>, first: Index, last: Index, to_replace: impl Into<Obj> ) -> InterpResult<()> {
        self.tk().run(( self.path, "rchars", tag_or_id.into(), first, last, to_replace ))
    }

    pub fn scale( &self, tag_or_id: impl Into<TagOrId>, x_origin: c_double, y_origin: c_double, x_scale: c_double, y_scale: c_double )
        -> InterpResult<()>
    {
        self.tk().run(( self.path, "scale", tag_or_id.into(), x_origin, y_origin, x_scale, y_scale ))
    }

    pub fn scan_mark( &self, x: c_int, y: c_int ) -> InterpResult<()> {
        self.0.tk().run(( self.0.path, "scan", "mark", x, y ))
    }

    pub fn scan_dragto( &self, x: c_int, y: c_int, gain: Option<c_int> ) -> InterpResult<()> {
        match gain {
            Some( gain ) => self.tk().run(( self.path, "scan", "dragto", x, y, gain )),
            None         => self.tk().run(( self.path, "scan", "dragto", x, y )),
        }
    }

    pub fn select_adjust( &self, tag_or_id: impl Into<TagOrId>, index: Index ) -> InterpResult<()> {
        self.tk().run(( self.path, "select", "adjust", tag_or_id.into(), index ))
    }

    pub fn select_clear( &self ) -> InterpResult<()> {
        self.tk().run(( self.path, "select", "clear" ))
    }

    pub fn select_from( &self, tag_or_id: impl Into<TagOrId>, index: Index ) -> InterpResult<()> {
        self.tk().run(( self.path, "select", "from", tag_or_id.into(), index ))
    }

    pub fn select_item( &self ) -> InterpResult<Option<ItemId>> {
        let result = self.tk().eval(( self.path, "select", "item", ))?.to_string();
        if result.is_empty() {
            Ok( None )
        } else {
            Ok( Some( ItemId( result )))
        }
    }

    pub fn select_to( &self, tag_or_id: impl Into<TagOrId>, index: Index ) -> InterpResult<()> {
        self.tk().run(( self.path, "select", "to", tag_or_id.into(), index ))
    }

    pub fn type_( &self, tag_or_id: impl Into<TagOrId> ) -> InterpResult<Option<ItemType>> {
        let result = self.tk().eval(( self.path, "type", tag_or_id.into() ))?.to_string();
        if result.is_empty() {
            Ok( None )
        } else {
            Ok( Some( <ItemType as FromStr>::from_str( &result ).unwrap() ))
        }
    }

    #[cex]
    pub fn xview( &self ) -> Result!( (c_double, c_double) throws DeError, InterpError ) {
        let obj = self.tk().eval(( self.path, "xview" ))?;
        Ok( from_obj::<(c_double, c_double)>( obj )? )
    }

    pub fn xview_moveto( &self, fraction: c_double ) -> InterpResult<()> {
        self.tk().run(( self.path, "xview", "moveto", fraction ))
    }

    pub fn xview_scroll_units( &self, number: c_int ) -> InterpResult<()> {
        self.tk().run(( self.path, "xview", "scroll", number, "units" ))
    }

    pub fn xview_scroll_pages( &self, number: c_int ) -> InterpResult<()> {
        self.tk().run(( self.path, "xview", "scroll", number, "units" ))
    }

    #[cex]
    pub fn yview( &self ) -> Result!( (c_double, c_double) throws DeError, InterpError ) {
        let obj = self.tk().eval(( self.path, "yview" ))?;
        Ok( from_obj::<(c_double, c_double)>( obj )? )
    }

    pub fn yview_moveto( &self, fraction: c_double ) -> InterpResult<()> {
        self.tk().run(( self.path, "yview", "moveto", fraction ))
    }

    pub fn yview_scroll_units( &self, number: c_int ) -> InterpResult<()> {
        self.tk().run(( self.path, "yview", "scroll", number, "units" ))
    }

    pub fn yview_scroll_pages( &self, number: c_int ) -> InterpResult<()> {
        self.tk().run(( self.path, "yview", "scroll", number, "units" ))
    }
}