Config-Model-TkUI

 view release on metacpan or  search on metacpan

lib/Config/Model/TkUI.pm  view on Meta::CPAN


        # snatched from oxygen-icon-theme
        $warn_img = $cw->Photo( -file => $icon_path . 'dialog-warning.png' );

        # snatched from openclipart-png
        $tool_img = $cw->Photo( -file => $icon_path . 'tools_nicu_buculei_01.png' );

        # snatched from gnome gnome-icon-theme package
        foreach my $img_name (qw/next previous window-close gtk-execute/) {
            $gnome_img{$img_name} = $cw->Photo(
                -file => $icon_path . "gnome-$img_name.png"
            );
        }
    }

    if ($args->{-root}) {
        carp "TkUI: -root parameter is deprecated in favor of -instance";
        my $root = delete $args->{-root};
        $cw->{instance} = $root->instance;
    }

    $cw->{instance} //= delete $args->{-instance};

    foreach my $parm (qw/-store_sub -quit/) {
        my $attr = $parm;
        $attr =~ s/^-//;
        $cw->{$attr} = delete $args->{$parm};
    }

    my $extra_menu = delete $args->{'-extra-menu'} || [];

    my $title = delete $args->{'-title'}
        || $0 . " " . $cw->{instance}->config_root->config_class_name;

    # check unknown parameters
    croak "Unknown parameter ", join( ' ', keys %$args ) if %$args;

    # initialize internal attributes
    $cw->{location} = '';
    $cw->{current_mode} = 'view';

    $cw->setup_scanner();

    # create top menu
    require Tk::Menubutton;
    my $menubar = $cw->Menu;
    $cw->configure( -menu => $menubar );
    $cw->{my_menu} = $menubar;

    $cw->add_file_menu($menubar, $extra_menu);
    $cw->add_help_menu($menubar);

    $cw->bind( '<Control-s>', sub { $cw->save } );
    $cw->bind( '<Control-q>', sub { $cw->quit } );
    $cw->bind( '<Control-c>', sub { $cw->edit_copy } );
    $cw->bind( '<Control-v>', sub { $cw->edit_paste } );
    $cw->bind( '<Control-f>', sub { $cw->pack_find_widget } );

    $cw->add_edit_menu($menubar);

    my $history_menu = $menubar->cascade(-label => 'History');

    my $option_menu = $menubar->cascade( -label => 'Options');
    $option_menu->command( -label => 'Font', -command => sub { $cw->set_font(); });

    # create 'hide empty values'
    $cw->{hide_empty_values} = 0;
    $option_menu->checkbutton(
        -label  => "Hide empty values",
        -variable => \$cw->{hide_empty_values},
        -command  => sub { $cw->reload($cw->{location}) },
    );

    # create 'show only custom values'
    $cw->{show_only_custom} = 0;
    $option_menu->checkbutton(
        -label => 'Show only custom values',
        -variable => \$cw->{show_only_custom},
        -command  => sub { $cw->reload($cw->{location}) },
    );

    # create 'show only custom values'
    $cw->{auto_save_mode} = 0;
    $option_menu->checkbutton(
        -label => 'Auto save',
        -variable => \$cw->{auto_save_mode},
    );

    my $weak_cw = $cw;
    weaken($weak_cw);
    $cw->{instance}->on_change_cb( sub {
        $weak_cw->save if $weak_cw->{auto_save_mode};;
    });

    # create frame for location entry
    my $loc_frame = $cw->Frame( -relief => 'sunken', -borderwidth => 1 )
        ->pack( -pady => 0, -fill => 'x' );
    $cw->{path_history} = [];
    $cw->{path_index} = 0;

    # add button
    my $previous_btn = $loc_frame->Button (
        -image => $gnome_img{'previous'},
        -state => 'disabled',
        -command => sub { $cw->go_to_previous();},
    );
    $previous_btn->pack(-side => 'left');
    my $next_btn = $loc_frame->Button (
        -image => $gnome_img{'next'},
        -state => 'disabled',
        -command => sub { $cw->go_to_next();},
    );
    $next_btn->pack(-side => 'left');

    $loc_frame->Label( -text => 'location :' )->pack( -side => 'left' );
    $loc_frame->Label( -textvariable => \$cw->{location} )->pack( -side => 'left' );

    # add bottom frame
    my $bottom_frame = $cw->Frame->pack(qw/-pady 0 -fill both -expand 1/);
    my $tree_frame = $bottom_frame->Frame->pack(qw/-fill both -expand 1 -side left/);
    my $filter_frame = $tree_frame->Frame->pack(qw/-fill x -side top/);

    # create the widget for tree navigation

lib/Config/Model/TkUI.pm  view on Meta::CPAN

    my ( $cw, $msg ) = @_;
    # $cw->Subwidget('msg_label')->configure(-background => "red"); # does not work
    $cw->{message} = $msg;

    if (my $id = $cw->{id}) {
        $cw->afterCancel($id) ;
    } ;

    my $unshow = sub {
        delete $cw->{id};
        $cw->{message} = '';
    } ;
    $cw->{id} = $cw->after(5000,$unshow) ;
}

sub tree_width {
    my ( $cw, $value ) = @_;
    $cw->Subwidget('tree')->configure( -width => $value );
}

sub tree_height {
    my ( $cw, $value ) = @_;
    $cw->Subwidget('tree')->configure( -height => $value );
}

my $parser = Pod::POM->new();

# parse from my documentation
my $pom = $parser->parse_file(__FILE__)
    || die $parser->error();

my $help_text;
my $info_text;
foreach my $head1 ( $pom->head1() ) {
    $help_text = Pod::POM::View::Text->view_head1($head1)
        if $head1->title eq 'USAGE';
    $info_text = Pod::POM::View::Text->view_head1($head1)
        if $head1->title =~ /more information/i;

}

sub add_file_menu($cw, $menubar, $extra_menu) {
    my $file_items = [
        [ qw/command wizard -command/, sub { $cw->wizard } ],
        [ command => 'redraw tree', -command => sub { $cw->reload } ],
        [ command => 'reload from file', -command => sub { $cw->ask_reset; } ],
        [ command => 'check for errors',     -command => sub { $cw->check(1) } ],
        [ command => 'check for warnings',   -command => sub { $cw->check( 1, 1 ) } ],
        [ command => 'show unsaved changes', -command => sub { $cw->show_changes; } ],
        [ command => 'save (Ctrl-s)', -command => sub { $cw->save } ],
        @$extra_menu,
        [
            command  => 'debug ...',
            -command => sub {
                require Tk::ObjScanner;
                Tk::ObjScanner::scan_object( $cw->{instance}->config_root );
            }
        ],
        [ command => 'quit (Ctrl-q)', -command => sub { $cw->quit } ],
    ];
    $menubar->cascade( -label => 'File', -menuitems => $file_items );

    return;
}

sub add_help_menu {
    my ( $cw, $menubar ) = @_;

    my $about_sub = sub {
        $cw->Dialog(
            -title => 'About',
            -text  => "Config::Model::TkUI \n"
                . "(c) 2008-2021 Dominique Dumont \n"
                . "Licensed under LGPLv2\n"
        )->Show;
    };

    my $info_sub = sub {
        $cw->CmeDialog(
            -title => 'TODO',
            -text =>  $info_text
        )->Show;
    };

    my $help_sub = sub {
        $cw->CmeDialog(
            -title => 'help',
            -text => $help_text
        )->Show;
    };

    my $class   = $cw->{instance}->config_root->config_class_name;
    my $man_sub = sub {
        $cw->Pod(
            -tree       => 0,
            -file       => "Config::Model::models::" . $class,
            -title      => $class,
            -exitbutton => 0,
        );
    };

    my $help_items = [
        [ qw/command About -command/, $about_sub ],
        [ qw/command Usage -command/, $help_sub ],
        [ command => 'More info',   -command => $info_sub ],
        [ command => "$class help", -command => $man_sub ],
    ];
    $menubar->cascade( -label => 'Help', -menuitems => $help_items );
}

sub add_edit_menu($cw, $menubar) {
    my $edit_items = [
        # [ qw/command cut   -command/, sub{ $cw->edit_cut }],
        [ command => 'copy (Ctrl-c)',  '-command', sub { $cw->edit_copy } ],
        [ command => 'paste (Ctrl-v)', '-command', sub { $cw->edit_paste } ],
        [ command => 'find (Ctrl-f)',  '-command', sub { $cw->pack_find_widget; } ],
    ];
    $menubar->cascade( -label => 'Edit', -menuitems => $edit_items );
    return;
}

# Note: this callback is called by Tk::Tree *before* changing the
# indicator. And the indicator is used by Tk::Tree to store the
# open/close/none mode. So we can't rely on getmode for path that are
# opening. Hence the parameter passed to the sub stored with each
# Tk::Tree item
sub open_item {
    my ( $cw, $path ) = @_;
    my $tktree = $cw->{tktree};
    $logger->trace("open_item on $path");
    my $data = $tktree->infoData($path);

    # invoke the scanner part (to create children)
    # the parameter indicates that we are opening this path
    $data->[0]->(1);

    $cw->show_single_list_value ($tktree, $data->[1], $path, 0);

    my @children = $tktree->infoChildren($path);
    $logger->trace("open_item show @children");
    map { $tktree->show( -entry => $_ ); } @children;
}

sub close_item {
    my ( $cw, $path ) = @_;
    my $tktree = $cw->{tktree};
    $logger->trace("close_item on $path");
    my $data = $tktree->infoData($path);

    $cw->show_single_list_value ($tktree, $data->[1], $path, 1);

    my @children = $tktree->infoChildren($path);
    $logger->trace("close_item hide @children");
    map { $tktree->hide( -entry => $_ ); } @children;
}

sub check {
    my $cw             = shift;
    my $show           = shift || 0;
    my $check_warnings = shift || 0;

    my $wiz = $cw->setup_wizard( sub { $cw->check_end( $show, @_ ); } );

    $wiz->start_wizard( stop_on_warning => $check_warnings );
}

sub check_end {
    my $cw          = shift;
    my $show        = shift;
    my $has_stopped = shift;

    $cw->reload if $has_stopped;

    if ( $show and not $has_stopped ) {
        $cw->Dialog(
            -title => 'Check',
            -text  => "No issue found"
        )->Show;

lib/Config/Model/TkUI.pm  view on Meta::CPAN

    }

    my $tree = $cw->{tktree};

    my $instance_name = $cw->{instance}->name;
    my $root = $cw->{instance}->config_root;

    my $new_drawing = not $tree->infoExists($instance_name);

    my $scan_root = sub {
        my $opening = shift ;
        $tree->itemConfigure($instance_name, 2, -text => $root->fetch_gist);
        $cw->{scanner}->scan_node( [ $instance_name, $cw, $opening, $actions, $force_display_path ], $root );
    };

    if ($new_drawing) {
        $tree->add( $instance_name, -data => [ $scan_root, $root ] );
        $tree->itemCreate( $instance_name, 0, -text => $instance_name, );
        $tree->itemCreate( $instance_name, 2, -text => '' );
        $tree->setmode( $instance_name, 'close' );
        $tree->open($instance_name);
    }

    # the first parameter indicates that we are opening the root
    $scan_root->( 1 );
    $cw->{editor}->reload if defined $cw->{editor};
}

# call-back when Tree element is selected
sub on_browse {
    my ( $cw, $path ) = @_;
    $cw->update_loc_bar($path);
    $cw->update_history($path);
    $cw->create_element_widget('view');
}

sub update_loc_bar {
    my ( $cw, $path ) = @_;

    #$cw->{path}=$path ;
    my $datar = $cw->{tktree}->infoData($path);
    my $obj   = $datar->[1];
    my $loc = $cw->{location} = $obj->location_short;
    return $loc;
}

sub update_history ($cw, $loc) {
    my $history = $cw->{path_history};

    # avoid consecutive duplicated entries
    if ($history->@* > 1 and $loc eq $history->[-1]) {
        return;
    }

    push $history->@*, $loc;
    my $path_idx = $cw->{path_index} = $history->$#*;

    # enable previous button when history has more than one item
    $cw->Subwidget('prev_btn')->configure(-state => $path_idx > 0 ? 'normal' : 'disabled');

    my $h_cascade = $cw->Subwidget('history');

    my $max_count = 20;
    $cw->{history_count} //= 0;

    if ($cw->{history_count}++ > $max_count) {
        # delete all history entries from the menu
        $h_cascade->menu->delete(0, 'end');

        # Add the last $max_count history entries to the menu
        for (my $i = 0; $i <= $max_count; $i++) {
            my $entry_idx = $path_idx - $i;
            $h_cascade->menu->add(
                'command',
                -label => $history->[$entry_idx],
                -command =>sub { $cw->go_to_loc($path_idx); }
            );
        }
    }
    else {
        # add a menu entry
        $h_cascade->command(
            -label => $loc,
            -command => sub { $cw->go_to_loc($path_idx); }
        );
    }

    return;
}

sub go_to_loc ($cw, $idx) {
    my $path = $cw->{path_history}[$idx];
    my $loc = $cw->update_loc_bar($path);
    # enable previous button when history has more than one item
    $cw->Subwidget('prev_btn')->configure(-state => $idx > 0 ? 'normal' : 'disabled');
    # when jumping into history, the next location does not make
    # sense, hence next button is disabled
    $cw->Subwidget('next_btn')->configure(-state => 'disabled');
    $cw->force_display($path, $loc);
    $cw->create_element_widget('view', $path);
}

sub go_to_previous ($cw) {
    my $idx = --$cw->{path_index};
    my $path = $cw->{path_history}[$idx];
    my $loc = $cw->update_loc_bar($path);
    $cw->Subwidget('prev_btn')->configure(-state => $idx > 0 ? 'normal' : 'disabled');
    $cw->Subwidget('next_btn')->configure(-state => 'normal');
    $cw->force_display($path, $loc);
    $cw->create_element_widget('view', $path);
}

sub go_to_next ($cw) {
    my $idx = ++$cw->{path_index};
    my $path = $cw->{path_history}[$idx];
    my $history_last_idx = $cw->{path_history}->$#*;

    my $loc = $cw->update_loc_bar($path);
    $cw->Subwidget('prev_btn')->configure(-state => 'normal');
    $cw->Subwidget('next_btn')->configure(-state => $idx < $history_last_idx ? 'normal' : 'disabled');
    $cw->force_display($path, $loc);
    $cw->create_element_widget('view', $path);
}

sub on_select {
    my ( $cw, $path ) = @_;
    $cw->update_loc_bar($path);
    $cw->update_history($path);
    $cw->create_element_widget('edit');
}

sub on_cut_buffer_dump {
    my ( $cw, $tree_path, $selection_for_test ) = @_;
    $cw->update_loc_bar($tree_path);
    $cw->update_history($tree_path);

    # get cut buffer content, See Perl/Tk book p297
    my $sel = $selection_for_test // eval { $cw->SelectionGet; };

    return if $@;    # no selection

    my $obj = $cw->{tktree}->infoData($tree_path)->[1];



( run in 0.525 second using v1.01-cache-2.11-cpan-5511b514fd6 )