# -*- perl -*- # # $Id: Telefonbuch.pm,v 1.33 2005/04/05 22:34:30 eserte Exp $ # Author: Slaven Rezic # # Copyright (C) 1998,1999 Slaven Rezic. All rights reserved. # This program is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. # # Mail: eserte@cs.tu-berlin.de # WWW: http://user.cs.tu-berlin.de/~eserte/ # # experimentell package Telefonbuch; require Exporter; use Telefonbuch99; use Telefonbuch2001; use BBBikeUtil; use Karte::GIS; use strict; use vars qw($VERBOSE $cdromdir $maxrec_str $maxrec_tel @EXPORT_OK @ISA $no_start_ziel $force $default_img_fmt $cgi_host $cgi_port $cgi_path ); @ISA = qw(Exporter); @EXPORT_OK = qw(tk_str_dialog tk_tel_dialog); *my_die = (defined &main::status_message ? sub { main::status_message($_[0], "die") } : sub { die $_[0] } ); *my_warn = (defined &main::status_message ? sub { main::status_message($_[0], "info") } : sub { warn $_[0] } ); use constant pagesize => 0x2000; use vars qw($Telefonbuch); $Telefonbuch = "Telefonbuch99"; #$Telefonbuch = "Telefonbuch2001"; my(@strasse_hist, @nachname_hist, @vorname_hist); my $str_base = "bstrd"; my $tel_base = "btp1d"; my %old_linear; my %l_found; my %res; my($map_toplevel, $map_canvas, $map_map, $map_mapx, $map_mapy); $cgi_host = ($main::devel_host ? "localhost" : "user.cs.tu-berlin.de") unless defined $cgi_host; $cgi_port = 80 unless defined $cgi_port; $cgi_path = "/~eserte/bbbike/cgi/berlinmap.cgi" unless defined $cgi_path; #warn join("\n", map { join(", ", @$_) } search_string($search)), "\n"; sub exists { my @cdrom_drives; if ($^O =~ /mswin32/i) { require Win32Util; @cdrom_drives = Win32Util::get_cdrom_drives(); if (!@cdrom_drives) { @cdrom_drives = qw(D: E: F:); } } else { require UnixUtil; @cdrom_drives = UnixUtil::get_cdrom_drives(); if (!@cdrom_drives) { @cdrom_drives = qw(/cdrom /mnt/cdrom /cd /CDROM); } } if (defined $cdromdir) { unshift @cdrom_drives, $cdromdir; } my @datadirs; push @datadirs, map { ("$_/database", "$_/32Bit/database") } @cdrom_drives; foreach (@datadirs) { if (-d $_) { if (-f "$_/${str_base}d00") { $Telefonbuch = "Telefonbuch98"; } elsif (-f "$_/" . $Telefonbuch99::strdbbasefile) { $Telefonbuch = "Telefonbuch99"; $Telefonbuch->set_dbroot($_); } elsif (-f "$_/" . $Telefonbuch2001::strdbbasefile) { $Telefonbuch = "Telefonbuch2001"; $Telefonbuch->set_dbroot($_); } return $_; } } my_warn("Unrecognized database files..."); undef; } sub check_exists { my($top) = @_; if (!Telefonbuch::exists() && !$force) { while (1) { # cdmount: meine eigene Spezialität my $has_cdmount = (is_in_path("cdmount")); require Tk::Dialog; my $ans = $top->Dialog (-title => 'Fehler', -text => 'Die Telefonbuch-CD-ROM ist anscheinend nicht ' . (defined $main::os and $main::os eq 'win' ? 'eingelegt' : 'gemountet') . '.' . "\nUnterstützte CD-ROMs: Berlin 1999/2000, Berlin 2000/2001 mit Spezialprogramm", -bitmap => 'error', -buttons => ['Weiter', ($has_cdmount ? ('CD einlegen') : ()), 'Abbrechen'], )->Show; if ($ans eq 'Abbrechen') { return undef; } elsif ($ans eq 'Weiter') { return 1; } system("cdmount e"); my $t = $top->Toplevel(-title => 'Warte auf CD...'); my $abbruch = 0; $t->bind('' => sub { $abbruch = 1 }); $t->Button(-text => 'Abbruch', -command => sub { $abbruch = 1 })->pack; my $r = undef; foreach (1..10) { system("cdmount"); if (Telefonbuch::exists()) { $r = 1; last; } $t->update; if ($abbruch) { last; } } $t->destroy; return $r; } } else { 1; } } my %start_button; my %ziel_button; my(%found_x, %found_y); # erzeugt ein Frame (ungepackt) für die Start/Ziel-Auswahl # $type: 'str' oder 'tel' sub start_ziel_frame { my($top, $type) = @_; my $f = $top->Frame(-bd => 2, -relief => 'ridge', ); $f->Label(-text => 'Als ')->pack(-side => 'left'); $start_button{$type} = $f->Button (-text => 'Start', -command => sub { set_start_ziel($f, $type, 'start') }, #XXX -state => 'disabled', )->pack(-side => 'left'); $f->Label(-text => ' / ')->pack(-side => 'left'); $ziel_button{$type} = $f->Button (-text => 'Ziel', -command => sub { set_start_ziel($f, $type, 'ziel') }, #XXX -state => 'disabled', )->pack(-side => 'left'); $f->Label(-text => 'markieren')->pack(-side => 'left'); $f; } # erzeugt ein Frame (ungepackt) für CGI- und Tk-Map-Auswahl sub map_frame { my($t, $type) = @_; my $f = $t->Frame(-bd => 2, -relief => 'ridge', ); $f->Label(-text => "Karte zeigen:")->pack(-side => "left"); cgi_map_frame($f, $type)->pack(-side => "left", -fill => "x"); tk_map_frame($f, $type)->pack(-side => "left", -fill => "x"); $f; } # erzeugt ein Frame (ungepackt) für die CGI-Map-Auswahl # $type: 'str' oder 'tel' sub cgi_map_frame { my($top, $type) = @_; # check for existing HTTP-Server on $cgi_host:$cgi_port require IO::Socket; my $inet = new IO::Socket::INET(PeerAddr => $cgi_host, PeerPort => $cgi_port, Proto => 'tcp'); if ($inet) { $inet->close; $top->Button(-text => 'CGI', -command => sub { show_cgi_map($top, $type) }, )->pack(-side => 'left'); } $top; } sub tk_map_frame { my($top, $type) = @_; $top->Button(-text => 'Tk', -command => sub { show_tk_map($top, $type) }, )->pack(-side => 'left'); $top; } # XXX besser: vorhandene Objekte benutzen sub new_kreuzungen { require Strassen; my $str; if ($main::str_obj{'s'}) { $str = $main::str_obj{'s'}; } else { $str = new Strassen $main::str_file{'s'}; } my($crossings) = $str->all_crossings(RetType => 'hash', UseCache => 1); my $kr = new Kreuzungen Hash => $crossings; # $kr->make_grid ist nicht mehr nötig $kr; } sub adjust_x_y { my $type = 'str'; if (defined $l_found{$type}->curselection && ref $res{$type} eq 'ARRAY' ) { my($i) = $l_found{$type}->curselection; ($found_x{$type}, $found_y{$type}) = ($res{$type}->[$i][4], $res{$type}->[$i][5]); } } sub set_start_ziel { my($top, $type, $start_ziel) = @_; adjust_x_y() if $type eq 'str'; if (!defined $found_x{$type} || !defined $found_y{$type}) { require Tk::Dialog; $top->Dialog (-title => 'Fehler', -text => 'Es kann kein ' . ucfirst($start_ziel) . 'punkt ausgewählt werden.', -bitmap => 'error')->Show; return undef; } my $kr = new_kreuzungen(); my(@nearest) = $kr->nearest($found_x{$type}, $found_y{$type}); if (@nearest) { if ($start_ziel eq 'start') { main::set_route_start($nearest[0]); } else { main::set_route_ziel($nearest[0]); } } else { undef; } } sub show_cgi_map { my($top, $type) = @_; adjust_x_y() if $type eq 'str'; if (!defined $found_x{$type} || !defined $found_y{$type}) { require Tk::Dialog; $top->Dialog (-title => 'Fehler', -text => 'Es kann kein Karte angezeigt werden.', -bitmap => 'error')->Show; return undef; } require Karte::Berlinmap1996; # XXX lib muß in @INC sein require WWWBrowser; my($mapx, $mapy, $mapxx, $mapyy) = $Karte::Berlinmap1996::obj->coord ($Karte::Berlinmap1996::obj->standard2map($found_x{$type}, $found_y{$type})); WWWBrowser::start_browser ("http://$cgi_host:${cgi_port}$cgi_path?x=$mapx&xx=$mapxx&y=$mapy&yy=$mapyy"); 1; } sub set_tk_map { my($c, $mapx, $mapy) = @_; if (!(defined $map_mapx and $map_mapx eq $mapx and defined $map_mapy and $map_mapy eq $mapy)) { if (defined $map_map) { $map_map->delete; undef $map_map; } $map_map = $c->Photo(-file => $Karte::Berlinmap1996::obj->filename ($mapx, $mapy)); $map_mapx = $mapx; $map_mapy = $mapy; $c->itemconfigure('map', -image => $map_map); $c->bind ('map', '' => sub { my $c = shift; my $e = $c->XEvent; my($x, $y) = ($c->canvasx($e->x), $c->canvasy($e->y)); if ($x < 105) { $mapx = $Karte::Berlinmap1996::obj->incx($mapx, -1); } elsif ($x > 745) { $mapx = $Karte::Berlinmap1996::obj->incx($mapx, 1); } if ($y < 105) { $mapy = $Karte::Berlinmap1996::obj->incy($mapy, -1); } elsif ($y > 705) { $mapy = $Karte::Berlinmap1996::obj->incy($mapy, 1); } $c->coords('flag', 0, 0); $map_toplevel->Busy; eval { set_tk_map($c, $mapx, $mapy); }; my_warn($@) if $@; $map_toplevel->Unbusy; } ); } } sub show_tk_map { my($top, $type) = @_; adjust_x_y() if $type eq 'str'; # XXX lib und images muß in @INC sein require Karte::Berlinmap1996; my($mapx, $mapy, $mapxx, $mapyy); if (!defined $found_x{$type} || !defined $found_y{$type}) { ($mapx, $mapy) = (13, 'o'); # ungefähr die Mitte Berlins } else { ($mapx, $mapy, $mapxx, $mapyy) = $Karte::Berlinmap1996::obj->coord ($Karte::Berlinmap1996::obj->standard2map($found_x{$type}, $found_y{$type})); } if (!Tk::Exists($map_toplevel)) { $map_toplevel = $top->Toplevel(-title => 'Berlinmap'); $map_toplevel->geometry("830x850"); $map_canvas = $map_toplevel->Canvas->pack(-expand => 1, -fill => 'both'); $map_canvas->createImage(0,0,-anchor => 'nw', -tags => 'map'); eval { my $Photo = ($main::default_img_fmt eq 'xpm' ? "Pixmap" : "Photo"); my $flag = $map_canvas->$Photo (-file => Tk::findINC("images/flag2_bl." . $main::default_img_fmt)); $map_canvas->createImage($mapxx-5, $mapyy-16, -anchor => 'nw', -image => $flag, -tags => 'flag') if defined $mapxx and defined $mapyy; }; my_warn($@) if $@; } $map_toplevel->deiconify; $map_toplevel->raise; $map_toplevel->Busy; eval { set_tk_map($map_canvas, $mapx, $mapy); $map_canvas->coords('flag',$mapxx-5, $mapyy-16) if defined $mapxx and defined $mapyy; $map_canvas->raise("flag"); }; my_warn($@) if $@; $map_toplevel->Unbusy; 1; } sub tk_str_dialog { my($top, $mark_sub, $get_coord_sub) = @_; return if !check_exists($top); my $entry_widget; eval { require Tk::HistEntry }; if ($@) { $entry_widget = 'Entry'; } else { $entry_widget = 'HistEntry'; } $maxrec_str = 2000; # Suche begrenzen my $t = $top->Toplevel(-title => 'Telefonbuch-CD-ROM'); $t->transient($top) if $main::transient; my($str, $hnr, $zip); my $f = $t->Frame->pack(-fill => 'x'); $f->Label(-text => "Straße")->grid(-row => 0, -column => 0, -sticky => 'e'); my $e_str = $f->$entry_widget(-textvariable => \$str )->grid(-row => 0, -column => 1, -sticky => 'w'); if ($e_str->can('history')) { $e_str->history(\@strasse_hist); $e_str->OnDestroy(sub { @strasse_hist = $e_str->history}); } $f->Label(-text => 'Hausnummer')->grid(-row => 1, -column => 0, -sticky => "e"); my $e_hnr = $f->Entry(-width => 6, -textvariable => \$hnr )->grid(-row => 1, -column => 1, -sticky => 'w'); ### not yet... # $f->Label(-text => 'PLZ')->grid(-row => 2, -column => 0, # -sticky => "e"); # my $e_zip = $f->Entry(-width => 6, # -textvariable => \$zip # )->grid(-row => 2, -column => 1, -sticky => 'w'); my $b_search = $t->Button (-text => 'Suchen', -command => sub { $t->Busy; eval { if ($e_str->can('historyAdd')) { $e_str->historyAdd; } $str =~ s/^\s+//; $str =~ s/\s+$//; $hnr =~ s/^\s+//; $hnr =~ s/\s+$//; #XXX $res{'str'} = get_from_db($str, $hnr); if (!defined $res{'str'} || ref $res{'str'} ne 'ARRAY' || !@{ $res{'str'} }) { $res{'str'} = $Telefonbuch->search_street_hnr($str, $hnr); use Data::Dumper; print STDERR "Line " . __LINE__ . ", File: " . __FILE__ . "\n" . Data::Dumper->Dumpxs([\%res],[]); # XXX } if ((!defined $res{'str'} || ref $res{'str'} ne 'ARRAY' || !@{ $res{'str'} }) && $Telefonbuch->can('search_street_nearest_hnr')) { $res{'str'} = $Telefonbuch->search_street_nearest_hnr($str, $hnr); } $l_found{'str'}->delete(0, 'end'); undef $found_x{'str'}; undef $found_y{'str'}; if (!defined $res{'str'} || ref $res{'str'} ne 'ARRAY') { $l_found{'str'}->insert('end', 'Nichts gefunden'); return; } my $first = 1; foreach my $item (@{$res{'str'}}) { my($str0, $hnr0, $plz, $ort, $x, $y) = @{ $item }; if ($first) { if ($mark_sub) { warn "Mark $str0 $hnr0 ($x,$y)\n" if $VERBOSE; $mark_sub->($x, $y); } #XXX not needed if ($get_coord_sub) { ($found_x{'str'}, $found_y{'str'}) = ($x, $y); # $found_y{'str'}) = $get_coord_sub->($x, $y); # } $first = 0; } if (defined $plz) { $l_found{'str'}->insert('end' => "$str0 $hnr0 ($plz)"); } elsif (defined $ort) { $l_found{'str'}->insert('end' => "$str0 $hnr0 ($ort)"); } else { $l_found{'str'}->insert('end' => "$str0 $hnr0"); } } }; $t->Unbusy; })->pack; $l_found{'str'} = $t->Scrolled('Listbox',# -height => 5, -scrollbars => 'osoe', )->pack(-fill => 'both', -expand => 1); #$t->afterIdle(sub { $l_found{'str'}->GeometryRequest(-1, 5*12); }); #XXX start_ziel_frame($t, 'str')->pack(-fill => 'x') unless $no_start_ziel; if ($main::advanced) { map_frame($t, 'str')->pack(-fill => 'x'); } $t->Button(-text => 'Schließen', -command => sub { $t->destroy })->pack(-fill => 'x'); if ($mark_sub) { $l_found{'str'}->bind ('' => sub { # XXX warum stand hier früher res{'res'} statt res{'str'}? return if !defined $res{'str'} || ref $res{'str'} ne 'ARRAY'; my($str0, $hnr0, $plz, $ort, $x, $y) = @{ $res{'str'}->[$l_found{'str'}->index('active')] }; $mark_sub->($x, $y); }); } $e_str->tabFocus if $e_str->can('tabFocus'); $e_str->bind('' => sub { $e_hnr->tabFocus if $e_hnr->can('tabFocus'); }); $e_hnr->bind('' => sub { $b_search->invoke }); $t->bind('' => sub { $t->destroy }); $t; } # Rückgabe: ein Array von Items, jeweils im # [Straße, Hausnummer, PLZ, Ort, X, Y]-Format sub Telefonbuch98::search_street_hnr { my($tel, $str, $hnr, $plz) = @_; my(%search_args) = (-spec => 'street', -maxrec => $maxrec_str, ); my @res = search_string($str, $str_base, 0, [0], %search_args); my @ret; my %street_hash; # Überprüfung der PLZ, falls gegeben if (defined $plz) { my @ret ; foreach my $item (@res) { if ($item->[2] == $plz) { push @ret, $item; } } @res = @ret; } # Überprüfung mit Hausnummer if (@res && defined $hnr && $hnr ne '') { foreach my $item (@res) { if ($item->[1] eq $hnr) { if (!exists $street_hash{$item->[0]}) { $street_hash{$item->[0]}++; $item->[0] = cp850__latin1($item->[0]); convert_street($item); push @ret, $item; } } } } return \@ret if (@ret); # keine Hausnummer hat gepasst bzw. keine wurde angegeben => # alle Straßen ausgeben if (@res) { foreach my $item (@res) { if (!exists $street_hash{$item->[0]}) { $street_hash{$item->[0]}++; $item->[0] = cp850__latin1($item->[0]); convert_street($item); push @ret, $item; } } return \@ret; } # keine Treffer (); } sub convert_street { my $data_ref = shift; ($data_ref->[4], $data_ref->[5]) = $Karte::GIS::obj->map2standard($data_ref->[4], $data_ref->[5]); } sub convert_tel { my $data_ref = shift; ($data_ref->[3], $data_ref->[4]) = $Karte::GIS::obj->map2standard($data_ref->[3], $data_ref->[4]); } sub tk_tel_dialog { my($top, $mark_sub, $get_coord_sub) = @_; return if !check_exists($top); my $entry_widget; eval { require Tk::HistEntry }; if ($@) { require Tk::LabEntry; $entry_widget = 'LabEntry'; } else { $entry_widget = 'HistEntry'; } $maxrec_tel = 200; # Suche begrenzen my $t = $top->Toplevel(-title => 'Telefonbuch-CD-ROM'); $t->transient($top) if $main::transient; my($nn, $vn); my %search_args; $search_args{-exact} = 1; $ {$search_args{-oldlinear}} = undef; my $e_nn = $t->$entry_widget (-label => 'Nachname', -labelPack => [-side => 'left'], -textvariable => \$nn)->pack(-anchor => 'w'); my $e_vn = $t->$entry_widget (-label => 'Vorname', -labelPack => [-side => 'left'], -textvariable => \$vn)->pack(-anchor => 'w'); if ($e_nn->can('history') and $e_vn->can('history')) { $e_nn->history(\@nachname_hist); $e_vn->history(\@vorname_hist); $e_nn->OnDestroy(sub { @nachname_hist = $e_nn->history}); $e_vn->OnDestroy(sub { @vorname_hist = $e_vn->history}); } $t->Checkbutton(-text => 'Exakt', -variable => \$search_args{-exact}, )->pack(-anchor => 'w'); $t->LabEntry(-label => 'Max. Treffer:', -labelPack => [-side => 'left'], -width => 6, -textvariable => \$maxrec_tel)->pack(-anchor => 'w'); my $l_found; my($b_prev, $b_next, $b_weiter); my @res; my $res_i = 0; my $show_sub = sub { my(%args) = @_; my $nolabel = delete $args{-nolabel}; my $fast = delete $args{-fast} || 0; if ($res_i > $#res) { warn "Adjusting index from $res_i to $#res"; $res_i = $#res; } if (ref $res[$res_i] ne 'ARRAY') { warn "Not an array reference (index $res_i)"; return; } my(@data) = @{ $res[$res_i] }; if ($VERBOSE && !$fast) { warn join("||", @data), "\n"; } if (!$fast) { $b_prev->configure(-state => ($res_i == 0 ? 'disabled' : 'normal')); $b_next->configure(-state => ($res_i == $#res ? 'disabled' : 'normal')); } if ($Telefonbuch eq 'Telefonbuch2001') { my($x, $y) = ($data[4], $data[5]); my $addtag = "$data[7] $data[6], $data[8], $data[0] $data[1]"; if (defined $mark_sub) { eval { $mark_sub->($x, $y, -addtag => $addtag, ); }; } ($found_x{'tel'}, $found_y{'tel'}) = ($x, $y); $l_found->configure(-text => $addtag); } elsif (defined $data[3] && $data[3] ne '') { if ($Telefonbuch eq 'Telefonbuch98') { convert_tel(\@data); } my($x, $y) = ($data[3], $data[4]); if ($VERBOSE && !$fast) { warn "Mark $x/$y\n"; } if ($Telefonbuch eq 'Telefonbuch98') { $args{-addtag} = ["$data[12] $data[21], $data[20]", "$data[13] $data[14]"]; } else { $args{-addtag} = [join(" ", @data[5..$#data])]; } if (defined $mark_sub) { eval { $mark_sub->($x, $y, %args); }; } ($found_x{'tel'}, $found_y{'tel'}) = ($x, $y); } if (!$nolabel && $Telefonbuch ne 'Telefonbuch2001') { my $text; if ($Telefonbuch eq 'Telefonbuch98') { $text = $data[18]; } else { $text = join(" ", @data[5..$#data]); } $text =~ s//\n/g; $text =~ s/<[^>]+>/ /g; $l_found->configure(-text => $text); } }; my $b_f = $t->Frame->pack(-anchor => 'w'); my $b_search = $b_f->Button(-text => 'Suchen', -command => sub { $t->Busy; eval { if ($e_nn->can('historyAdd')) { $e_nn->historyAdd; } if ($e_vn->can('historyAdd')) { $e_vn->historyAdd; } $ {$search_args{-oldlinear}} = undef; my($res) = $Telefonbuch->search_name ($nn, $vn, %search_args); @res = @$res; if (!@res || ref $res[0] ne 'ARRAY') { $l_found->configure(-text => 'Nichts gefunden'); $b_weiter->configure(-state => 'disabled'); return; } $b_weiter->configure (-state => (defined $ {$search_args{-oldlinear}} ? 'normal' : 'disabled')); $res_i = 0; $show_sub->(); }; $t->Unbusy; })->pack(-side => 'left'); $b_weiter = $b_f->Button(-text => 'Weiter', -command => sub { $t->Busy; eval { my $res = $Telefonbuch->search_name ($nn, $vn, %search_args); @res = @$res; if (!@res || ref $res[0] ne 'ARRAY') { $l_found->configure(-text => 'Nichts gefunden'); $b_weiter->configure(-state => 'disabled'); return; } $b_weiter->configure (-state => (defined $ {$search_args{-oldlinear}} ? 'normal' : 'disabled')); $res_i = 0; $show_sub->(); }; $t->Unbusy; })->pack(-side => 'left'); eval { require Tk::FireButton; Tk::FireButton->VERSION(0.04); }; my $firebutton = (!$@ ? 'FireButton' : 'Button'); $b_f->Button(-text => '|<', -command => sub { return if !@res; $res_i = 0; $show_sub->(); }, -padx => 0, -pady => 0, )->pack(-side => 'left'); $b_prev = $b_f->$firebutton(-text => '<' . '<', -command => sub { return if !@res; return if $res_i-1 < 0; $res_i--; $show_sub->(); }, -padx => 0, -pady => 0, )->pack(-side => 'left'); $b_next = $b_f->$firebutton(-text => '>>', -command => sub { return if !@res; return if $res_i+1 > $#res; $res_i++; $show_sub->(); }, -padx => 0, -pady => 0, )->pack(-side => 'left'); $b_f->Button(-text => '>|', -command => sub { return if !@res; $res_i = $#res; $show_sub->(); }, -padx => 0, -pady => 0, )->pack(-side => 'left'); $b_f->Button(-text => 'Alle', -command => sub { return if !@res; $t->Busy; eval { for($res_i = 0; $res_i <= $#res; $res_i++) { $show_sub->(-dont_delete_old => 1, -dont_center => 1, -nolabel => 1, -fast => 1); } }; $t->Unbusy; })->pack(-side => 'left'); if (is_in_path("dial")) { $b_f->Button(-text => 'Wählen', -command => sub { my $telnr = $res[$res_i]->[20]; if (defined $telnr && $telnr ne '') { warn "Wähle $telnr...\n"; system("dial $telnr"); } })->pack(-side => 'left'); } $l_found = $t->Label(-wraplength => 320, -width => 40, -justify => 'left')->pack; start_ziel_frame($t, 'tel')->pack(-fill => 'x') unless $no_start_ziel; if ($main::advanced) { map_frame($t, 'tel')->pack(-fill => 'x'); } $e_nn->tabFocus if $e_nn->can('tabFocus'); $e_nn->bind('' => sub { $e_vn->tabFocus if $e_vn->can('tabFocus'); }); $e_vn->bind('' => sub { $b_search->invoke }); $t->bind('' => sub { $t->destroy }); $t; } sub Telefonbuch98::search_name { my($nn, $vn, %search_args) = @_; $search_args{'-tocmp'} = \&tocmp2; $search_args{'-maxrec'} = $maxrec_tel; my @res = search_string($nn, $tel_base, [26, 12], [12, 13, 18, 21], %search_args); if (@res && defined $vn && $vn ne '') { my @newres; $vn = tocmp2($vn); foreach (@res) { if (tocmp2($_->[21]) eq $vn) { push @newres, $_; } } return @newres; } if (@res) { return @res; } undef; } sub search_string { my($search, $base, $key, $convertref, %search_args) = @_; my $tocmp = (defined $search_args{'-tocmp'} ? $search_args{'-tocmp'} : \&tocmp); my $maxrec = $search_args{-maxrec}; $search = $tocmp->($search); # für exakte (lineare) Suche my $search2 = $search; if (defined $search_args{-spec} && $search_args{-spec} eq 'street') { $search2 =~ s/\s*\(.*\)\s*$//; # Bezirk abschneiden, für lineare Suche # "straße" normieren... ß ist bereits zu s geworden $search =~ s/strase$/str/g; $search2 =~ s/strase$/str/g; } my $fakesearch = $search2; # für binäre Suche my $ch = substr($fakesearch, length($fakesearch)-1, 1); $ch = chr(ord($ch)-1); $fakesearch =~ s/.$/$ch/; $fakesearch .= ("\xff" x 10); warn "FAKESEARCH: $fakesearch\n" if $VERBOSE; my @res; my $dir = Telefonbuch::exists(); if (!defined $dir) { my_die("Das Telefonbuch-Verzeichnis konnte nicht gefunden werden! Vielleicht ist die CD-ROM nicht eingelegt?"); } my $datafile = "$dir/${base}d00"; my $indexfile = "$dir/${base}i00"; open(I, $indexfile) or my_die("$indexfile: $!"); binmode I; open(D, $datafile) or my_die("$datafile: $!"); binmode D; my $begin = 8; my $middle; seek(I, 0, 2) or die "Can't seek: $!"; my $end = tell I; my $last_middle = -1; my $do_linear = 0; if (exists $search_args{-oldlinear} && defined $ {$search_args{-oldlinear}}) { warn "Continue linear search\n" if $VERBOSE; $do_linear = 1; $middle = $ {$search_args{-oldlinear}}; } while (1) { if (!$do_linear) { $middle = int(($end-$begin)/4/2)*4 + $begin; if ($middle == $last_middle) { $do_linear = 1; warn "Switch to linear search\n" if $VERBOSE; $middle += 4; } } else { $middle += 4; } my $buf; seek(I, $middle, 0) or die "Can't seek: $!"; defined read(I, $buf, 4) or die "Can't read: $!"; my $seek = unpack("V", $buf); seek(D, $seek, 0) or die "Can't seek: $!"; defined read(D, $buf, 4) or die "Can't read: $!"; my($ds, $extra_offset) = unpack("v2", $buf); # Anzahl der Datensätze my $curr_seek = $seek + 4; my(@ds, @ds2, @ds3); for(my $i = 0; $i < $ds; $i++) { if (int($curr_seek / pagesize) != int(($curr_seek+3) / pagesize)) { # next file page $curr_seek = (int($curr_seek / pagesize)+1)*pagesize; seek(D, $curr_seek, 0) or die "Can't seek: $!"; } defined read(D, $buf, 4) or die "Can't read: $!"; my($index, $offset) = unpack("v2", $buf); # if ($index < 0 || $index > $ds) { # warn "*** ERROR\n"; # warn sprintf("Invalid index: $index at 0x%x\n", $seek); # exit 1; # } # $ds[$index] = $offset; # XXX $ds[$i] = $offset; $ds2[$i] = $index; $ds3[$index] = $i; #warn "*** $ds3[$index]\n"; $curr_seek += 4; } $ds[$ds] = 0; # Ende markieren my(@data, @data2); for(my $i = 0; $i < $ds; $i++) { seek(D, $seek-$ds[$i]-$extra_offset, 0) or die "Can't seek: $!"; defined read(D, $buf, $ds[$i]-$ds[$i+1]) or die "Can't read: $!"; push @data, $buf; $data2[$ds2[$i]] = $buf; } #warn ">", join("|| ", map { if (ref $_ eq 'ARRAY') { join("|", @$_)} else { $_ } } @data2), "\n" if $VERBOSE; my $k; if (ref $key eq 'ARRAY') { foreach my $kk (@$key) { if (defined $ds3[$kk] && $data[$ds3[$kk]] ne '') { $k = $ds3[$kk]; last; } } } else { $k = $ds3[$key]; } if (!defined $k) { die "No valid key?! (seek $seek)"; } $data[$k] = cp850__latin1($data[$k]); my $cmpdata = $tocmp->($data[$k]); warn ">", join(", ", @data), "\n" if $VERBOSE; if ($do_linear) { if (defined $convertref) { foreach my $i (@$convertref) { next if $i == $k; # bereits convertiert $data2[$i] = cp850__latin1($data2[$i]); } } #warn "$cmpdata <=> $search exact:$search_args{-exact}\n"; if ((!$search_args{-exact} && $cmpdata =~ /^\Q$search\E/i) || ($search_args{-exact} && $cmpdata =~ /^\Q$search\E$/i)) { push @res, [@data2]; if (defined $maxrec && @res > $maxrec) { warn "Über $maxrec Datensätze gefunden!\n" if $VERBOSE; if (exists $search_args{'-oldlinear'}) { $ {$search_args{'-oldlinear'}} = $middle; } last; } #warn "pushing $data2[0]"; } else { #warn "last"; if (exists $search_args{'-oldlinear'}) { $ {$search_args{'-oldlinear'}} = undef; } last if ($cmpdata !~ /^$search2/i); } } else { my $cmp = ($cmpdata cmp $fakesearch); #warn "$cmpdata $cmp $search"; if ($cmp < 0) { $begin = $middle; } elsif ($cmp > 0) { $end = $middle; } else { $do_linear = 1; next; } $last_middle = $middle; } } close D; close I; return @res; } sub cp850__latin1 { $_ = $_[0]; if (defined $_) { tr/\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377/\307\374\351\342\344\340\345\347\352\353\350\357\356\354\304\305\311\346\306\364\366\362\373\371\377\326\334\370\243\330\327F\341\355\363\372\361\321\252\272\277\256\254\275\274\241\253\273\.\:\?\|\+\301\302\300\251\+\|\+\+\242\245\+\+\+\+\+\-\+\343\303\+\+\+\+\+\=\+\244\360\320\312\313\310i\315\316\317\+\+FL\246\314T\323\337\324\322\365\325\265\336\376\332\333\331\375\335\-\264\255\261\=\276\266\247\367\270\260\250\'\271\263\262f\240/; } $_; } sub tocmp { my $a = shift; $a =~ tr/äöüÄÖÜß/aouAOUs/; lc($a); } sub tocmp2 { my $a = shift; if (defined $a) { $a =~ s/ä/ae/g; $a =~ s/ö/oe/g; $a =~ s/ü/ue/g; $a =~ s/Ä/Ae/g; $a =~ s/Ö/Oe/g; $a =~ s/Ü/Ue/g; $a =~ s/ß/ss/g; return lc($a); } } # Bekommt einen String der Form "Straße Hausnummer" und teilt ihn # in Straße und Hausnummer auf. Genauer genommen ist die Rückgabe: # (strasse, hausnummer_complete, hausnummer) # wobei hausnummer_complete die komplette Hausnummer ist sub parse_str_number { my $str = shift; if ($str =~ /^(.*)\s+((\d+)([a-z])?)$/i) { return ($1, $2, $3); } elsif ($str =~ m|^(.*)\s+((\d+)[a-z]?[-/]\d+[a-z]?)|i) { return ($1, $2, $3); } else { return ($str); } } sub get_from_db { my($str, $hnr) = @_; my @res; eval { push @INC, "$FindBin::RealBin/miscsrc", "/home/e/eserte/src/bbbike/miscsrc"; require TelbuchDBApprox; my $approx = TelbuchDBApprox->new; $str .= " $hnr" if defined $hnr; @res = $approx->search($str); @res = map { [$_->{Street}, $_->{Nr}, undef, $_->{Citypart}, split(/,/, $_->{Coord})] } @res; }; my_warn($@) if $@; \@res; } { # Execute simple test if run as a script package main; no strict; $INC{'Telefonbuch.pm'} = 1; eval join('',) || die("$@ $main::DATA") unless caller(); } %main::str_file = %$main::str_file if 0; # peacify -w $main::devel_host = $main::devel_host if 0; # peacify -w 1; __END__ use FindBin; use lib ("$FindBin::RealBin/lib", "$FindBin::RealBin"); use Tk; use strict; # Hier stehen die "emulierten" Variablen, die auch im BBBike-Hauptprogramm # vorkommen. use vars qw($advanced $os $transient $devel_host $default_img_fmt); use Getopt::Long; $os = ($^O eq 'MSWin32' ? 'win' : 'unix'); $advanced = 1; $transient = 1; $devel_host = 1; $Telefonbuch::cgi_host = "localhost"; # weil $cgi_host schon gesetzt ist $default_img_fmt = "gif"; GetOptions("maproot=s" => \$Karte::map_root, "force!" => \$Telefonbuch::force); #$Telefonbuch::VERBOSE=1; # XXX $Telefonbuch::no_start_ziel = 1; my $top = tkinit; $top->withdraw; # for debugging... $top->bind("all", "" => sub { $top->WidgetDump }); my $str_t = Telefonbuch::tk_str_dialog($top); my $tel_t = Telefonbuch::tk_tel_dialog($top); $str_t->OnDestroy(sub { if (!Tk::Exists($tel_t)) { $top->destroy; } }); $tel_t->OnDestroy(sub { if (!Tk::Exists($str_t)) { $top->destroy; } }); MainLoop; 1;