Geo-Vector
view release on metacpan or search on metacpan
lib/Geo/Vector/Layer/Dialogs/Open.pm view on Meta::CPAN
package Geo::Vector::Layer::Dialogs::Open;
# @brief
use strict;
use warnings;
use Carp;
use Encode;
use Glib qw/TRUE FALSE/;
use Gtk2::Ex::Geo::Dialogs qw/:all/;
use Geo::Vector::Layer::Dialogs qw/:all/;
require Win32::DriveInfo if $Config::Config{'osname'} eq 'MSWin32';
## @ignore
sub open {
my($gui) = @_;
my $self = { gui => $gui };
# bootstrap:
my($dialog, $boot) = Gtk2::Ex::Geo::Layer::bootstrap_dialog
($self, $gui, 'open_dialog', "Open vector layer",
{
open_dialog => [delete_event => \&cancel_open_vector, $self],
open_vector_delete_datasource_button => [clicked => \&delete_data_source, $self],
open_vector_connect_datasource_button => [clicked => \&connect_data_source, $self],
open_vector_remove_button => [clicked => \&remove_layer, $self],
open_vector_schema_button => [clicked => \&show_schema, $self],
open_vector_cancel_button => [clicked => \&cancel_open_vector, $self],
open_vector_ok_button => [clicked => \&open_vector, $self],
open_vector_auto_update_schema_checkbutton => [toggled => \&show_schema, $self],
},
[
'open_vector_driver_combobox',
'filesystem_driver_combobox',
'open_vector_datasource_combobox'
]
);
if ($boot) {
my $combo = $dialog->get_widget('open_vector_driver_combobox');
my $model = $combo->get_model();
for my $driver (Geo::OGR::Drivers()) {
my @t = $driver->DataSourceTemplate;
next if $t[0] eq '<filename>';
my $n = $driver->FormatName;
$model->set ($model->append, 0, $n);
}
$combo->set_active(0);
$combo = $dialog->get_widget('filesystem_driver_combobox');
$model = $combo->get_model();
$model->set ($model->append, 0, 'auto');
for my $driver (Geo::OGR::Drivers()) {
my $n = $driver->GetName;
$model->set ($model->append, 0, $n);
}
$combo->set_active(0);
$dialog->get_widget('open_vector_datasource_combobox')
->signal_connect(changed => sub { empty_layer_data($_[1]) }, $self);
$dialog->get_widget('open_vector_build_connection_button')
->signal_connect(clicked => \&build_data_source, $self);
fill_named_data_sources_combobox($self);
my $treeview = $dialog->get_widget('open_vector_directory_treeview');
$treeview->set_model(Gtk2::TreeStore->new('Glib::String'));
my $cell = Gtk2::CellRendererText->new;
my $col = Gtk2::TreeViewColumn->new_with_attributes('', $cell, markup => 0);
$treeview->append_column($col);
$treeview->signal_connect(
button_press_event => sub
{
my($treeview, $event, $self) = @_;
select_directory($self, $treeview) if $event->type =~ /^2button/;
return 0;
}, $self);
$treeview->signal_connect(
key_press_event => sub
{
my($treeview, $event, $self) = @_;
select_directory($self, $treeview) if $event->keyval == $Gtk2::Gdk::Keysyms{Return};
return 0;
}, $self);
$treeview = $dialog->get_widget('open_vector_layer_treeview');
$treeview->set_model(Gtk2::TreeStore->new(qw/Glib::String Glib::String/));
$treeview->get_selection->set_mode('multiple');
my $i = 0;
for my $column ('layer', 'geometry') {
my $cell = Gtk2::CellRendererText->new;
my $col = Gtk2::TreeViewColumn->new_with_attributes($column, $cell, text => $i++);
$treeview->append_column($col);
}
$treeview->signal_connect(cursor_changed => \&on_layer_treeview_cursor_changed, $self);
$treeview = $dialog->get_widget('open_vector_property_treeview');
$treeview->set_model(Gtk2::TreeStore->new(qw/Glib::String Glib::String/));
$i = 0;
foreach my $column ('property', 'value') {
my $cell = Gtk2::CellRendererText->new;
if ($column eq 'value') {
$cell->set(wrap_width => 400);
$cell->set(wrap_mode => 'word');
}
my $col = Gtk2::TreeViewColumn->new_with_attributes($column, $cell, text => $i++);
$treeview->append_column($col);
}
$treeview = $dialog->get_widget('open_vector_schema_treeview');
$treeview->set_model(Gtk2::TreeStore->new(qw/Glib::String Glib::String/));
$i = 0;
foreach my $column ('field', 'type') {
my $cell = Gtk2::CellRendererText->new;
my $col = Gtk2::TreeViewColumn->new_with_attributes($column, $cell, text => $i++);
$treeview->append_column($col);
}
$self->{directory_toolbar} = [];
my $entry = $dialog->get_widget('open_vector_SQL_entry');
$entry->signal_connect(key_press_event => sub {
my($entry, $event, $history) = @_;
my $key = $event->keyval;
if ($key == $Gtk2::Gdk::Keysyms{Up}) {
$entry->set_text($history->arrow_up);
return 1;
} elsif ($key == $Gtk2::Gdk::Keysyms{Down}) {
$entry->set_text($history->arrow_down);
return 1;
}
}, $self->{gui}{history});
$entry->signal_connect(changed => \&on_SQL_entry_changed, $self);
}
$self->{path} = $gui->{folder} if $gui->{folder};
$self->{path} = File::Spec->rel2abs('.') unless $self->{path};
fill_directory_treeview($self);
fill_layer_treeview($self);
$dialog->get_widget('open_vector_update_checkbutton')->set_active(0);
}
## @ignore
sub fill_named_data_sources_combobox {
my($self, $default) = @_;
$default = '' unless $default;
my $model = Gtk2::ListStore->new('Glib::String');
$model->set($model->append, 0, '');
my $i = 1;
my $active = 0;
for my $data_source (sort keys %{$self->{gui}{resources}{datasources}}) {
$model->set ($model->append, 0, $data_source);
$active = $i if $data_source eq $default;
$i++;
}
my $combo = $self->{open_dialog}->get_widget('open_vector_datasource_combobox');
$combo->set_model($model);
$combo->set_active($active);
}
## @ignore
sub get_data_source {
my $self = shift;
my $combo = $self->{open_dialog}->get_widget('open_vector_datasource_combobox');
my $active = $combo->get_active();
my $data_source = $self->{path};
return ('', $data_source) if $active < 0;
my $model = $combo->get_model;
my $iter = $model->get_iter_from_string($active);
my $driver = $model->get($iter, 0);
if ($driver eq '') {
my $d = get_value_from_combo($self->{open_dialog}, 'filesystem_driver_combobox');
$driver = $d unless $d eq 'auto';
#if ($driver eq 'GeoJSON') { # hack
# $data_source = "file:/$data_source";
#}
return ($driver, $data_source);
}
return @{$self->{gui}{resources}{datasources}{$driver}};
}
##@ignore
sub open_vector {
my($button, $self) = @_;
my $dialog = $self->{open_dialog};
$self->{gui}->{folder} = $self->{path};
my($driver, $data_source) = get_data_source($self);
my $sql = $dialog->get_widget('open_vector_SQL_entry')->get_text;
$sql =~ s/^\s+//;
$sql =~ s/\s+$//;
$self->{gui}{history}->editing($sql);
unless ($driver) {
my $d = get_value_from_combo($dialog, 'filesystem_driver_combobox');
$driver = $d unless $d eq 'auto';
}
my $layers = get_selected_from_selection($dialog->get_widget('open_vector_layer_treeview')->get_selection);
if ($sql) {
$self->{gui}{history}->enter();
$dialog->get_widget('open_vector_SQL_entry')->set_text('');
}
my $wish = $dialog->get_widget('open_vector_layer_name_entry')->get_text;
my $update = $dialog->get_widget('open_vector_update_checkbutton')->get_active;
my $hidden = $dialog->get_widget('open_vector_open_hidden_button')->get_active;
for my $name (keys %$layers) {
my $layer;
my $encoding = 'utf8' if $data_source =~ /^Pg:/; # not really the case always but...
eval {
my %params = ( data_source => $data_source,
open => $name,
sql => $sql,
update => $update,
encoding => $encoding );
$params{driver} = $driver unless $driver eq 'auto';
$layer = Geo::Vector::Layer->new(%params);
};
if ($@) {
my $err = $@;
if ($err) {
$err =~ s/\n/ /g;
$err =~ s/\s+$//;
$err =~ s/\s+/ /g;
$err =~ s/^\s+$//;
} else {
$err = "data_source=$data_source, layer=$name, sql=$sql, update=$update";
}
$self->{gui}->message("Could not open layer: $err");
return;
}
$name = $wish if (keys %$layers) == 1;
$layer->visible(0) if $hidden;
$self->{gui}->add_layer($layer, $name, 1);
}
$self->{gui}{tree_view}->set_cursor(Gtk2::TreePath->new(0));
$self->{gui}{overlay}->render;
delete $self->{directory_toolbar};
$dialog->get_widget('open_dialog')->destroy;
}
##@ignore
sub cancel_open_vector {
my $self = pop;
delete $self->{directory_toolbar};
$self->{open_dialog}->get_widget('open_dialog')->destroy;
}
##@ignore
sub remove_layer {
my($button, $self) = @_;
my($driver, $data_source) = get_data_source($self);
my $layers = get_selected_from_selection(
lib/Geo/Vector/Layer/Dialogs/Open.pm view on Meta::CPAN
##@ignore
sub fill_directory_treeview {
my $self = shift;
my $treeview = $self->{open_dialog}->get_widget('open_vector_directory_treeview');
my $model = $treeview->get_model;
$model->clear;
my $toolbar = $self->{open_dialog}->get_widget('open_vector_directory_toolbar');
for (@{$self->{directory_toolbar}}) {
$toolbar->remove($_);
}
$self->{directory_toolbar} = [];
if ($self->{path} eq '') {
@{$self->{dir_list}} = ();
my @d = Win32::DriveInfo::DrivesInUse();
#my $fso = Win32::OLE->new('Scripting.FileSystemObject');
#for ( in $fso->Drives ) {
# push @d, $_->{DriveLetter}.':';
#}
for (@d) {
$_ .= ':';
}
for (@d) {
s/\\$//;
push @{$self->{dir_list}},$_;
}
@{$self->{dir_list}} = reverse @{$self->{dir_list}} if $self->{dir_list};
for my $i (0..$#{$self->{dir_list}}) {
my $iter = $model->insert (undef, 0);
$model->set ($iter, 0, $self->{dir_list}->[$i] );
}
$self->{open_dialog}->get_widget('open_vector_directory_treeview')->set_cursor(Gtk2::TreePath->new(0));
@{$self->{dir_list}} = reverse @{$self->{dir_list}} if $self->{dir_list};
return;
}
my($volume, $directories, $file) = File::Spec->splitpath($self->{path}, 1);
$self->{volume} = $volume;
my @dirs = File::Spec->splitdir($directories);
unshift @dirs, File::Spec->rootdir();
if ($^O eq 'MSWin32') {
unshift @dirs, $volume;
}
for (reverse @dirs) {
next if /^\s*$/;
my $filename;
eval {
$filename = Glib->filename_to_unicode($_);
};
next if $@;
#my $label = Gtk2::Label->new($filename) if Gtk2->CHECK_VERSION(2, 18, 0);
my $label = Gtk2::Label->new($filename) unless $Config::Config{'osname'} eq 'MSWin32';
my $b = Gtk2::ToolButton->new($label, $filename);
$b->signal_connect(
clicked => sub {
my($button, $self) = @_;
$self->{open_dialog}->get_widget('open_vector_datasource_combobox')->set_active(0);
my $n = $button->get_label;
if ($n eq $self->{volume}) {
$self->{path} = '';
} else {
my @directories;
for (reverse @{$self->{directory_toolbar}}) {
push @directories, $_->get_label;
last if $_ == $_[0];
}
if ($^O eq 'MSWin32') {
shift @directories; # remove volume
}
my $directory = File::Spec->catdir(@directories);
$self->{path} = File::Spec->catpath($self->{volume}, $directory, '');
}
fill_directory_treeview($self);
fill_layer_treeview($self);
},
$self);
#$label->show;
$b->show_all;
$toolbar->insert($b,0);
push @{$self->{directory_toolbar}}, $b;
}
@{$self->{dir_list}} = ();
my @files;
eval {
@files = Geo::GDAL::ReadDir($self->{path});
};
@dirs = ();
my @fs;
for (sort {$b cmp $a} @files) {
my $test = File::Spec->catfile($self->{path}, $_);
my $m;
eval {
($m) = Geo::GDAL::Stat($test);
};
next if $@;
my $is_dir = $m eq 'd';
next if (/^\./ and not $_ eq File::Spec->updir);
next if $_ eq File::Spec->curdir;
s/&/&/g;
s/</</g;
s/>/>/g;
if ($is_dir) {
push @dirs, "<b>[$_]</b>";
} else {
push @fs, $_;
}
}
for (@fs) {
push @{$self->{dir_list}}, $_;
}
for (@dirs) {
push @{$self->{dir_list}}, $_;
}
# in a file
lib/Geo/Vector/Layer/Dialogs/Open.pm view on Meta::CPAN
my @layers = sort {$b cmp $a} keys %$layers;
if (@layers) {
for my $name (@layers) {
my $iter = $model->insert (undef, 0);
my $u;
eval {
$u = Glib->filename_to_unicode($name);
};
next if $@;
$model->set ($iter, 0, $u, 1, $layers->{$name});
}
$treeview->set_cursor(Gtk2::TreePath->new(0));
}
else {
my $iter = $model->insert (undef, 0);
$model->set ($iter, 0, "no layers found", 1, "");
unless ($@ =~ /no reason given/) {
$@ =~ s/RuntimeError\s+//;
$@ =~ s/FATAL:\s+(\w)/uc($1)/e;
$@ =~ s/\s+at\s+\w+\.\w+\s+line\s+\d+\s+//;
$model->set ($model->append(undef), 0, $@, 1, "");
}
}
on_layer_treeview_cursor_changed($treeview, $self);
return @layers > 0;
}
## @ignore
sub on_SQL_entry_changed {
my($entry, $self) = @_;
my $sql = $entry->get_text;
$sql =~ s/^\s+//;
$sql =~ s/\s+$//;
$self->{open_dialog}->get_widget('open_vector_layer_name_entry')->set_text('SQL') if $sql;
}
## @ignore
sub on_layer_treeview_cursor_changed {
my($treeview, $self) = @_;
my($path, $focus_column) = $treeview->get_cursor;
if ($path) {
my $model = $treeview->get_model;
my $iter = $model->get_iter($path);
my $layer_name = $model->get($iter, 0);
$self->{open_dialog}->get_widget('open_vector_layer_name_entry')->set_text($layer_name);
}
if ($self->{open_dialog}->get_widget('open_vector_auto_update_schema_checkbutton')->get_active()) {
show_schema(undef, $self);
} else {
$self->{open_dialog}->get_widget('open_vector_property_treeview')->get_model->clear;
$self->{open_dialog}->get_widget('open_vector_schema_treeview')->get_model->clear;
$self->{open_dialog}->get_widget('open_vector_schema_label')->set_label('');
}
$self->{gui}{history}->editing('');
$self->{open_dialog}->get_widget('open_vector_SQL_entry')->set_text('');
}
## @ignore
sub build_data_source {
my($button, $self) = @_;
my $combo = $self->{open_dialog}->get_widget('open_vector_driver_combobox');
my $index = $combo->get_active;
my $code = '';
my $format;
my $template = '';
my $help = '';
my $i = -1;
for my $driver (Geo::OGR::Drivers()) {
($template, $help) = $driver->DataSourceTemplate;
next if $template eq '<filename>';
$i++;
next unless $i == $index;
$code = $driver->GetName;
$format = $driver->FormatName;
last;
}
my @template = split(/[\[\]]/, $template);
#print STDERR "build $code data source, t = $template\n";
# ask from user the name for the new data source, and things defined by the template
my $data_source_name;
my %input;
my @ask;
$i = 0;
for my $c (@template) {
my @c = $c =~ /\<(\w+)\>/;
if ($i % 2 == 1) { # optional
} else {
for (@c) {
$_ .= '*';
}
}
push @ask, @c;
$i++;
}
my $dialog = Gtk2::Dialog->new('Build a non-file data source',
$self->{open_dialog}->get_widget('open_dialog'),
'destroy-with-parent',
'gtk-cancel' => 'reject',
'gtk-ok' => 'ok');
my $vbox = Gtk2::VBox->new(FALSE, 0);
$vbox->pack_start(Gtk2::Label->new("Define a connection to a $format data source"), FALSE, FALSE, 0);
my $table = Gtk2::Table->new(1+@ask, 2, TRUE);
$table->attach(Gtk2::Label->new("Unique name for the data source*:"), 0, 1, 0, 1, 'fill', 'fill', 0, 0);
my $e = Gtk2::Entry->new();
$e->set_name('data_source_name');
$table->attach($e, 1, 2, 0, 1, 'fill', 'fill', 0, 0);
$i = 1;
for my $a (@ask) {
my $l = Gtk2::Label->new($a.":");
$l->set_justify('left');
$table->attach($l, 0, 1, $i, $i+1, 'expand', 'fill', 0, 0);
$e = Gtk2::Entry->new();
$a =~ s/\*$//;
$e->set_name($a);
$table->attach($e, 1, 2, $i, $i+1, 'fill', 'fill', 0, 0);
$i++;
}
lib/Geo/Vector/Layer/Dialogs/Open.pm view on Meta::CPAN
$dialog->signal_connect(response => \&add_data_source, [$self, $template, $code]);
$dialog->show_all;
}
## @ignore
sub get_entries {
my($widget, $entries) = @_;
if ($widget->isa('Gtk2::Container')) {
$widget->foreach(\&get_entries, $entries);
} elsif ($widget->isa('Gtk2::Entry')) {
my $n = $widget->get_name;
my $t = $widget->get_text;
if ($n and $t) {
$entries->{$n} = $t;
}
}
}
## @ignore
sub add_data_source {
my($dialog, $response, $x) = @_;
unless ($response eq 'ok') {
$dialog->destroy;
return;
}
my($self, $template, $driver) = @$x;
my %input;
get_entries($dialog, \%input);
my @template = split(/[\[\]]/, $template);
# build connection string;
my $connection_string = '';
# at indexes 1,3,.. the contents are optional
my $i = 0;
for my $c (@template) {
my @c = $c =~ /\<(\w+)\>/;
my $got_input = 0;
for my $k (keys %input) {
for my $p (@c) {
$got_input = 1 if $k eq $p;
}
$c =~ s/\<$k\>/$input{$k}/;
}
if ($i % 2 == 1) { # optional
if ($got_input) {
$connection_string .= $c;
}
} else {
$connection_string .= $c;
}
$i++;
}
#print STDERR "connection string: $connection_string\n";
$self->{gui}{resources}{datasources}{$input{data_source_name}} = [$driver, $connection_string];
fill_named_data_sources_combobox($self, $input{data_source_name});
# Ensure that the dialog box is destroyed when the user responds.
$dialog->destroy;
}
## @ignore
sub delete_data_source {
my($button, $self) = @_;
my $combo = $self->{open_dialog}->get_widget('open_vector_datasource_combobox');
my $active = $combo->get_active();
return if $active < 0;
my $model = $combo->get_model;
my $iter = $model->get_iter_from_string($active);
my $name = $model->get($iter, 0);
return if $name eq '';
$model->remove($iter);
delete $self->{gui}{resources}{datasources}{$name};
}
## @ignore
sub connect_data_source {
my($button, $self) = @_;
unless (fill_layer_treeview($self)) {
# No layers found in data source
fill_directory_treeview($self);
}
}
## @ignore
sub select_directory {
my($self, $treeview) = @_;
my $combo = $self->{open_dialog}->get_widget('open_vector_driver_combobox');
$combo->set_active(0) if $combo->get_active;
$self->{open_dialog}->get_widget('open_vector_layer_treeview')->get_model->clear;
my($path, $focus_column) = $treeview->get_cursor;
my $index = $path->to_string if $path;
if (defined $index) {
my $dir = $self->{dir_list}->[$index];
$dir =~ s/^<b>\[//;
$dir =~ s/\]<\/b>$//;
my $directory;
if ($self->{path} eq '') {
$self->{volume} = $dir;
$directory = File::Spec->rootdir();
} else {
my @directories;
for (reverse @{$self->{directory_toolbar}}) {
push @directories, $_->get_label;
}
if ($^O eq 'MSWin32') {
shift @directories; # remove volume
}
if ($dir eq File::Spec->updir) {
pop @directories;
} else {
push @directories, $dir;
}
$directory = File::Spec->catdir(@directories);
}
$self->{path} = File::Spec->catpath($self->{volume}, $directory, '');
fill_directory_treeview($self);
fill_layer_treeview($self);
}
}
## @ignore
sub show_schema {
my($button, $self) = @_ == 2 ? @_ : (undef, $_[0]);
my $property_model = $self->{open_dialog}->get_widget('open_vector_property_treeview')->get_model;
$property_model->clear;
my $schema_model = $self->{open_dialog}->get_widget('open_vector_schema_treeview')->get_model;
$schema_model->clear;
my $label = '';
my $sql = $self->{open_dialog}->get_widget('open_vector_SQL_entry')->get_text;
my $vector;
if ($sql) {
eval {
$vector = Geo::Vector->new( data_source => $self->{_open_data_source},
sql => $sql );
};
croak("$@ Is the SQL statement correct?") if $@;
$label = 'Schema of the SQL query';
} else {
my $treeview = $self->{open_dialog}->get_widget('open_vector_layer_treeview');
my($path, $focus_column) = $treeview->get_cursor;
return unless $path;
( run in 1.338 second using v1.01-cache-2.11-cpan-8dfa8b56332 )