1#!/usr/bin/env perl
2# -*- perl -*-
3
4#
5# Author: Slaven Rezic
6#
7# Copyright (c) 1995-2012 Slaven Rezic. All rights reserved.
8# This is free software; you can redistribute it and/or modify it under the
9# terms of the GNU General Public License, see the file COPYING.
10#
11# Mail: slaven@rezic.de
12# WWW:  http://bbbike.sourceforge.net
13#
14
15package main;
16
17## Additional files for perl2exe.
18## NOTE: This list is not maintained anymore.
19#perl2exe_include Tk/Checkbutton.pm
20
21## This works theoretically with 5.8.x, but there's a possible
22## endless loop which is solved in 5.10.0, see rt perl #41442
23## XXX Nope: still an endless loop with debian's perl 5.10.0
24## To reproduce: start bbbike, and add landstra�en layer
25#if ($] >= 5.010 || $] >= 5.008009) {
26#    eval q{ use open ':locale' }; if ($] >= 5.008 && $@) { warn $@ }
27#}
28
29BEGIN {
30    if ($Devel::Trace::TRACE) {
31	$Devel::Trace::TRACE = 0;
32	warn <<EOF;
33**********************************************************************
34* NOTE: Turning -d:Trace off
35*       You can turn it again on in the ptksh using
36*
37*         \$Devel::Trace::TRACE = 1;
38*
39**********************************************************************
40EOF
41    }
42}
43
44use FindBin;
45use lib ("$FindBin::RealBin",
46	 "$FindBin::RealBin/images",
47	 "$FindBin::RealBin/lib",
48	);
49# To create the Devel::Size output, start bbbike with:
50#     env BBBIKE_DEBUG=Devel::Size ./bbbike | & grep size
51BEGIN {
52    if ($ENV{BBBIKE_DEBUG}) {
53	eval 'use BBBikeDebug';
54	die $@ if $@;
55    }
56}
57
58BEGIN {
59    my $nosplash = grep { $_ eq '-nosplash' } @ARGV;
60    # save cmdline arguments; Tk::ProgressSplash would eat
61    # X11-specific options (maybe a bug there?)
62    my @save_ARGV = @ARGV; @ARGV = ();
63    if ($] >= 5.005 && !$^C && !$^P && !$nosplash) {
64	# XXX don't know whether this is a Tk400 or an old perl problem
65	eval {
66	    require Tk::ProgressSplash;
67	    my $splashtype = 'normal';
68	    # $splashtype = 'fast'; not used anymore: too unstable,
69	    # fails with MSWin32, failures also seen on Linux x86_64
70	    # systems.
71	    $splash_screen = Tk::ProgressSplash->Show
72		(-splashtype => $splashtype,
73		 "$FindBin::RealBin/images/bbbike_splash.xpm",
74		 240, 90, "BBBike", 1);
75	}; warn $@ if $@;
76    }
77    @ARGV = @save_ARGV;
78    if ($nosplash) { $use_logo = 0 }
79
80    local $^W;
81    $^W = 0 if $^O eq 'MSWin32'; # to avoid "no such signal" warnings
82
83    {
84	use vars qw(@SIGTRAP_SIGNALS);
85	@SIGTRAP_SIGNALS = qw(USR1 INFO);
86	# Activate with CTRL-T on BSD systems. Possibly dangerous if forked
87	# processes are active, but works fine with -server option.
88	my $siginfo_handler = sub {
89	    # Cannot use warn or STDERR because of Tk::Stderr interference
90	    require Carp;
91	    local $| = 1;
92	    print Carp::longmess("Pid $$ currently"), "\n";
93	};
94	$SIG{$_} = $siginfo_handler for @SIGTRAP_SIGNALS;
95    }
96    ## Does not play well with Tk::Stderr, so do not use it anymore
97    #eval 'use sigtrap ("stack-trace", @SIGTRAP_SIGNALS)'; warn $@ if $@;
98
99    ## Not a good idea: setting this means that $? is always -1
100    #$SIG{CHLD} = 'IGNORE';
101
102    $booting = 1;
103}
104
105use Config;
106
107## DEBUG_BEGIN
108#BEGIN{mymstat("before autouse BBBikeMail, Text::Wrap, File::Copy");}
109## DEBUG_END
110
111use BBBikeGlobalVars 1.012;
112
113# Call "autouse" as early as possible. Otherwise there will be errors,
114# if any other module requires theses modules.
115# "autouse" cannot be used on modules with non-standard import functions
116BEGIN {
117    %autouse_func =
118	('BBBikeMail'	=> [qw(enter_send_mail)],
119	 'Text::Wrap'	=> [qw(wrap)],
120	 'File::Copy'	=> [qw(copy mv)],
121	 'BBBikeGPS'
122	 => [qw(gps_interface draw_gpsman_data do_draw_gpsman_data)],
123	 'BBBikeWeather'
124	 => [qw(wetter_dir_exists ignore_weather reset_wind update_weather
125		show_weather_db parse_wetterline analyze_wind)],
126	 'BBBikeHeavy'
127	 => [qw(start_followmouse stop_followmouse
128		string_eval_die load_plugins load_plugin layer_editor
129		getmap get_file_or_url get_user_agent get_uncached_user_agent delete_map
130		pdf_export svg_export perlmod_install_advice
131		show_register save_register_routes load_register_routes
132		show_calories check_available_memory
133		reload_all make_temp make_unique_temp
134		save_route_as_gpx save_route_as_kml
135		restart_bbbike_hint
136	      )],
137	 #XXX problems with autouse! -> what problems?
138	 'BBBikeEdit'
139	 => [qw(insert_point_from_canvas create_relation_from_canvas
140		ampeln_on_route radweg_open radweg_draw_canvas
141	       )],
142	 'BBBikeLazy'
143	 => [qw(bbbikelazy_setup bbbikelazy_init bbbikelazy_clear
144		bbbikelazy_reload bbbikelazy_reload_all
145		bbbikelazy_redraw_current_view
146		bbbikelazy_add_data bbbikelazy_remove_data plotstr_on_demand)],
147	 'BBBikePrint'
148	 => [qw(create_postscript print_postscript toggle_legend
149		print_text_postscript print_text_pdflatex print_route_pdf
150		view_pdf print_text_windows)],
151	);
152    while(my($k,$v) = each %autouse_func) {
153	eval "use autouse $k => qw(" . join(" ", @$v) . ");";
154	die "Can't autouse $k: $@" if $@;
155    }
156}
157
158## This is only for the Autoloader-Hack (see "make autoload")
159#use AutoLoader 'AUTOLOAD';
160
161## DEBUG_BEGIN
162#BEGIN{mymstat("before Tk");}
163## DEBUG_END
164
165BEGIN {
166    eval q{ use Tk; };
167    if ($@) {
168	if ($^C) {
169	    die $@;
170	} else {
171	    warn $@;
172	    if ($^O eq 'MSWin32' || -t STDIN) {
173		warn "Please enter RETURN to exit.\n";
174		<STDIN>;
175	    }
176	    CORE::exit(1);
177	}
178    }
179}
180
181# Add ...\c\bin directory for Strawberry Perl on Windows.
182# This directory contains shared libraries e.g. libxml2.
183# Also the ...\perl\bin may be missing.
184if ($^O eq 'MSWin32' && $^X =~ m{(.*)(\\perl\\bin)\\}) {
185    my $c_bin_dir = "$1\\c\\bin";
186    my $perl_bin_dir = "$1$2";
187    if (-d $c_bin_dir) {
188	$ENV{PATH} .= ";$c_bin_dir";
189    }
190    if (-d $perl_bin_dir) {
191	$ENV{PATH} .= ";$perl_bin_dir";
192    }
193}
194
195#XXX for now disabled ... still too many bugs floating around -> what bugs?
196#use Tk::ErrorDialog; # XXX is this OK?
197use Tk::Canvas;
198use Tk::CanvasUtil;
199use File::Basename;
200## DEBUG_BEGIN
201#BEGIN{mymstat("before BBBikeUtil");}
202## DEBUG_END
203use BBBikeUtil;
204use BBBikeUtil qw(min max first clone s2hm_or_s);
205use BBBikeTkUtil qw(pack_buttonframe);
206use BBBikeVar;
207use BBBikeCalc;
208use BBBikeTrans;
209## DEBUG_BEGIN
210#BEGIN{mymstat("before Strassen");}
211## DEBUG_END
212use Strassen;
213use Strassen::Dataset;
214## DEBUG_BEGIN
215#BEGIN{mymstat("before Route");}
216## DEBUG_END
217use Route;
218## DEBUG_BEGIN
219#BEGIN{mymstat("before Karte");}
220## DEBUG_END
221use Karte;
222use Hooks;
223use VectorUtil qw(get_polygon_center point_in_polygon point_in_grid offset_line);
224## DEBUG_BEGIN
225#BEGIN{mymstat("before locale");}
226## DEBUG_END
227
228use strict;
229## DEBUG_BEGIN
230#BEGIN{mymstat("before use vars");}
231## DEBUG_END
232
233# i18n functions M and Mfmt
234BEGIN {
235    if (!eval '
236use Msg; # This call has to be in bbbike!
2371;
238') {
239	warn $@ if $@;
240	eval 'sub M ($) { $_[0] }';
241	eval 'sub Mfmt { sprintf(shift, @_) }';
242    }
243}
244
245# XXX This is a hack until I decide how to do custom create_page best.
246{
247    package My::Tk::Getopt;
248    use vars qw(@ISA);
249    @ISA = ('Tk::Getopt');
250
251    BEGIN { *M = \&main::M }
252
253    sub _create_page {
254	my $self = shift;
255	my $current_top  = $_[2];
256	if ($current_top eq lc(M("Strecken/Punkte"))) {
257	    my $current_page = $_[0];
258	    my $optnote      = $_[1];
259	    $current_page = $optnote->{$current_top} if !defined $current_page;
260	    my $optlist      = $_[3];
261
262	    my %opt2opt;
263	    for my $optdef (@{$optlist->{$current_top}}) {
264		$opt2opt{$optdef->[0]} = $optdef;
265	    }
266	    #use Hash::Util qw(lock_keys); lock_keys %opt2opt;
267
268	    $current_page->Label(
269				 -text => M"Strecken und Punkte, die beim Start von BBBike\ngezeichnet werden sollen.",
270				 -justify => 'left',
271				)->pack(-anchor => 'w');
272	    my $f = $current_page->Frame->pack(-anchor => 'w');
273	    Tk::grid('x',
274		     $f->Label(-text => "Berlin"), # XXX not for osm-data, there should be only one column here!
275		     $f->Label(-text => M"Umland"),
276		     $f->Label(-text => M"jwd"),
277		    );
278	    Tk::grid($f->Label(-text => M"Stra�en"),
279		     $f->Checkbutton(-variable => $self->varref($opt2opt{'str'})),
280		     $f->Checkbutton(-variable => $self->varref($opt2opt{'landstr'})),
281		     $f->Checkbutton(-variable => $self->varref($opt2opt{'landstrjwd'})),
282	    );
283	    Tk::grid($f->Label(-text => M("Orte")."/".M("Ortsteile")),
284		     $f->Checkbutton(-variable => $self->varref($opt2opt{'ortsteil'})),
285		     $f->Checkbutton(-variable => $self->varref($opt2opt{'ort'})),
286		     $f->Checkbutton(-variable => $self->varref($opt2opt{'ortjwd'})),
287		    );
288	    Tk::grid($f->Label(-text => M"Gew�sser"),
289		     $f->Checkbutton(-variable => $self->varref($opt2opt{'wasserstadt'})),
290		     $f->Checkbutton(-variable => $self->varref($opt2opt{'wasserumland'})),
291		     $f->Checkbutton(-variable => $self->varref($opt2opt{'wasserjwd'})),
292		    );
293
294	    require Tk::Ruler;
295	    $f->Ruler(-padx => 2)->rulerGrid(-columnspan => 4);
296
297	    Tk::grid($f->Label(-text => M"Radwege"),
298		     $f->Checkbutton(-variable => $self->varref($opt2opt{'cyclepath'})), '-', '-');
299	    Tk::grid($f->Label(-text => M"Radrouten"),
300		     $f->Checkbutton(-variable => $self->varref($opt2opt{'cycleroute'})), '-', '-');
301	    Tk::grid($f->Label(-text => M"Gr�ne Wege"),
302		     $f->Checkbutton(-variable => $self->varref($opt2opt{'greenway'})), '-', '-');
303	    Tk::grid($f->Label(-text => M("Ampeln")."/".M("Bahn�berg�nge")),
304		     $f->Checkbutton(-variable => $self->varref($opt2opt{'ampel'})), '-', '-');
305	    Tk::grid($f->Label(-text => M"F�hren"),
306		     $f->Checkbutton(-variable => $self->varref($opt2opt{'faehre'})), '-', '-');
307	    Tk::grid($f->Label(-text => M"Fl�chen"),
308		     $f->Checkbutton(-variable => $self->varref($opt2opt{'flaeche'})), '-', '-');
309	    Tk::grid($f->Label(-text => M"Sehensw�rdigkeiten"),
310		     $f->Checkbutton(-variable => $self->varref($opt2opt{'sehenswuerdigkeiten'})), '-', '-');
311	    Tk::grid($f->Label(-text => M"Fragezeichen"),
312		     $f->Checkbutton(-variable => $self->varref($opt2opt{'fragezeichen'})), '-', '-');
313
314	    $f->Ruler(-padx => 2)->rulerGrid(-columnspan => 4);
315
316	    Tk::grid('x',
317		     $f->Label(-text => M"Linien"),
318		     $f->Label(-text => M"Bahnh�fe"),
319		    );
320	    Tk::grid($f->Label(-text => M"U-Bahn"),
321		     $f->Checkbutton(-variable => $self->varref($opt2opt{'ubahn'})),
322		     $f->Checkbutton(-variable => $self->varref($opt2opt{'ubahnhof'})),
323		    );
324	    Tk::grid($f->Label(-text => M"S-Bahn"),
325		     $f->Checkbutton(-variable => $self->varref($opt2opt{'sbahn'})),
326		     $f->Checkbutton(-variable => $self->varref($opt2opt{'sbahnhof'})),
327		    );
328	    Tk::grid($f->Label(-text => M"Regionalbahn"), #XXX translation is missing
329		     $f->Checkbutton(-variable => $self->varref($opt2opt{'rbahn'})),
330		     $f->Checkbutton(-variable => $self->varref($opt2opt{'rbahnhof'})),
331		    );
332	} else {
333	    $self->SUPER::_create_page(@_);
334	}
335    }
336}
337
338## DEBUG_BEGIN
339#BEGIN{mymstat("before use your");}
340## DEBUG_END
341
342use your qw($Karte::Standard::obj $Karte::Standard::init_scrollregion
343	    $Karte::GISmap::obj $Karte::Polar::obj
344	    $Tk::Getopt::x11_pass_through
345	    $wettermeldung2::proxy $wettermeldung2::module
346	    %wettermeldung2::loc %wettermeldung2::www_site
347	    $wettermeldung2::FIELD_TEMP $wettermeldung2::tk_widget
348	    $Http::tk_widget
349	    %GfxConvert::tmpfiles
350	    $BikePower::has_xs
351	    $Radwege::bez @Radwege::bbbike_category_order
352	    %Radwege::category_plural
353	    $FURadar::use_map $FURadar::progress
354	    $PLZ::VERBOSE $Devel::Trace::TRACE
355	    $Tk::Config::xlib
356	   );
357
358*transpose_ls          = \&transpose_ls_slow;
359# If you don't have a FPU, maybe \&old_create_transpose_subs should be
360# used instead.
361*create_transpose_subs = \&old_create_transpose_subs_no_int;
362
363## DEBUG_BEGIN
364#BEGIN{mymstat("before use BBBikeXS");}
365## DEBUG_END
366
367# BBBikeXS functions are optional, as there are pure-perl replacements
368eval 'use BBBikeXS 0.09';
369
370## DEBUG_BEGIN
371#BEGIN{mymstat("after use BBBikeXS");}
372## DEBUG_END
373
374# $VERSION is the version of the BBBike distribution
375$VERSION = $BBBike::VERSION;
376# Since the migration to git $PROG_REVISION is meaningless. Previously
377# it was constructed from the RCS version of this file.
378$PROG_REVISION = '3.500';
379
380# OS related
381$progname = basename($0);
382# Note that $ENV{HOST} is not generally available (or sometimes only
383# as a shell variable with the same name), especially in my non-tcsh
384# configurations.
385$devel_host = ($ENV{HOST} && $ENV{HOST} =~ /^(biokovo|biokovo-amd64|mosor|vran|cabulja|cvrsnica|spiff|mom|devpc01|devpc01-debian)(\.|$)/i);
386$os =   $^O eq 'MSWin32' || $^O eq 'os2' || $^O eq 'dos' ? 'win'
387      : $^O eq 'MacOS'				    	 ? 'mac'
388      : 						   'unix';
389$os_bsd = $^O =~ /bsd/i;
390
391$^W = $devel_host; # $advanced also sets $^W, see below
392
393if (!defined $is_handheld) {
394    $is_handheld = $Config{"archname"} =~ /^arm-linux$/i;
395}
396$use_clipboard = 1 if $os eq 'win';
397
398# include after setting $os!
399require TkChange;
400
401# compatibility includes
402if ($Tk::VERSION < 800) {
403    print STDERR Mfmt("Die Tk-Version ist veraltet (%s). M�glicherweise ist
404BBBike trotzdem benutzbar. Empfohlen wird ein Upgrade auf Version 804.027 oder
405besser.\n", $Tk::VERSION);
406}
407
408if ($Tk::VERSION <= 402.004) {
409    require TkCompat;
410}
411
412if ($os eq 'unix' && $Tk::VERSION >= 804.027001) {
413    require Tk::MsgBox;
414    import Tk::MsgBox 'as_default';
415}
416
417# OS compat
418if ($os eq 'win') {
419    require Win32Util;
420} elsif ($^O eq 'darwin') {
421    require MacOSXUtil;
422}
423
424my $terminal_encoding;
425if ($os eq 'win') {
426    require WinCompat;
427    # XXX This encoding is maybe valid for Win98 (some?) command.com, what about other Windows?
428    # XXX Unfortunately using encoding on STDERR
429    # creates a segfault with ActivePerl Build 811 + Win98, so it's disabled...
430    #$terminal_encoding = "cp850";
431} else {
432    local $^W = undef;
433    if ("$ENV{LANG}$ENV{LC_ALL}" =~ /utf-?8/i) {
434	$terminal_encoding = "utf8";
435    }
436}
437if ($terminal_encoding && $] >= 5.008) {
438    eval '
439	binmode STDOUT, ":encoding($terminal_encoding)";
440	binmode STDERR, ":encoding($terminal_encoding)";
441    '; warn $@ if $@;
442}
443
444# enable DnD
445use Tk::DropSite;
446
447# Var section: map scales and orientation
448set_landscape();
449$scale_coeff = 1;
450$small_scale  = 0.0625;    # map scale for overview window (region mode)
451$medium_scale = 0.13;      # map scale for overview window (city/Berlin mode)
452$small_scale_edit  = 0.01;         # dasselbe f�r den Edit-Mode XXX remove?
453$medium_scale_edit = 0.02;
454set_canvas_scale(DEFAULT_SCALE); # sets $scale
455Karte::preload('Standard');
456my $init_scale_massstab; # in 1:x form
457$bbbike_route_ext = 'bbr';
458$map_bg = 'grey85';
459use vars qw($balloon_info_from_all_tags_closeenough);
460$balloon_info_from_all_tags_closeenough = 3; # was 5, then 4
461
462# Var section: street and point attributes
463$init_str_draw{'s'} = 1;      # draw streets by default
464for (qw(s l r b u w f v e z g gP gD gBO sBAB fz wr)) { $p_sub_draw{"pp-$_"} = 1} # this list should cover most keys of %str_file (but not the dependent ones like "comm" or "qs")
465$init_p_draw{'lsa'} = 1;
466$p_far_away{'o'} = 0;
467$str_restrict{'s'}  = {qw(BAB 0 B 1 HH 1 H 1 NH 1 N 1 NN 1 Pl 0 Br 0)}; # Pl = places, Br = bridges
468# NOTE: This is misused for getting all valid RBahn categories:
469$str_restrict{'r'}  = {qw(RA 1 RB 1 RC 1 R 1 R0 0 RBau 0 RG 1 RP 0)};
470$str_restrict{'b'}  = {qw(S 1 SA 1 SB 1 SC 1 S0 0 SBau 0 SBetrieb 0)};
471$str_restrict{'u'}  = {qw(U 1 UA 1 UB 1 U0 0 UBau 0 UBetrieb 0)};
472$str_restrict{'qs'} = {qw(Q0 0 Q1 1 Q2 1 Q3 1)};
473$str_restrict{'ql'} = {qw(Q0 0 Q1 1 Q2 1 Q3 1)};
474$str_restrict{'hs'} = {qw(q0 0 q1 1 q2 1 q3 1 q4 1)};
475$str_restrict{'hl'} = {qw(q0 0 q1 1 q2 1 q3 1 q4 1)};
476$str_ignore{'temp_sperre_s'} = {0 => 1, 1 => 1, 2 => 1, 3 => 1}; # XXX BNP auch?
477# Should maybe go to Strassen::Cat?
478$tunnel_qr = qr{^_?Tu_?$};
479$roundabout_qr = qr{^(Mini)?Roundabout$};
480# no $cat_rueck handling here
481$complex_IMG_qr = qr/^IMG:([^|]+)(?:\|ANCHOR:([^|]+))?$/;
482$viewangle_qr = qr{^View:([-+]?\d+):([-+]?\d+)}; # XXX duplicated in Strassen::Cat
483
484# do not draw Steigung and Gef�lle at the same time:
485$str_ignore{'comm'} = {'Gf' => 1}; # XXX with ";"???
486require Radwege;
487foreach (@Radwege::category_order) {
488    $str_restrict{'rw'}->{$Radwege::category_code{$_}} = 1
489        if defined $Radwege::category_code{$_};
490}
491$str_nr_draw{'comm-route'} = 1;
492$str_nr_draw{'sBAB'} = 1; # XXX sollte vielleicht umschaltbar sein?
493# minimum width for "two-track" effect
494$sBAB_two_track_width = 3;
495
496$do_iconframe = 1;
497$do_route_strnames          = 0 if !defined $do_route_strnames;
498$do_route_strnames_km       = 0 if !defined $do_route_strnames_km;
499$do_route_strnames_compact  = 0 if !defined $do_route_strnames_compact;
500$do_route_strnames_comments = 1 if !defined $do_route_strnames_comments;
501$net_type = "s";
502$no_make_net = 0;
503$str_far_away{'w'} = 0;
504$orte_label_size = 1;
505use constant MIN_ORT_CAT => 0;
506use constant MAX_ORT_CAT => 6;
507$str_far_away{'l'} = 0;
508$show_overview_mode = "city";
509$show_overview = $show_strlist = 0;
510$show_calories = 0;
511$use_hoehe = 1; # XXX kann im Programm nicht gesetzt werden
512$steigung_optimierung = 0;
513$green_optimization = 0;
514$special_vehicle_rb = 'normal';
515$grade_minimum_short_length = 100; # 100m gilt als kurz f�r grademinimumshort
516$use_legend = $use_legend_right = 0;
517$use_faehre = 0;
518$sperre{'einbahn'} = 1;
519$sperre{'einbahn-strict'} = 0;
520$sperre{'sperre'} = 1;
521$sperre{'wegfuehrung'} = 1;
522$sperre{'Q3'} = 0;
523$sperre_file = "gesperrt";
524# immediate_replot: 0 = none, 1 = immediate, 2 = deferred
525my($immediate_replot, $immediate_recalc) = (1, 1);
526$auto_visible = 1;
527%tag_visibility =
528  ('p-hoehe'  => 1,
529   'str-s-NN' => 0.5,
530   'str-s-N'  => 0.5,
531   'p-lsa'    => 0.5,
532   'p-o-0'    => 0.375,
533   'p-o-1'    => 0.25,
534   'str-s-H'  => 0.125,
535   'p-o-2'    => 0.125,
536  );
537$map_draw = 0;
538$map_default_type = 'berlinmap';
539$use_map_fallback = 1;
540$map_surround = 0;
541$dont_delete_map = 1;
542$use_current_coord_prefix = 0;
543$coord_prefix = undef;
544$coordlist_lbox_nl = "";
545$min_cache_decider_time = 0.500; # 500ms, dann wird gecached
546$steady_mark = 0;
547$lowmem = 0;
548$use_logo = 1 if !defined $use_logo;
549$center_loaded_route = 0;
550$zoom_loaded_route = 1;
551$zoom_new_route = 0;
552$zoom_new_route_chooseort = 1;
553$special_edit = '';
554$map_mode = MM_SEARCH;
555%b2_mode_desc = (B2M_NONE,	 M"Nichts",
556		 B2M_SCAN,	 M"Scanning",
557		 B2M_FASTSCAN,	 M"Fast Scanning",
558		 B2M_AUTOSCROLL, M"Autoscrolling",
559		 B2M_DELLAST,	 M"Letzten Punkt l�schen",
560		);
561# Default ist rot, weil das Orange von power oder wind schlecht zu erkennen ist
562$mark_color    = 'red'; # Farbe der Markierung in mark_street et al.
563$mark_color_overview = 'blue'; # better than red because it does not conflict with Bundesstra�en
564$gps_waypoints = 50;
565$gps_waypointlength = 10;
566$gps_waypointcharset = 'ascii';
567$gps_needuniqueroutenumber = 0;
568
569### Fonts
570$standard_height = 12;
571set_sans_serif_font_family();
572
573### Images
574@image_type_order = ('png', # best quality
575		     'jpg', # 24bit, good quality XXX what about non-real world photo images?
576		     'xpm', # small memory size (8bit pixmaps)
577		     'gif',
578		    );
579
580###################################################################
581$really_no_www = $os eq 'win'; # Trumpet und Win32Sock h�ngen zu lange, wenn es keine Verbindung gibt XXX aber moderne Windows nicht mehr, oder?
582$no_map = !$devel_host && (!defined $ENV{USER} || $ENV{USER} !~ /^(eserte|rezic|srezic|slavenr)$/);
583$abbiege_optimierung = 0;
584# Verlust in Metern beim Linksabbiegen ohne Ampel
585# XXXXX und beim Geradeausfahren??????
586$abbiege_penalty = { 'H'   => 70, # entspricht ca. 10s bei 25km/h
587		     'HH'  => 140, # entspricht ca. 20s bei 25km/h
588		     'BAB' => 140, # h�h? f�r Radfahrer?
589		     'B'   => 140,
590		   };
591$lost_strecke_per_ampel = 50; # verlorene Strecke pro Ampel in m # XXX F ...?
592%lost_time_per_ampel = ('X' => 15,
593			'F' => 5,
594			# B?
595		       ); # verlorene Zeit pro Ampel in s
596$average_v = 0;
597
598$radwege_optimierung = 0;
599for(0..$#Radwege::category_order, "") {
600    $radwege_speed{"RW$_"} = 100;
601}
602
603@strcat_order = qw(B HH H NH N NN);
604if (0) { # not enabled by default
605    unshift @strcat_order, "BAB";
606}
607
608$steigung_penalty = {};
609$strecke = 0;
610$dim_color = '#999999';
611$unit_s = 'km';
612$next_is_undo = 0;
613# kontrolliert das Zeichnen der Start/Zielflagge:
614@do_flag{qw(start via ziel)} = (1, 1, 1);
615# $in_search: wahr, wenn gerade gesucht wird
616
617use enum qw(:SRP_ COORD TYPE);
618
619$aufschlag = 1; # XXX ???
620
621# Do as early as possible to avoid warnings:
622if (!$ENV{HOME} || !-d $ENV{HOME}) { # z.B. unter Win32
623    $ENV{HOME} = $FindBin::RealBin;
624}
625
626# Weather variables section
627$wetter_force_update = 1 if !defined $wetter_force_update;
628$wetter_route_update = 0 if !defined $wetter_route_update;
629$wetter_station = 'uptodate' if !defined $wetter_station;
630@wetter_dir = ("$ENV{HOME}/doc/met", "/home/e/eserte/doc/met");
631%wetter_zuordnung =
632  ('dahlem1'   => 'wetter-full',
633   'dahlem2'   => 'wetter',
634   #'tempelhof' => 'wetter-tempelhof',
635  );
636%wetter_name =
637  ('dahlem1'   => M"Dahlem (FU, lang)",
638   'dahlem2'   => M"Dahlem (FU, kurz)",
639   #'tempelhof' => M"Tempelhof (DWD)",
640  );
641%wetter_full = ('dahlem1' => 1);
642$temperature = 20; # degrees Celsius
643BBBikeCalc::init_wind();
644
645use enum qw(:WIND_COLOR_ RED GREEN BLUE NAME);
646
647%wind_colors = (-2 => [qw(255   0   0  red)],
648		-1 => [qw(255 165   0  orange)],
649		 0 => [qw(255 215   0  gold)],
650		 1 => [qw(154 205  50  YellowGreen)],
651		 2 => [qw(105 139 105  DarkSeaGreen4)],
652	       );
653
654## DEBUG_BEGIN
655#BEGIN{mymstat("use vars f�r postscript...");}
656## DEBUG_END
657### Postscript
658$ps_color    = 'color';
659$ps_rotate   = 1;
660$ps_scale_a4 = 1;
661$ps_fixed_font = "Courier7";
662$nr = -1; # number of points in route (XXX correct???)
663
664# User directories (~/.bbbike, route directory, cache)
665my $home = $ENV{HOME};
666if ($os eq 'win') {
667    $home = Win32Util::get_user_folder();
668    if (-d $home) {
669        $bbbike_configdir = catfile($home, "BBBike");
670    }
671}
672if (!defined $bbbike_configdir) {
673    $bbbike_configdir = defined $home ? catfile($home, ".bbbike") : "/bbbike.cfg";
674}
675if (!-d $bbbike_configdir) {
676    mkdir $bbbike_configdir, 0700;
677}
678if (-d $bbbike_configdir) {
679    $bbbike_routedir = catfile($bbbike_configdir, "route");
680    if (!-d $bbbike_routedir) {
681	mkdir $bbbike_routedir, 0700;
682    }
683}
684$oldpath = $bbbike_routedir;
685$save2_path = $home;
686
687{
688    # Hopefully robust determination of temporary directory
689    die "\$bbbike_configdir is not set" if !defined $bbbike_configdir;
690    my $cachedir = catfile($bbbike_configdir, "cache");
691    if (!-d $cachedir) {
692	mkdir $cachedir, 0700;
693    }
694    $cache_root = (-d $cachedir && -w $cachedir
695		   ? $cachedir
696		   : $tmpdir);
697    $Karte::cache_root = $cache_root;
698    $Strassen::Util::cachedir = $cache_root;
699}
700
701{
702    for my $_testdir ('__SPEC__',
703		      $ENV{TMPDIR},
704		      ($^O eq 'MSWin32' ? ($ENV{TEMP}, $ENV{TMP}) : ()),
705		      "/tmp",
706		      "/temp",
707		      '__CONFIG__',
708		     ) {
709	my $testdir = $_testdir;
710	next if !defined $testdir;
711	if ($_testdir eq '__SPEC__') {
712	    $testdir = eval { require File::Spec; File::Spec->tmpdir };
713	    next if !defined $testdir;
714	} elsif ($_testdir eq '__CONFIG__') {
715	    $testdir = catfile($bbbike_configdir, "tmp");
716	    if (!-d $testdir) {
717		mkdir $testdir, 0700;
718	    }
719	}
720
721	if (-d $testdir && -w $testdir) {
722	    $tmpdir = $testdir;
723	    last;
724	}
725    }
726    if (!defined $tmpdir) {
727	$tmpdir = "/tmp";
728	print STDERR M("Achtung: es konnte kein schreibbares tempor�res Verzeichnis gefunden werden. Unter Umst�nden sind einige Operationen nicht m�glich.") . "\n";
729    }
730}
731
732# XXX $do_wwwmap stuff is sort-of obsolete. Remove completely?
733Karte::preload('Berlinmap2000');
734$do_wwwmap = (! $Karte::Berlinmap2000::obj ||
735	      ! -e $Karte::Berlinmap2000::obj->fs_dir);
736if ($devel_host) {
737    $Karte::cache_root = "/usr/www/berlin";
738}
739
740# Hook init
741foreach (qw(before_plot after_plot new_route del_route after_resize
742	    after_new_layer after_delete_layer
743	    after_change_visibility after_change_stacking
744	    delete_background_images
745	  )) {
746    new Hooks $_;
747}
748
749eval { local $SIG{'__DIE__'};
750       do "$FindBin::RealBin/$progname" . "_0.config" };
751
752## DEBUG_BEGIN
753#BEGIN{mymstat("before getopt BEGIN");} mymstat("before getopt");
754## DEBUG_END
755
756handle_options();
757
758# at this point the $devel_host setting is valid (_set_public was maybe called)
759if ($devel_host && !$public && !grep { "danger" eq $_ } @Strassen::Dataset::comments_types) {
760    push @Strassen::Dataset::comments_types, "danger";
761}
762@comments_types = @Strassen::Dataset::comments_types;
763
764if ($lowmem) {
765    @image_type_order = ('xpm', 'gif', 'jpg', 'png');
766}
767
768## DEBUG_BEGIN
769#mymstat("after getopt processing");
770## DEBUG_END
771
772use vars qw($city_obj $dataset_title);
773if (!defined $city && !defined $datadir) {
774    $city = "Berlin";
775    $country = "DE";
776}
777if (defined $city) {
778    require Geography;
779    $city_obj = Geography->new($city, $country);
780    if (!$city_obj) {
781	die Mfmt("Kann keine passende Datei f�r Stadt=%s und Land=%s finden",
782		 $city, (defined $country ? $country : M("(unbestimmt)")));
783    }
784    set_datadir($city_obj->datadir, -clearold => 1);
785    %global_search_args = $city_obj->search_args;
786    if ($city eq "Berlin") {
787	$no_original_datadir = 0; # XXX Was bedeutet das genau?
788	$dataset_title = undef;
789    } else {
790	$no_original_datadir = 1; # XXX Was bedeutet das genau?
791	$dataset_title = $city . " " . $country;
792    }
793    if ($city_obj->scrollregion) {
794	@scrollregion = $city_obj->scrollregion;
795	$normal_scrollregion = $scrollregion[2]-$scrollregion[0];
796	for (@scrollregion) { $_ *= $scale };
797    }
798} elsif ($datadir) {
799    set_datadir($datadir, -clearold => 1);
800    $no_original_datadir = 1;
801    $dataset_title = $city_obj && $city_obj->{dataset_title} ? $city_obj->{dataset_title} : basename($datadir);
802}
803if (!$city_obj) {
804    require Geography::Base;
805    $city_obj = Geography::Base->new;
806    warn "Fallback to unspecified city object...\n";
807}
808
809if ($city_obj->can("skip_features")) {
810    %skip_features = map{($_,1)} $city_obj->skip_features;
811}
812# XXX nicer solution?
813if ($city_obj->is_osm_source) {
814    $sBAB_two_track_width = 9999; # effectively turning off
815}
816
817# define_item_attribs should be called after determining the $city
818define_item_attribs();
819generate_plot_functions();
820
821if (!@scrollregion) {
822    my $init_scrollregion = $Karte::Standard::init_scrollregion;
823    $normal_scrollregion = $init_scrollregion*$scale;
824    @scrollregion = ((-$normal_scrollregion) x 2,
825		     ($normal_scrollregion)  x 2);
826}
827
828# XXX Henne-und-Ei-Problem: ich w�rde gerne Plotting-Defaults anhand der
829# -city-Option setzen (z.B. Zeichnen der Landstra�en f�r OR). Problem:
830# das initiale Setzen von %init_str geschieht auch w�hrend handle_options
831# Ich br�uchte also eine Art pre_handle_options, um erst einmal die
832# -city-Option herauszufischen und dann den Rest handhaben...
833
834if ($environment ne "normal") {
835    eval { local $SIG{'__DIE__'};
836	   require $progname . "_" . $environment . ".config" };
837}
838
839## DEBUG_BEGIN
840#mymstat("before advanced");
841## DEBUG_END
842if ($advanced) {
843    $^W = 1;
844    Karte::preload(':all');
845    require BBBikeAdvanced;
846}
847
848# XXX The MM_DRAG (move) button could be removed completely some day.
849use vars qw($MM_DRAG_IS_OBSOLETE);$MM_DRAG_IS_OBSOLETE = 1;
850
851$coord_system_obj = $Karte::Standard::obj;
852$coord_system     = $coord_system_obj->token;
853
854if ($verbose) {
855    set_verbose();
856}
857
858if ($proxy) {
859    $wettermeldung2::proxy = $proxy;
860}
861
862if ($do_www) {
863    $wetter_source{'www'}   = 1;
864}
865if (wetter_dir_exists() and !$public) {
866    $wetter_source{'db'}    = 1;
867}
868if ($devel_host and !$public) {
869    $wetter_source{'local'} = 1;
870}
871# XXX ja?
872# �berpr�fen ... auf win32 wird trotz do_www=0 trotzdem geladen?!
873if (!grep($_, values %wetter_source) and $do_www and !$really_no_www) {
874    $wetter_source{'www'} = 1;
875}
876
877# XXX DEL: all occurences of $XXX_use_old_R_symbol
878use vars qw($XXX_use_old_R_symbol);
879$XXX_use_old_R_symbol = 0; # !$devel_host; # Old ugly R symbol or "eisenbahn"
880
881if ($net_type ne 's' && $coloring eq 'wind') {
882    $coloring = 'black';
883}
884reset_wind();
885## DEBUG_BEGIN
886#mymstat("before update_weather");
887## DEBUG_END
888update_weather(1) if $want_wind;
889## DEBUG_BEGIN
890#mymstat("after update_weather");
891## DEBUG_END
892$wetter_route_update = 1;
893
894# Always use Bikepower (e.g. mandatory for Steigungsoptimierung)
895$bikepwr = 1;
896if ($bikepwr) {
897    eval {
898	require BikePower;
899    };
900    if ($@) {
901 	status_message(Mfmt("Kann BikePower nicht laden: %s", $@), 'err');
902 	$bikepwr = 0;
903    } else {
904	if ($verbose && $BikePower::has_xs) {
905	    print STDERR M"Verwende die XS version von BikePower\n";
906	}
907 	$bp_obj = new BikePower;
908	$bp_obj->given('P');
909	$bp_obj->temperature($temperature);
910
911	set_corresponding_power();
912    }
913}
914if (!@power) {
915    @power = (50, 100);
916}
917
918TRY_SPEED_POWER_REFERENCE_STRING: {
919    $active_speed_power{Type} = 'speed';
920    $active_speed_power{Index} = 0;
921    if (defined $speed_power_reference_string) {
922	my($type, $val) = split /:/, $speed_power_reference_string;
923	if ($type =~ /^(speed|power)$/) {
924	    my $i = 0;
925	    for ($type eq 'speed' ? @speed : @power) {
926		if ($val eq $_) {
927		    $active_speed_power{Index} = $i;
928		    $active_speed_power{Type} = $type;
929		    last TRY_SPEED_POWER_REFERENCE_STRING;
930		}
931		$i++;
932	    }
933	    print STDERR "Referenzgeschwidigkeit/-leistung $type $val wird ignoriert\n";
934	} else {
935	    print STDERR "Die Option -reference sollte im Format type:value sein, wobei type entweder speed oder power ist und value die entsprechende Geschwindigkeit in km/h oder Leistung in W\n";
936	}
937    }
938}
939
940mk_speed_txt();
941for(my $i = 0; $i <= $#speed; $i++) {
942    $ampel_count->{"speed"}[$i] = 1;
943    $kopfstein_count->{"speed"}[$i] = 1;
944}
945for(my $i = 0; $i <= $#power; $i++) {
946    $ampel_count->{"power"}[$i] = 1;
947    $kopfstein_count->{"power"}[$i] = 1;
948}
949
950eval {
951    set_coord_output_sub();
952}; warn __LINE__ . ": $@" if $@;
953
954change_net_type();
955
956if ($do_wwwmap && $devel_host) {
957    $map_default_type = 'b2004';
958}
959
960if ($all_outline) {
961    $str_outline{'s'} =
962    $str_outline{'l'} =
963    $str_outline{'w'} =
964    $str_outline{'i'} = 1;
965}
966
967if (defined $init_scope) {
968    if    ($init_scope eq 'city')   { city_settings()   }
969    elsif ($init_scope eq 'region') { region_settings() }
970    elsif ($init_scope eq 'jwd')    { jwd_settings()    }
971}
972
973if ($visual) {
974    push(@extra_args, -visual => $visual);
975}
976
977if ($Tk::VERSION < 803 || $Tk::VERSION >= 804.025) {
978    eval { require Tk::UnderlineAll };
979    warn __LINE__ . ": $@" if $@ && $verbose;
980}
981
982eval { local $SIG{'__DIE__'};
983       do "$FindBin::RealBin/$progname" . "_1.config" };
984
985## DEBUG_BEGIN
986#BEGIN{mymstat("irgendwo in der mitte BEGIN");} mymstat("irgendwo in der mitte");
987## DEBUG_END
988
989if (!defined $top) {
990    $top = MainWindow->new(@extra_args);
991    $top->{initial_iconic} = $top->state eq 'iconic';
992
993    $top->scaling($scaling) if defined $scaling && $scaling ne "";
994
995    # Es gibt gute Gr�nde, f�r CloseMainWin kein Escape zu nehmen
996    # (damit k�nnen Vorg�nge abgebrochen werden). Verwendung von C-q,
997    # weil das mittlerweile quasi-Standard (Gtk, Qt/KDE, Windows) ist.
998    $top->eventAdd(qw[<<CloseMainWin>> <Control-c> <Control-q>]);
999    $top->eventAdd(qw[<<CloseWin>>     <Control-c> <Escape>]);
1000
1001    if ($os eq 'win') { # vorerst, Windows kann keine tearoffs
1002	$top->optionAdd("*tearOff", "false", "startupFile");
1003    }
1004    if ($os ne 'win') { # use standard bg color on Windows
1005	for (qw(background highlightBackground)) {
1006	    $top->optionAdd("*$_", 'grey80', 'startupFile');
1007	}
1008	# Workaround for a KDE 3.x problem: KDE sets background, but not
1009	# highlightBackground options which looks quite ugly.
1010	my $bg = $top->optionGet("background", "Background");
1011	if ($top->optionGet("highlightBackground", "HighlightBackground") ne $bg) {
1012	    $top->optionAdd("*highlightBackground", $bg, 'interactive');
1013	}
1014	# Unter Windows sollten Balloons eigentlich -bg => white sein XXX
1015	for (qw(Balloon CanvasBalloon)) {
1016	    $top->optionAdd("*$_.background", '#C0C080', 'startupFile');
1017	}
1018	for (qw(Scale Scrollbar)) {
1019	    $top->optionAdd("*$_.troughcolor", "grey95", "startupFile");
1020	}
1021    }
1022    # This is the list of widgets with some "action" area (editable or
1023    # selectable). It seems that the consensus in the GUI world is to
1024    # have this widgets in a brighter color (like Tix, Gtk, Windows...).
1025    # Do it so.
1026    # Browse is for Tk::HistEntry::Browse
1027    for (qw(Browse Entry NumEntry Date*NumEntryPlain PathEntry
1028	    Listbox KListbox K2Listbox
1029	    TixHList HList Text ROText BrowseEntry.LabEntry SimpleHistEntry
1030	    ListboxSearchAnything
1031	   )) {
1032	if ($os eq 'win') {
1033	    $top->optionAdd("*$_.background", "SystemWindow", "startupFile");
1034	} else {
1035	    $top->optionAdd("*$_.background", "grey95", "startupFile");
1036	}
1037    }
1038    # Introduce a www browser-like cursor feeling:
1039    for (qw(Button Checkbutton Radiobutton Menubutton
1040	    FlatCheckbox FlatRadiobutton FireButton)) {
1041	$top->optionAdd("*$_.cursor", "hand2", "startupFile");
1042    }
1043
1044    if (0) { # ... naja, m�sste ein Designer ran ... au�erdem with -tile nicht mehr unterst�tzt (?), und mit Windows ging's noch nie
1045	my $bg = $top->Photo(-file => Tk::findINC("images/bg.gif"));
1046	for (qw(Toplevel Label Button Checkbutton Radiobutton FlatBut
1047		FlatCheckbox FlatRadiobutton FireButton Menubutton Frame Pane),
1048	     "Bbbike Chooser", "Bbbike Copyright", "Bbbike Window",
1049	     "Bbbike Extended Chooser", "Bbbike Overview",
1050	     "Bbbike Routeinfo") {
1051	    $top->optionAdd("*$_.tile" => $bg) if $bg;
1052	}
1053	$top->optionAdd("*highlightBackground" => "white");
1054    }
1055}
1056
1057## DEBUG_BEGIN
1058#BEGIN{mymstat("after basic MainWindow setup BEGIN");} mymstat("after basic MainWindow setup");
1059## DEBUG_END
1060
1061# KDE initialisation
1062if ($run_under_kde) {
1063    eval {
1064	require KDEUtil;
1065	if ($kde = new KDEUtil -top => $top, -checkrunning => 1) {
1066	    my $kde_focus_policy =
1067		KDEUtil::WM::get_config($kde, 'General', 'FocusPolicy');
1068	    local $^W = 0;
1069	    $focus_policy = ($kde_focus_policy eq 'ClickToFocus'
1070			     ? 'click'
1071			     : 'follow');
1072	    $kde->kde_config_for_tk;
1073	}
1074    };
1075    warn __LINE__ . ": $@" if $@; # XXX and $verbose
1076}
1077
1078## DEBUG_BEGIN
1079#BEGIN{mymstat("after KDE initialisation");} mymstat("after KDE initialisation");
1080## DEBUG_END
1081
1082if (!defined $focus_policy) {
1083    if ($os eq 'unix') {
1084	#XXX $focus_policy = 'follow';
1085	$focus_policy = 'click';
1086    } else {
1087	$focus_policy = 'click';
1088    }
1089}
1090
1091if ($focus_policy eq 'follow') {
1092    @popup_style = ('-popover', 'cursor');
1093    # This seems to be a good idea for all platforms, but
1094    # is dangerous where focus also means "raise" and the
1095    # toplevel is not marked as transient. Therefore
1096    # first check if this work OK and maybe always enable
1097    # in BBBike 3.16 XXX
1098    #
1099    # Another problem, the reason why I disabled this for now: if
1100    # the search window is redisplayed by hitting the "/"
1101    # key, then the focus is not set to the search field.
1102    #
1103    #$top->focusFollowsMouse;
1104} else {
1105    @popup_style = ();
1106}
1107
1108# erst *nach* new MainWindow aufrufen (wegen Tk::CmdLine)
1109if (@ARGV) {
1110    $preload_file = $ARGV[0];
1111}
1112
1113# Die folgende Reihenfolge ist wichtig einzuhalten:
1114# * Geometry ermitteln und in @want_extends ablegen, aber noch nicht setzen
1115#   (set_default_geometry, geometry_dependent_settings)
1116# * Zeichens�tze ermitteln und Default einstellen (set_fonts)
1117# * EmptyMenubar zeichnen
1118# * Geometry setzen
1119
1120use enum qw(:GEOMETRY_ X Y WIDTH HEIGHT);
1121
1122# Geometry
1123set_default_geometry();
1124geometry_dependent_settings();
1125
1126# dots per inch und mm, must be called before set_fonts
1127$top_dpmm = $top->screenwidth/$top->screenmmwidth;
1128$top_dpi  = $top_dpmm*25.4;
1129$ps_image_res = int($top_dpi) . "x" . int($top_dpi);
1130
1131## DEBUG_BEGIN
1132#BEGIN{mymstat("before setfonts BEGIN");} mymstat("before setfonts");
1133## DEBUG_END
1134
1135# Zeichens�tze
1136set_fonts();
1137
1138## DEBUG_BEGIN
1139#BEGIN{mymstat("after setfonts BEGIN");} mymstat("after setfonts");
1140## DEBUG_END
1141
1142if ($Tk::VERSION < 800) {
1143    $standard_menubar = 0;
1144}
1145if ($standard_menubar && !$top->cget(-menu)) {
1146    require BBBikeMenubar;
1147    BBBike::Menubar::EmptyMenubar(); # Platz reservieren ...
1148    # Tk feature: menu bar is not counted to geometry
1149    my $menu_height;
1150    if ($os eq 'unix') {
1151	$top->withdraw;
1152	$top->update;
1153	$menu_height = ($top->wrapper)[1];
1154    } else {
1155	# wrapper[1] is not implemented on Windows ... guess menu height
1156	$menu_height = 20;
1157    }
1158    if ($want_extends[GEOMETRY_HEIGHT] =~ /^-/) {
1159	$want_extends[GEOMETRY_HEIGHT] += $menu_height;
1160    } else {
1161	$want_extends[GEOMETRY_HEIGHT] -= $menu_height;
1162    }
1163}
1164
1165if (@want_extends) {
1166    if (($want_extends[GEOMETRY_WIDTH]  < 30 && $want_extends[GEOMETRY_WIDTH] !~ /^-/) ||
1167	($want_extends[GEOMETRY_HEIGHT] < 20 && $want_extends[GEOMETRY_HEIGHT] !~ /^-/) ||
1168	$want_extends[GEOMETRY_X] < 0 ||
1169	$want_extends[GEOMETRY_Y] < 0) {
1170	print STDERR M("Die Fenstergr��e wird wegen ung�ltiger Werte nicht gesetzt: ")
1171	    . join(", ", @want_extends), "\n";
1172    } else {
1173	geometry($top, @want_extends);
1174	@want_extends = ();
1175    }
1176}
1177
1178if (defined $init_scale_massstab) {
1179    if ($init_scale_massstab =~ m{^1:(\d+)$}) {
1180	my $nenner = $1;
1181	my $nenner_now = calc_mapscale_nenner();
1182	# to the old $scale form:
1183	$init_scale_massstab = ($scale*$nenner_now)/$nenner;
1184    }
1185
1186    if ($init_scale_massstab > 0) {
1187	my $oldscale = $scale;
1188	set_canvas_scale($init_scale_massstab);
1189	my $change_scale_factor = $scale/$oldscale;
1190	foreach (@scrollregion) {
1191	    $_ *= $change_scale_factor;
1192	}
1193    } else {
1194	print STDERR "Ung�ltiger Skalierungswert <$init_scale_massstab> wird ignoriert\n";
1195    }
1196}
1197
1198$top->title("$progname $VERSION" .
1199	    ($dataset_title ? " [$dataset_title]" : "")
1200	   );
1201
1202my $has_icon = 0;
1203my $set_toplevel_icon;
1204$srtbike_photo = load_photo($top, 'srtbike_solid');
1205$srtbike16_icon = load_photo($top, 'srtbike16'); # used in info window
1206if ($os eq 'win' || $^O eq 'cygwin') {
1207    # Prefer .ico
1208    my $icon;
1209    if ($Tk::VERSION >= 804.027 and
1210	$icon = $FindBin::RealBin.'/images/srtbike.ico' and
1211	-r $icon and
1212	eval {
1213	    $top->iconbitmap($icon);
1214	    1;
1215	}) {
1216	$has_icon = 1;
1217	$set_toplevel_icon = sub {
1218	    my $self = shift;
1219	    eval {
1220		$self->iconbitmap($icon);
1221	    };
1222	};
1223    } else {
1224	# srtbike32.* looks broken on Win98 and Vista,
1225	# and srtbike16.* looks broken on WinXP and Vista
1226	if ($ENV{OS} && $ENV{OS} eq 'Windows_NT') { # this seems to be the case for XP
1227	    $srtbike_icon = load_photo($top, 'srtbike32');
1228	} else {
1229	    $srtbike_icon = $srtbike16_icon;
1230	}
1231    }
1232} else {
1233    # 16x16 is the preferred size for mini-icons in KDE
1234    # works also for twm (however, a little bit tiny)
1235    $srtbike_icon = $srtbike16_icon;
1236    if ($srtbike_icon) {
1237	$top->iconmask('@' . $FindBin::RealBin . '/images/srtbike16_mask.xbm');
1238    }
1239}
1240
1241if (!$has_icon) {
1242    # In ->Icon wird auch ein ->update durchgef�hrt:
1243    # XXX Unter Unix vielleicht darauf verzichten und iconimage stattdessen verwenden?
1244    # XXX Also set icon according to freedesktop specs.
1245    if (defined $srtbike_icon) {
1246	$top->Icon(-image => $srtbike_icon);
1247	$set_toplevel_icon = sub {
1248	    my $self = shift;
1249	    eval {
1250		$self->iconimage($main::srtbike_icon);
1251	    };
1252	};
1253    }
1254}
1255
1256if ($devel_host && $set_toplevel_icon) {
1257    # every toplevel in app should get bbbike icon
1258    require Tk::Toplevel; # make sure it's loaded
1259    package Tk::Toplevel;
1260    *InitObject = *InitObject; # cease warnings
1261    *InitObject = sub {
1262	my($self,$args) = @_;
1263	$self->SUPER::InitObject($args);
1264	    # setting icon may fail in other mainwindows
1265	    $self->afterIdle(sub { $set_toplevel_icon->($self) });
1266    };
1267}
1268
1269{
1270    # experimental...
1271    my $freedesktop_lib = "$ENV{HOME}/work/Tk-FreeDesktop-Wm/blib/lib";
1272    if (-d $freedesktop_lib) {
1273	if (!eval {
1274	    local @INC = ($freedesktop_lib, @INC);
1275	    require Tk::FreeDesktop::Wm;
1276	    my $fd = Tk::FreeDesktop::Wm->new(mw => $top);
1277	    $fd->set_wm_icon("$FindBin::RealBin/images/srtbike_mini.xpm");
1278	    1;
1279	}) {
1280	    warn "Cannot load Tk::FreeDesktop::Wm ($@), no NET icon support...";
1281	}
1282    }
1283}
1284
1285if ($splash_screen) {
1286    $splash_screen->Raise; # raise after the first ->update on $top, otherwise on Windows the splash screen will stay obscured by the main window
1287    $splash_screen->Update(0.0, 'raise splash');
1288}
1289
1290# Define something else on X server bugs (e.g. "projecting")
1291$capstyle_round = "round";
1292
1293# erst hier ist die @power-Zuweisung abgeschlossen
1294for(my $i=0; $i <= $#power; $i++) {
1295    $bikepwr_time[$i] = 0;
1296    $bikepwr_cal[$i] = 0;
1297}
1298mk_power_txt();
1299
1300## DEBUG_BEGIN
1301#BEGIN{mymstat("after mk_power_txt BEGIN");} mymstat("after mk_power_txt");
1302## DEBUG_END
1303
1304# Zeichens�tze f�r Stra�ennamen
1305# Normal
1306if (defined $font_family && $font_family =~ /nimbus/) {
1307    # XXX nimbus is a rather obscure font found in
1308    # /usr/ports/x11-fonts/freefonts --- maybe use another?
1309    #
1310    # somewhere called "nimbus sans" without "l"
1311    $rot_font_sub  = sub { "-*-nimbus sans l-medium-r-condensed--0-" . $_[0]
1312			       . "-0-0-p-0-iso8859-1"};
1313} elsif (defined $font_family && $font_family =~ /luxi/) {
1314    # a Type 1 font --- slower and nicer
1315    $rot_font_sub = sub { '-b&h-Luxi Sans-medium-r-normal--0-' . $_[0] . '-0-0-p-0-iso8859-1'};
1316}
1317if (defined $rot_font_sub && !check_font($rot_font_sub->(120))) {
1318    print STDERR "Der Normalzeichensatz in <$font_family> kann nicht gefunden werden, der Fallback wird verwendet...\n";
1319    undef $rot_font_sub;
1320}
1321# Fallback to helvetica
1322if (!$rot_font_sub) {
1323    my $font_family = "helvetica";
1324    $rot_font_sub  = sub { "-*-$font_family-medium-r-normal--0-" . $_[0]
1325			     . "-0-0-p-0-iso8859-1"};
1326}
1327# Bold
1328if (defined $font_family && $font_family =~ /nimbus/) {
1329    $rot_bold_font_sub  = sub { "-*-nimbus sans l-bold-r-condensed--0-" . $_[0]
1330				  . "-0-0-p-0-iso8859-1"};
1331} elsif (defined $font_family && $font_family =~ /luxi/) {
1332    $rot_bold_font_sub = sub { '-b&h-Luxi Sans-bold-r-normal--0-' . $_[0] . '-0-0-p-0-iso8859-1'};
1333}
1334if (defined $rot_bold_font_sub && !check_font($rot_bold_font_sub->(120))) {
1335    print STDERR "Der Fettschriftzeichensatz in <$font_family> kann nicht gefunden werden, der Fallback wird verwendet...\n";
1336    undef $rot_bold_font_sub;
1337}
1338# Fallback to helvetica bold
1339if (!$rot_bold_font_sub) {
1340    my $font_family = "helvetica";
1341    $rot_bold_font_sub  = sub { "-*-$font_family-bold-r-normal--0-" . $_[0]
1342				  . "-0-0-p-0-iso8859-1"};
1343}
1344%category_rot_font =
1345  ('NN'  => $rot_font_sub,
1346   'N'   => $rot_font_sub,
1347   'NH'  => $rot_font_sub,
1348   'H'   => $rot_bold_font_sub,
1349   'HH'  => $rot_bold_font_sub,
1350   'B'   => $rot_bold_font_sub,
1351   'BAB' => $rot_bold_font_sub,
1352   'W'   => $rot_bold_font_sub);
1353
1354# According to
1355# http://web.archive.org/web/20020124125029/www.iarchitect.com/color.htm
1356# using colors for dialog buttons is not advised. Well, anyway...
1357$top->optionAdd("*ok*foreground"      => 'green4');
1358$top->optionAdd("*ok*text"            => M"OK");
1359if ($Tk::VERSION >= 800) {
1360    $top->optionAdd("*ok*default"         => 'active');
1361}
1362$top->optionAdd("*apply*foreground"   => 'yellow4');
1363$top->optionAdd("*apply*text"         => M"�bernehmen");
1364$top->optionAdd("*search*foreground"  => 'yellow4');
1365$top->optionAdd("*search*text"        => M"Suchen");
1366# Fix strangely colored Pod menu
1367$top->optionAdd("*pod*search*foreground" => 'black');
1368$top->optionAdd("*show*foreground"    => 'yellow4');
1369$top->optionAdd("*show*text"          => M"Zeigen");
1370$top->optionAdd("*default*foreground" => 'yellow4');
1371$top->optionAdd("*default*text"       => M"Voreinstellung");
1372$top->optionAdd("*cancel*foreground"  => 'red');
1373$top->optionAdd("*cancel*text"        => M"Abbrechen");
1374#XXX Experiment for Tk804. Problems too solve: maybe icon too large for small screens/buttons; images should be transparent: {my $p=load_photo($top, "cross", -name => "cross");for(qw(close cancel)) { $top->optionAdd("*$_*compound","left"); $top->optionAdd("*$_*image","cross")}}
1375$top->optionAdd("*close*foreground"   => 'red');
1376$top->optionAdd("*close*text"         => M"Schlie�en");
1377$top->optionAdd("*end*foreground"     => 'green4');
1378$top->optionAdd("*end*text"           => M"Schlie�en");
1379
1380if ($small_icons) {
1381    $top->optionAdd("*Button*borderWidth" => 1);
1382    $top->optionAdd("*Checkbutton*borderWidth" => 1);
1383}
1384
1385$top->optionAdd("*FlatBut*borderWidth" => 0);
1386$top->optionAdd("*FlatBut*padX" => 1);
1387$top->optionAdd("*FlatBut*padY" => 0);
1388
1389$top->optionAdd("*SmallBut*padX" => 1);
1390$top->optionAdd("*SmallBut*padY" => 1);
1391
1392if ($use_logo and (!$splash_screen or !$splash_screen->{Exists})) {
1393    show_logo();
1394}
1395
1396if ($use_balloon) {
1397    eval {
1398	require Tk::Balloon;
1399	# -balloonposition: Ansonsten kann es bei Buttons vorkommen, dass
1400	# der Balloon Teile der Klickfl�che �berdeckt.
1401	$balloon = $top->Balloon(-balloonposition => "mouse");
1402    };
1403}
1404if (!defined $balloon) {
1405    eval q{
1406	package Tk::FakeBalloon; # AUTOLOAD: ignore
1407	@Tk::FakeBalloon::ISA = qw(Tk::Label);
1408	Construct Tk::Widget "FakeBalloon";
1409	sub attach {}
1410	sub configure {}
1411	sub IsWidget { 0 } # for Tk::Exists
1412	package main;
1413	$balloon = $top->FakeBalloon;
1414    };
1415    warn $@ if $@;
1416}
1417
1418## DEBUG_BEGIN
1419#BEGIN{mymstat("after balloon BEGIN");} mymstat("after balloon");
1420## DEBUG_END
1421
1422# XXX if !perl2exe
1423if (!$lowmem) {
1424    if (eval { require Tk::CanvasBalloon; 1 }) {
1425	$c_balloon = $top->CanvasBalloon(-initwait => $c_balloon_wait,
1426					 -show => $use_c_balloon);
1427    }
1428}
1429
1430## DEBUG_BEGIN
1431#BEGIN{mymstat("after canvasballoon BEGIN");} mymstat("after canvasballoon");
1432## DEBUG_END
1433
1434TRY: {
1435    last TRY unless $use_contexthelp;
1436    if (!eval {
1437	require Tk::ContextHelp;
1438	Tk::ContextHelp->VERSION(0.05); # Win32 check
1439    }) {
1440	$use_contexthelp = 0;
1441	last TRY;
1442    }
1443    $ch = $top->ContextHelp('-podfile' => "$FindBin::RealBin/$FindBin::Script" . ".pod");
1444}
1445if (!defined $ch) {
1446    eval q{
1447	package Tk::ContextHelp; # AUTOLOAD: ignore
1448	sub attach {}
1449	sub activate {}
1450	sub HelpButton { shift; shift->Label(-padx => 0, -pady => 0) }
1451	package main;
1452	$ch = bless {}, "Tk::ContextHelp";
1453    };
1454}
1455
1456# This is a hack to fix the background color of BrowseEntry's entry
1457# widget. Maybe something similar should go into official BrowseEntry?
1458# However, if this passes a "test phase" it should be available for
1459# all.
1460if ($devel_host) {
1461    require Tk::BrowseEntry;
1462    *Tk::MyBrowseEntry::oldPopulate = \&Tk::BrowseEntry::Populate;
1463    *Tk::BrowseEntry::Populate = sub {
1464	my $w = shift;
1465	Tk::MyBrowseEntry::oldPopulate($w, @_);
1466	$w->ConfigSpecs(-background=>['SELF']);
1467    };
1468}
1469
1470## DEBUG_BEGIN
1471#BEGIN{mymstat("after contexthelp BEGIN");} mymstat("after contexthelp");
1472## DEBUG_END
1473
1474$frame = $top->Frame;
1475$frame->pack(-side => "top", -expand => "yes", -fill => "both");
1476$ctrl_frame = $frame->Frame->pack(-anchor => 'w', -fill => 'x');
1477
1478## DEBUG_BEGIN
1479#BEGIN{mymstat("before topframe BEGIN");} mymstat("before topframe");
1480## DEBUG_END
1481
1482##### Topframe #######################################################
1483
1484$splash_screen->Update(0.1, 'create top') if $splash_screen;
1485
1486$menuarrow_photo = load_photo($top, 'menupfeil');
1487
1488my $col = 0;
1489use vars qw($top_frame);
1490$top_frame = $ctrl_frame->Frame->pack(-side => 'top', -anchor => 'w',
1491				      -fill => 'x');
1492
1493use vars qw($hslabel_frame $km_frame @speed_frame $wind_frame
1494            @power_frame $percent_frame $temp_frame);
1495
1496$top_frame->gridColumnconfigure(0, -weight => 1, -minsize => 50);
1497for(1..10) {
1498    $top_frame->gridColumnconfigure($_, -weight => 0);
1499}
1500
1501$hslabel_frame  = $top_frame->Frame
1502  (-relief => 'raised', -bd => 1);
1503
1504if (!$small_icons) {
1505    $hslabel_frame->Button
1506	(-text => M('Ort/Bahnhof').':',
1507	 -class => 'FlatBut',
1508	 -highlightthickness => 0, -takefocus => 0,
1509	 -command => sub { choose_ort(qw(p o)) },
1510	)->grid(-row => 0,
1511		-column => 0,
1512		-sticky => 'w');
1513    $hslabel_frame->Button
1514	(-text => M('Stra�e/Strecke').':',
1515	 -class => 'FlatBut',
1516	 -highlightthickness => 0, -takefocus => 0,
1517	 -command => \&choose_streets,
1518	)->grid(-column => 0,
1519		-row => 1,
1520		-sticky => 'w');
1521}
1522
1523#XXXXXXXXXXXXXXXXX Ab hier POD attaches Msg-tauglich machen
1524$hslabel_frame->gridColumnconfigure(1, -weight => 1, -minsize => 10);
1525$hs_label = $hslabel_frame->Label
1526  (-textvariable => \$act_value{Haltestelle},
1527   -fg => $dim_color,
1528   -font => $font{'bold'},
1529   -anchor => 'w',
1530  )->grid(-column => 1, -row => 0, -sticky => 'w');
1531$ch->attach($hs_label, -pod => "^\\s*Ort/Haltestelle");
1532
1533$str_label = $hslabel_frame->Label
1534  (-textvariable => \$act_value{Strasse},
1535   -fg => $dim_color,
1536   -font => $font{'bold'},
1537   -anchor => 'nw',
1538  )->grid(-column => 1, -row => 1, -sticky => 'w');
1539$ch->attach($str_label, -pod => "^Stra�e/Strecke");
1540
1541$km_frame = $top_frame->Frame(-relief => 'raised',
1542			      -bd => 1);
1543my $kmcb = $km_frame->Button
1544    (-textvariable => \$unit_s,
1545     -class => 'FlatBut',
1546     -command => sub { change_unit() },
1547    )->pack;
1548if ($km_frame->can('UnderlineAll')) { $km_frame->UnderlineAll }
1549
1550$km_frame->Label(-width => 5,
1551		 -textvariable => \$act_value{Km},
1552		 -font => $font{'bold'})->pack;
1553$balloon->attach($km_frame, -msg => M"Streckenl�nge");
1554$ch->attach($km_frame, -pod => "^\\s*km");
1555
1556$percent_frame = $top_frame->Frame
1557  (-relief => 'raised', -bd => 1);
1558$percent_frame->Label(-text => "%")->pack;
1559$percent_frame->Label(-width => 4,
1560		      -textvariable => \$act_value{Percent},
1561		      -font => $font{'bold'})->pack;
1562$balloon->attach($percent_frame, -msg => M"% �ber Luftlinie");
1563$ch->attach($percent_frame, -pod => "^\\s*%");
1564
1565$ampel_klein_photo      = load_photo($top, 'ampel_klein');
1566$ampel_klein_grey_photo = load_photo($top, 'ampel_klein_grey');
1567$kopfstein_klein_photo      = load_photo($top, 'kopfstein_klein');
1568$kopfstein_klein_grey_photo = load_photo($top, 'kopfstein_klein_grey');
1569$star_photo             = load_photo($top, 'star');
1570$newlayer_photo		= load_photo($top, 'newlayer');
1571
1572for(my $i = 0; $i <= $#speed; $i++) {
1573    my $ii = $i; # f�r das sub
1574    $speed_frame[$i] = $top_frame->Frame
1575      (-relief => 'raised', -bd => 1);
1576    $ch->attach($speed_frame[$i], -pod => "^\\s*km/h");
1577    my $b = $speed_frame[$i]->Button
1578      (-textvariable => \$speed_txt[$i],
1579       -class => 'FlatBut',
1580       -command => sub { enter_speed($ii) },
1581      )->grid(-row => 0, -column => 0);
1582    {
1583	my $f = $speed_frame[$i]->Frame->grid(-row => 0, -column => 1);;
1584	$ampel_count_button->{"speed"}[$i] =
1585	    $f->Button
1586		(-image => ($ampel_count->{"speed"}[$i]
1587			    ? $ampel_klein_photo
1588			    : $ampel_klein_grey_photo),
1589		 -class => 'FlatBut',
1590		 -padx => 1,
1591		 -command => sub { change_ampel_count("speed", $ii) },
1592		)->pack;
1593	$balloon->attach($ampel_count_button->{"speed"}[$i],
1594			 -msg => M"Ampeln in Zeitberechnung aufnehmen");
1595
1596	$kopfstein_count_button->{"speed"}[$i] =
1597	    $f->Button
1598		(-image => ($kopfstein_count->{"speed"}[$i]
1599			    ? $kopfstein_klein_photo
1600			    : $kopfstein_klein_grey_photo),
1601		 -class => 'FlatBut',
1602		 -padx => 1,
1603		 -command => sub { change_kopfstein_count("speed", $ii) },
1604		)->pack;
1605	$balloon->attach($kopfstein_count_button->{"speed"}[$i],
1606			 -msg => M"Langsamfahrstrecken in Zeitberechnung aufnehmen");
1607    }
1608    my $l = $speed_frame[$i]->Button
1609      (-width => 7,
1610       -class => 'FlatBut',
1611       -command => sub {
1612	   require BBBikeAlarm;
1613	   BBBikeAlarm::enter_alarm($top, \$act_value{Time}->[$ii],
1614				    -location => get_polar_location_of_route_end());
1615       },
1616       -textvariable => \$act_value{Time}->[$i],
1617       -font => $font{'bold'},
1618      )->grid(-row => 1, -column => 0, -columnspan => 2, -sticky => "ew");
1619    foreach (qw(2 3)) {
1620	$speed_frame[$i]->bind
1621	  ("<ButtonPress-$_>" =>
1622	   sub { change_active_speed_power("speed", $ii) });
1623	$b->bind("<ButtonPress-$_>" =>
1624		 sub { change_active_speed_power("speed", $ii) });
1625	$l->bind("<ButtonPress-$_>" =>
1626		 sub { change_active_speed_power("speed", $ii) });
1627    }
1628    enter_leave_bind_for_help($speed_frame[$i],
1629			      [M"Geschwindigkeit eingeben",
1630			       M"Geschwindigkeit als Voreinstellung festlegen",
1631			       M"Geschwindigkeit als Voreinstellung festlegen",
1632			      ]);
1633    enter_leave_bind_for_help($l,
1634			      [M"Alarm setzen", undef, undef]);
1635    enter_leave_bind_for_help($ampel_count_button->{"speed"}[$i],
1636			      [M"Ampeln in Zeitberechnung aufnehmen", "", ""]);
1637    enter_leave_bind_for_help($kopfstein_count_button->{"speed"}[$i],
1638			      [M"Langsamfahrstrecken in Zeitberechnung aufnehmen", "", ""]);
1639}
1640
1641if ($bikepwr) {
1642    for(my $i = 0; $i <= $#power; $i++) {
1643	my $ii = $i;
1644	$power_frame[$i] = $top_frame->Frame
1645	  (-relief => 'raised', -bd => 1);
1646	$ch->attach($power_frame[$i], -pod => "^\\s*W\$");
1647	my $b = $power_frame[$i]->Button
1648	  (-textvariable => \$power_txt[$i],
1649	   -class => 'FlatBut',
1650	   -command => sub { enter_power($ii) },
1651	  )->grid(-row => 0, -column => 0);
1652	{
1653	    my $f = $power_frame[$i]->Frame->grid(-row => 0, -column => 1);;
1654	    $ampel_count_button->{"power"}[$i] =
1655		$f->Button
1656		    (-image => ($ampel_count->{"power"}[$i]
1657				? $ampel_klein_photo
1658				: $ampel_klein_grey_photo),
1659		     -class => 'FlatBut',
1660		     -padx => 1,
1661		     -command => sub { change_ampel_count("power", $ii) },
1662		    )->pack;
1663	    $balloon->attach($ampel_count_button->{"power"}[$i],
1664			     -msg => M"Ampeln in Zeitberechnung aufnehmen");
1665
1666if (0) { # XXX activate if implemented in updatekm()
1667	    $kopfstein_count_button->{"power"}[$i] =
1668		$f->Button
1669		    (-image => ($kopfstein_count->{"power"}[$i]
1670				? $kopfstein_klein_photo
1671				: $kopfstein_klein_grey_photo),
1672		     -class => 'FlatBut',
1673		     -padx => 1,
1674		     -command => sub { change_kopfstein_count("power", $ii) },
1675		    )->pack;
1676	    $balloon->attach($kopfstein_count_button->{"power"}[$i],
1677			     -msg => M"Langsamfahrstrecken in Zeitberechnung aufnehmen");
1678}
1679	}
1680	my $l = $power_frame[$i]->Button
1681	  (-width => 7,
1682	   -class => 'FlatBut',
1683	   -command => sub {
1684	       require BBBikeAlarm;
1685	       BBBikeAlarm::enter_alarm($top, \$act_value{PowerTime}->[$ii],
1686					-location => get_polar_location_of_route_end());
1687	   },
1688	   -textvariable => \$act_value{PowerTime}->[$i],
1689	   -font => $font{'bold'},
1690	  )->grid(-row => 1, -column => 0, -columnspan => 2, -sticky => "ew");
1691	foreach (qw(2 3)) {
1692	    $power_frame[$i]->bind
1693	      ("<ButtonPress-$_>" =>
1694	       sub { change_active_speed_power("power", $ii) });
1695	    $b->bind("<ButtonPress-$_>" =>
1696		     sub { change_active_speed_power("power", $ii) });
1697	    $l->bind("<ButtonPress-$_>" =>
1698		     sub { change_active_speed_power("power", $ii) });
1699	}
1700	enter_leave_bind_for_help($power_frame[$i],
1701				  [M"Leistung eingeben",
1702				   M"Leistung als Voreinstellung festlegen",
1703				   M"Leistung als Voreinstellung festlegen",
1704				  ]);
1705	enter_leave_bind_for_help($l,
1706				  [M"Alarm setzen", undef, undef]);
1707	# XXX not yet activated
1708	#enter_leave_bind_for_help($ampel_count_button->{"speed"}[$i],
1709	#[M"Ampeln in Zeitberechnung aufnehmen", "", ""]);
1710	#enter_leave_bind_for_help($kopfstein_count_button->{"speed"}[$i],
1711	#[M"Langsamfahrstrecken in Zeitberechnung aufnehmen", "", ""]);
1712    }
1713}
1714
1715change_active_speed_power($active_speed_power{Type}, $active_speed_power{Index});
1716
1717##### Wind & Wetter #####
1718$wind_frame = $top_frame->Frame
1719  (-relief => 'raised', -bd => 1);
1720my $wb = $wind_frame->Button
1721    (-textvariable => \$act_value{Windlabel},
1722     -class => 'FlatBut',
1723     -command => sub { update_weather(1) },
1724     -width => 22)->pack;
1725$ch->attach($wb, -pod => "^\\s*Datum der Winddaten");
1726
1727my $wff = $wind_frame->Frame->pack(-fill => 'x');
1728my $wfewb = $wff->Button
1729  (-font => $font{'bold'},
1730   -textvariable => \$act_value{Wind},
1731   -class => 'FlatBut',
1732   -command => \&enter_wind,
1733  )->pack(-fill => 'x', -expand => 1, -side => 'left');
1734$ch->attach($wfewb, -pod => "^\\s*Winddaten");
1735
1736my $wfemb = $wff->Menubutton;
1737# Hack: Verwendung von -disabledforeground, weil es kein "label"-Kommando gibt.
1738my $wbm = $wfemb->Menu(-title => M("Wetterdaten"),
1739		       -disabledforeground => $wb->cget(-foreground));
1740$wbm->command(-label => M("Wetterstation").":",
1741	      -state => 'disabled',
1742	      -font => $font{'bold'},
1743	     );
1744
1745{
1746    my @weather_src;
1747    if (!$city_obj->is_osm_source) {
1748	@weather_src = (['uptodate' => M"aktuellste"],
1749			['dahlem2'],
1750			['dahlem1'],
1751			($devel_host && $advanced
1752			 ? (['wetterkarte' => 'Wetterkarte Berlin-Dahlem'],
1753			    ['metar-EDDT' => 'METAR Tegel'],
1754			    ['metar-EDDB' => 'METAR Sch�nefeld'],
1755			   )
1756			 : ()
1757			),
1758		       );
1759    } else {
1760	my $icao_file;
1761	if (-r "$datadir/icao_metar") {
1762	    $icao_file = "$datadir/icao_metar";
1763	} elsif (-r "$datadir/icao") {
1764	    $icao_file = "$datadir/icao";
1765	}
1766	if ($icao_file) {
1767	    eval {
1768		my $icao_s = Strassen->new_stream($icao_file);
1769		$icao_s->read_stream(sub {
1770					 my($r, undef, $line) = @_;
1771					 if (my($icao, $fullname) = $r->[Strassen::NAME] =~ m{^(\S+)\s+\((.*)\)}) {
1772					     push @weather_src, ["metar-$icao" => "METAR $fullname"];
1773					 } else {
1774					     warn "Cannot parse '$r->[Strassen::NAME]' at line $line in $datadir/icao\n";
1775					 }
1776				     });
1777	    };
1778	    warn $@ if $@;
1779	}
1780    }
1781
1782    foreach (@weather_src) {
1783	my $name = $_->[1];
1784	if (!defined $name) {
1785	    $name = $wetter_name{$_->[0]}
1786	}
1787	$wbm->radiobutton
1788	    (-label    => $name,
1789	     -variable => \$wetter_station,
1790	     -value    => $_->[0],
1791	     -command  => sub { update_weather($wetter_force_update) },
1792	    );
1793    }
1794    if (@weather_src) {
1795	$wbm->separator;
1796    }
1797}
1798
1799$wbm->command(-label => M('Quelle').':',
1800	      -state => 'disabled',
1801	      -font => $font{'bold'},
1802	     );
1803foreach ([M"WWW",           'www'],
1804	 [M"lokaler Cache", 'local'],
1805	 [M"Datenbank",     'db'],
1806	) {
1807    next if $_->[1] eq 'db'    && !wetter_dir_exists();
1808    next if $_->[1] eq 'local' && !$devel_host;
1809    $wbm->checkbutton
1810      (-label    => $_->[0],
1811       -variable => \$wetter_source{$_->[1]},
1812       -command  => sub { update_weather($wetter_force_update) },
1813      );
1814}
1815if (wetter_dir_exists()) {
1816    $wbm->separator;
1817    $wbm->command(-label => M('Auswahl aus Datenbank').':',
1818		  -state => 'disabled',
1819		  -font => $font{'bold'},
1820		 );
1821    $wbm->command(-label => M"Dahlem (kurz)",
1822		  -command => sub { show_weather_db('dahlem2') });
1823    $wbm->command(-label => M"Dahlem (lang)",
1824		  -command => sub { show_weather_db('dahlem1') });
1825#      $wbm->command(-label => M"Tempelhof",
1826#  		  -command => sub { show_weather_db('tempelhof') });
1827}
1828$wbm->separator;
1829$wbm->command(-label => M"Wind ignorieren",
1830	      -command => sub { ignore_weather() },
1831	     );
1832{
1833    my $index = $wbm->index('last');
1834    push @edit_mode_cmd, sub { $wbm->invoke($index) };
1835}
1836
1837$wbm->command(-label => M"Aktualisierung",
1838	      -command => sub { update_weather(1) },
1839	     );
1840$wbm->checkbutton(-label => M"automatische Aktualisierung",
1841		  -variable => \$wetter_force_update,
1842		  -command => sub { update_weather($wetter_force_update) },
1843		 );
1844$wbm->checkbutton(-label => M"automatische Routenaktualisierung",
1845		  -variable => \$wetter_route_update,
1846		 );
1847
1848menuright($wb, $wbm);
1849menuright($wfewb, $wbm);
1850menuarrow($wfemb, $wbm, undef, '-pack' => [-side => 'bottom']);
1851
1852if ($wind_frame->can('UnderlineAll')) { $wind_frame->UnderlineAll }
1853
1854$temp_frame = $top_frame->Frame
1855  (-relief => 'raised', -bd => 1);
1856$ch->attach($temp_frame, -pod => "^\\s*Temp\$");
1857$temp_frame->Button
1858    (-text => 'Temp',
1859     -width => 7,
1860     -class => 'FlatBut',
1861     -command => sub {
1862	 require WWWBrowser;
1863	 require BBBikeWeather;
1864	 BBBikeWeather::require_wettermeldung();
1865	 WWWBrowser::start_browser("http://$wettermeldung2::www_site{dahlem1}$wettermeldung2::loc{dahlem1}");
1866     }
1867    )->pack;
1868$temp_frame->Label(-textvariable => \$act_value{Temp},
1869		  )->pack;
1870
1871arrange_topframe();
1872
1873##### Iconframe #######################################################
1874
1875$check_sub{'s'} = sub {
1876    plot("str",'s');
1877};
1878$check_sub{'l'} = sub {
1879    plot("str",'l');
1880};
1881$check_sub{'u'} = sub {
1882    $p_draw{'u'} = $p_draw{'sperre_u'} = $str_draw{'u'};
1883    $progress->InitGroup;
1884    plot("str",'u');
1885    plot("p",'u');
1886    plot_sperre($p_file{"sperre_u"}, -abk => "sperre_u");
1887    $progress->FinishGroup;
1888};
1889$check_sub{'b'} = sub {
1890    $p_draw{'b'} = $p_draw{'sperre_b'} = $str_draw{'b'};
1891    $progress->InitGroup;
1892    plot('str','b');
1893    plot('p','b');
1894    plot_sperre($p_file{"sperre_b"}, -abk => "sperre_b");
1895    $progress->FinishGroup;
1896};
1897$check_sub{'r'} = sub {
1898    $p_draw{'r'} = $str_draw{'r'};
1899    $progress->InitGroup;
1900    plot('str','r');
1901    plot('p','r');
1902    $progress->FinishGroup;
1903};
1904$check_sub{'w'} = sub {
1905    plot('str','w');
1906};
1907$check_sub{'f'} = sub {
1908    plot('str','f');
1909};
1910$check_sub{'o'} = sub { plot('p','o',Shortname => 1) };
1911$check_sub{'p'} = sub { plot('p','p') };
1912
1913## DEBUG_BEGIN
1914#BEGIN{mymstat("before do_iconframe BEGIN");} mymstat("before do_iconframe");
1915## DEBUG_END
1916$DockFrame = 'Frame';
1917
1918# use FlatCheckbox or not?
1919# flat relief relies on Tie::Watch installed
1920if ($flat_relief and !eval 'require Tie::Watch; 1') {
1921    $flat_relief = 0;
1922}
1923$Checkbutton = 'Checkbutton';
1924$Radiobutton = 'Radiobutton';
1925if ($flat_relief) {
1926    eval { require Tk::FlatCheckbox };
1927    if (!$@) {
1928	$Checkbutton = 'FlatCheckbox';
1929	if ($os ne 'win') {
1930	    $top->optionAdd('*FlatCheckbox*background' => 'grey80',
1931			    "startupFile");
1932	}
1933    }
1934    eval { require Tk::FlatRadiobutton };
1935    if (!$@) {
1936	$Radiobutton = 'FlatRadiobutton';
1937	if ($os ne 'win') {
1938	    $top->optionAdd('*FlatRadiobutton*background' => 'grey80',
1939			    "startupFile");
1940	}
1941    }
1942}
1943
1944$splash_screen->Update(0.2, 'create iconframe') if $splash_screen;
1945
1946do_iconframe() if $do_iconframe;
1947if ($standard_menubar) {
1948## DEBUG_BEGIN
1949#mymstat("before set menubar");
1950## DEBUG_END
1951    BBBike::Menubar::Set();
1952}
1953## DEBUG_BEGIN
1954#BEGIN{mymstat("after do_iconframe BEGIN");}
1955## DEBUG_END
1956
1957# Erzeugt das Frame mit den Icons und den dazugeh�rigen Men�s
1958sub do_iconframe {
1959    my $sym_frame = $ctrl_frame->Frame
1960      (Name => 'symframe')->pack(-side => 'top', -anchor => 'w');
1961
1962    my $def_selectcolor;
1963    {
1964	# get default selectcolor
1965	my $cb = $top->Checkbutton;
1966	$def_selectcolor = $cb->cget(-selectcolor);
1967	$cb->destroy;
1968    }
1969
1970    $top->optionAdd('*symframe*padX' => 0, 'startupFile');
1971    $top->optionAdd('*symframe*padY' => 0, 'startupFile');
1972    # XXX ja?
1973    $top->optionAdd('*symframe*indicatorOn' => $flat_relief, 'startupFile');
1974    $top->optionAdd('*symframe*selectColor' => 'white', 'startupFile')
1975      unless $flat_relief;
1976    $top->optionAdd('*symframe*Menu*selectColor' => $def_selectcolor,
1977		    'startupFile');
1978    if ($flat_relief) {
1979	$top->optionAdd('*symframe*relief' => 'flat');
1980	$top->optionAdd('*symframe*Menu*relief' => 'raised');
1981    }
1982
1983    if ($small_icons) {
1984	foreach (qw(Button Checkbutton Radiobutton Menubutton
1985		    FlatCheckbox FlatRadiobutton FireButton)) {
1986	    $top->optionAdd('*symframe*$_*padY' => 0, 'startupFile');
1987	}
1988    }
1989
1990    my($dock_port, $dock_port2);
1991    eval {
1992	die; # XXX not ready....
1993	require Tk::DockFrame;
1994	$DockFrame = 'DockFrame';
1995	$dock_port = $sym_frame->DockPort->grid(-row => 0,
1996						-column => 0,
1997						-sticky => 'nw');
1998	$dock_port2 = $sym_frame->DockPort->grid(-row => 0,
1999						 -column => 1,
2000						 -sticky => 'nw');
2001    };
2002
2003    use vars qw($curr_row);
2004    local $curr_row = 0;
2005    $misc_frame = $sym_frame->$DockFrame
2006      (-bd => 1, -relief => 'raised',
2007       ($DockFrame eq 'DockFrame' ? ('-dock' => $dock_port) : ()));
2008    if ($DockFrame ne 'DockFrame') {
2009	$misc_frame->grid(-row => 0,
2010			  -column => 0,
2011			  -sticky => 'nsew');
2012    }
2013    $misc_frame->gridColumnconfigure(999, -weight => 1); # force buttons to the left
2014    $col = 0;
2015##### Stra�en #####
2016my $strasse_check;
2017my $strcm;
2018my $radwege_check_index;
2019my $qualitaet_check_index;
2020my $handicap_check_index;
2021my $sperre_check_index;
2022my $ampeln_check_index;
2023my $fragezeichen_check_index;
2024my $nolighting_check_index;
2025my $gruene_wege_check_index;
2026my $vorfahrt_check_index;
2027my $c_bpcm;
2028my $comments_all_check_index;
2029my $cycle_routes_check_index;
2030unless($skip_features{"strassen"}) {
2031    $strasse_photo = load_photo($misc_frame, 'strasse');
2032    $strasse_check = $misc_frame->$Checkbutton
2033      (image_or_text($strasse_photo, 'Str'),
2034       -variable => \$str_draw{'s'},
2035       -command => $check_sub{'s'},
2036      )->grid(-row => $curr_row, -column => $col, -sticky => 's');
2037    $balloon->attach($strasse_check, -msg => M"Stra�en");
2038    $ch->attach($strasse_check, -pod => "^\\s*Stra�en-Symbol");
2039
2040    my $strcmb = $misc_frame->Menubutton;
2041    $strcmb->focus;
2042    $strcm = $strcmb->Menu(-title => M("Stra�en"));
2043    menu_entry_choose_ort
2044	($strcm, 's',
2045	 -accelerator => 'S',
2046	 -strchooseortargs =>
2047	 {'-markstartifactive' => 1,
2048	  (!$city_obj->is_osm_source
2049	   ? (-completelistbutton => sub { choose_from_plz(-interactive => 1) },
2050	      -completelistbuttonlabel => M"Alle Stra�en"
2051	     )
2052	   : ()
2053	  ),
2054	 },
2055	 -strextrachoosemenuaction =>
2056	 sub {
2057	     $strcm->cascade(-label => M('Erweiterte Auswahl').' ...');
2058	     my $ausm = $strcm->Menu(-title => M("Erweiterte Auswahl").' ...');
2059	     $strcm->entryconfigure('last', -menu => $ausm);
2060	     $ausm->command(-label => M"Volltextsuche",
2061			    -accelerator => "Ctrl-F",
2062			    -command => sub {
2063				require BBBikeAdvanced;
2064				search_anything();
2065			    });
2066	     $plzmcmd = $ausm->command
2067		 (-label => M"Komplette Stra�enliste",
2068		  -command => sub { choose_from_plz(-interactive => 1) });
2069	     if ($advanced) {
2070		 $ausm->command
2071		     (-label => M"Telefonbuch-Datenbank (Stra�e)",
2072		      -command => sub {
2073			  telefonbuch_dialog("str");
2074		      });
2075		 $ausm->command
2076		     (-label => M"Telefonbuch-Datenbank (Name)",
2077		      -command => sub {
2078			  telefonbuch_dialog("tel");
2079		      });
2080		 $ausm->command(-label => M"MySQL-DB",
2081				-command => sub {
2082				    push @INC, "$FindBin::RealBin/miscsrc";
2083				    eval {
2084					require TelbuchDBApprox;
2085					TelbuchDBApprox::tk_choose($top);
2086				    };
2087				    if ($@) {
2088					status_message($@, "die");
2089				    }
2090				});
2091	     }
2092	 },
2093	);
2094    $strcm->separator;
2095    if ($os ne 'win' || $advanced) {
2096	# No rotation on win possible.
2097	$strcm->checkbutton(-label => M"Stra�ennamen",
2098			    -variable => \$str_name_draw{'s'},
2099			    -command => sub {
2100				pending(1, 'replot-str-s');
2101			    },
2102			   );
2103    }
2104    $strcm->cascade(-label => M"Stra�enkategorien");
2105    {
2106	my $skm = $strcm->Menu(-title => M"Stra�enkategorien");
2107	$strcm->entryconfigure('last', -menu => $skm);
2108	my @l = ([M"wichtige Hauptstra�en", 'HH'],
2109		 [M"Hauptstra�en", 'H'],
2110		 ($devel_host || $city_obj->is_osm_source ? [M"wichtige Nebenstra�e", 'NH'] : ()), # XXX good name for this? Some osm records have the comment "Erg�nzungsstra�e mit besonderer Bedeutung"
2111		 [M"Nebenstra�en", 'N'],
2112		 [M"f�r Kfz gesperrte Stra�en", 'NN']);
2113	foreach (@l) {
2114	    my($label,$cat) = @$_;
2115	    $skm->checkbutton
2116	      (-label => $label,
2117	       -variable => \$str_restrict{'s'}->{$cat},
2118	       -command => sub {
2119		   pending(1, 'replot-str-s');
2120	       },
2121	      );
2122	}
2123	if ($advanced) {
2124	    $skm->separator;
2125	    $skm->checkbutton
2126		(-label => M"Autobahnen/Kfz-Stra�en",
2127		 -variable => \$str_draw{'sBAB'},
2128		 -command => sub {
2129		     plot("str", "sBAB",
2130			  -filename => get_strassen_file("strassen_bab"));
2131		 },
2132		);
2133	}
2134
2135    }
2136    $strcm->checkbutton(-label => M"Radwege",
2137			-variable => \$str_draw{'rw'},
2138			-command => sub { plot('str','rw')},
2139			-accelerator => 'Shift-R',
2140		       );
2141    $radwege_check_index = $strcm->index('last');
2142    $strcm->cascade(-label => M"Radwegekategorien");
2143    {
2144	my $rkm = $strcm->Menu(-title => M"Radwegekategorien");
2145	$strcm->entryconfigure('last', -menu => $rkm);
2146	foreach my $t (@Radwege::category_order) {
2147	    my $cat_code = $Radwege::category_code{$t} || '';
2148	    next if $cat_code eq 'RW0';
2149	    $rkm->checkbutton
2150	      (-label => $Radwege::category_name{$t},
2151	       -variable => \$str_restrict{'rw'}->{$cat_code},
2152	       -command => sub {
2153		   pending(1, 'replot-str-rw');
2154	       },
2155	      );
2156	}
2157    }
2158
2159    my $create_comment_layers_cb = sub {
2160	my($menu, $type, %cb_args) = @_;
2161	my $label = $comment_cat_labels{$type} || $type;
2162	my $def = 'comm-' . $type;
2163	$menu->checkbutton
2164	    (-label => $label,
2165	     -variable => \$str_draw{$def},
2166	     -command => sub {
2167		 my $file  = get_strassen_file("comments_" . $type);
2168		 plot('str', $def, Filename => $file);
2169	     },
2170	     %cb_args,
2171	    );
2172    };
2173
2174    unless ($skip_features{"radroute"}) {
2175	$create_comment_layers_cb->($strcm, "route", -accelerator => 'Shift-Y');
2176	$cycle_routes_check_index = $strcm->index('last');
2177	$strcm->command(-label => M"Radroute ausw�hlen",
2178			-command => sub {
2179			    choose_ort(qw(s comm-route),
2180				       -markstartifactive => 1);
2181			});
2182    }
2183
2184    $strcm->checkbutton(-label => M"Einbahn-/gesperrte Stra�en",
2185			-variable => \$p_draw{'sperre'},
2186			-command => sub { plot_sperre() },
2187			-accelerator => 'G',
2188		       );
2189    $sperre_check_index = $strcm->index('last');
2190    $strcm->checkbutton(-label => M"Ampeln",
2191			-variable => \$p_draw{'lsa'},
2192			-command => sub { plot('p','lsa') },
2193			-accelerator => 'A',
2194		       );
2195    $ampeln_check_index = $strcm->index('last');
2196    $strcm->checkbutton(-label => M"Stra�enqualit�t",
2197			-variable => \$str_draw{'qs'},
2198			-command => sub { plot('str','qs') },
2199			-accelerator => 'Shift-Q',
2200		       );
2201    $qualitaet_check_index = $strcm->index('last');
2202    $strcm->cascade(-label => M"Qualit�tskategorien");
2203    {
2204	my $qm = $strcm->Menu(-title => M"Qualit�tskategorien");
2205	$strcm->entryconfigure('last', -menu => $qm);
2206	foreach (0 .. 3) {
2207	    my $cat = "Q$_";
2208	    my $label = $category_attrib{$cat}->[ATTRIB_SINGULAR];
2209	    $qm->checkbutton
2210	      (-label => $label,
2211	       -variable => \$str_restrict{'qs'}->{$cat},
2212	       -command => sub {
2213		   $str_restrict{'ql'}->{$cat} =
2214		       $str_restrict{'qs'}->{$cat};
2215		   pending(1, 'replot-str-qs');
2216		   pending(1, 'replot-str-ql');
2217	       },
2218	      );
2219	}
2220    }
2221    $strcm->checkbutton(-label => M"Sonstige Beeintr�chtigungen",
2222			-variable => \$str_draw{'hs'},
2223			-command => sub { plot('str','hs') },
2224			-accelerator => 'Shift-H',
2225		       );
2226    $handicap_check_index = $strcm->index('last');
2227    unless ($skip_features{"nolighting"}) {
2228	$strcm->checkbutton(-label => M"Unbeleuchtete Stra�en",
2229			    -variable => \$str_draw{'nl'},
2230			    -command => sub { plot('str','nl') },
2231			    -accelerator => 'Shift-N',
2232			   );
2233	$nolighting_check_index = $strcm->index('last');
2234    }
2235    unless ($skip_features{"green"}) {
2236	$strcm->checkbutton(-label => M"Gr�ne Wege",
2237			    -variable => \$str_draw{'gr'},
2238			    -command => sub { plot('str','gr') },
2239			    -accelerator => 'Shift-G',
2240			   );
2241	$gruene_wege_check_index = $strcm->index('last');
2242    }
2243    unless ($skip_features{"vorfahrt"}) {
2244	$strcm->checkbutton(-label => M"Vorfahrt",
2245			    -variable => \$p_draw{'vf'},
2246			    -command => sub { plot('p','vf') },
2247			    -accelerator => 'Shift-V',
2248			   );
2249	$vorfahrt_check_index = $strcm->index('last');
2250    }
2251
2252    $strcm->cascade(-label => M"Kommentare");
2253    {
2254	$c_bpcm = $strcm->Menu(-title => M"Sonstige");
2255	$strcm->entryconfigure("last", -menu => $c_bpcm);
2256	my @used_types;
2257	foreach my $type (@comments_types) {
2258	    next if $type =~ /^(cyclepath|mount|route|ferry)$/; # handled elsewhere
2259	    if (!$advanced) {
2260		# kfzverkehr: poor presentation
2261		# scenic: almost no data
2262		next if $type =~ /^(kfzverkehr|scenic)$/;
2263	    }
2264	    $create_comment_layers_cb->($c_bpcm, $type);
2265	    push @used_types, $type;
2266	}
2267	$c_bpcm->separator;
2268	my $str_draw_all = 0;
2269	$c_bpcm->checkbutton
2270	    (-label => M("Alle"),
2271	     -variable => \$str_draw_all,
2272	     -command => sub {
2273		 my $onoff = $str_draw_all;
2274		 $progress->InitGroup;
2275		 for my $type (@used_types) {
2276		     my $def = 'comm-' . $type;
2277		     $str_draw{$def} = $onoff;
2278		     plot('str', $def, Filename => get_strassen_file("comments_" . $type));
2279		 }
2280		 $progress->FinishGroup;
2281	     },
2282	     -accelerator => 'Shift-C',
2283	    );
2284	$comments_all_check_index = $c_bpcm->index('last');
2285    }
2286
2287    unless ($skip_features{"hoehe"}) {
2288	$strcm->checkbutton(-label => M"H�henangaben",
2289			    -variable => \$p_draw{'hoehe'},
2290			    -command => sub { plot('p','hoehe') });
2291    }
2292
2293    # XXX the mount file is very problematic at the moment; do not show it to the normal user until everything's fixed! See StrassenNetz.pm and steigung_stat. Also, I think comments_mount is not used for Steigungsoptimierung, so don't puzzle the user about this.
2294    if ($devel_host) {
2295	$strcm->checkbutton
2296	    (-label => M"Steigungen",
2297	     -variable => \$str_draw{'mount'},
2298	     -command => \&plot_mount,
2299	    );
2300    }
2301    if (1) {
2302	$strcm->checkbutton(-label => M"Fragezeichen",
2303			    -variable => \$str_draw{'fz'},
2304			    -command => sub { plot('str','fz') },
2305			    -accelerator => '?',
2306			   );
2307	$fragezeichen_check_index = $strcm->index('last');
2308    }
2309    $strcm->checkbutton(-label => M"Outline zeichnen",
2310			-variable => \$str_outline{'s'},
2311			-command => sub {
2312			    pending(1, 'replot-str-s');
2313			},
2314		       );
2315    menu_entry_up_down($strcm, $tag_group{'str_s'});
2316    menuright($strasse_check, $strcm);
2317    menuarrow($strcmb, $strcm, $col++, -special => 'LAYER');
2318}
2319##### Landstra�en #####
2320my $landstrasse_check;
2321my $lstrcm;
2322my $radwege_l_check_index;
2323my $qualitaet_l_check_index;
2324my $handicap_l_check_index;
2325my $land_jwd_check_index;
2326unless ($skip_features{"landstrassen"}) {
2327    $landstrasse_photo =
2328      load_photo($misc_frame, 'landstrasse');
2329    $landstrasse_check = $misc_frame->$Checkbutton
2330      (image_or_text($landstrasse_photo, 'LStr'),
2331       -variable => \$str_draw{'l'},
2332       -command => $check_sub{'l'},
2333      )->grid(-row => $curr_row, -column => $col, -sticky => 's');
2334    $balloon->attach($landstrasse_check, -msg => M"Landstra�en");
2335    $ch->attach($landstrasse_check, -pod => "^\\s*Landstra�en-Symbol");
2336
2337    my $lstrcmb = $misc_frame->Menubutton;
2338    $lstrcm = $lstrcmb->Menu(-title => M"Landstra�en");
2339    menu_entry_choose_ort($lstrcm, 'l',
2340			  -accelerator => 'L',
2341			  -strchooseortargs => {'-markstartifactive' => 1});
2342    $lstrcm->separator;
2343    $lstrcm->checkbutton(-label => M"Outline zeichnen",
2344			 -variable => \$str_outline{'l'},
2345			 -command => sub {
2346			     pending(1, 'replot-str-l');
2347			 },
2348			);
2349    unless ($skip_features{wideregion}) {
2350	$lstrcm->checkbutton(-label => M"Landstra�en jwd zeichnen",
2351			     -variable => \$str_far_away{'l'},
2352			     -command => sub {
2353				 pending(1, 'replot-str-l');
2354			     },
2355			     -accelerator => 'Shift-L',
2356			    );
2357	$land_jwd_check_index = $lstrcm->index('last');
2358    }
2359    $lstrcm->checkbutton(-label => M"Stra�ennamen",
2360			 -variable => \$str_name_draw{'l'},
2361			 -command => sub {
2362			     pending(1, 'replot-str-l');
2363			 },
2364		       );
2365    $lstrcm->checkbutton(-label => M"Stra�ennummern",
2366			 -variable => \$str_nr_draw{'l'},
2367			 -command => sub {
2368			     pending(1, 'replot-str-l');
2369			 },
2370		       );
2371    $lstrcm->checkbutton(-label => M"Stra�enqualit�t",
2372			 -variable => \$str_draw{'ql'},
2373			 -command => sub { plot('str','ql') },
2374			 -accelerator => 'Shift-Q',
2375			);
2376    $qualitaet_l_check_index = $lstrcm->index('last');
2377    $lstrcm->checkbutton(-label => M"Sonstige Beeintr�chtigungen",
2378			 -variable => \$str_draw{'hl'},
2379			 -command => sub { plot('str','hl') },
2380			);
2381    $handicap_l_check_index = $lstrcm->index('last');
2382    $lstrcm->checkbutton(-label => M"Radwege im Umland",
2383			-variable => \$str_draw{'comm-cyclepath'},
2384			 -command => sub {
2385			     my $file = get_strassen_file("comments_cyclepath");
2386			     plot('str', 'comm-cyclepath', Filename => $file);
2387			 },
2388			 -accelerator => 'Shift-R',
2389			);
2390    $radwege_l_check_index = $lstrcm->index('last');
2391    menu_entry_up_down($lstrcm, $tag_group{'str_l'});
2392    menuright($landstrasse_check, $lstrcm);
2393    menuarrow($lstrcmb, $lstrcm, $col++, -special => 'LAYER');
2394}
2395
2396##### Orte #####
2397my $ort_check;
2398my $ocm;
2399my $ort_jwd_check_index;
2400unless ($skip_features{"orte"}) {
2401    $ort_photo = load_photo($misc_frame, 'ort');
2402    $ort_check = $misc_frame->$Checkbutton
2403      (image_or_text($ort_photo, 'Ort'),
2404       -variable => \$p_draw{'o'},
2405       -command => $check_sub{'o'},
2406      )->grid(-row => $curr_row, -column => $col, -sticky => 's');
2407    $balloon->attach($ort_check, -msg => M"Orte im Umland");
2408    $ch->attach($ort_check, -pod => "^\\s*Ort-Symbol");
2409
2410    my $ocmb = $misc_frame->Menubutton;
2411    $ocm = $ocmb->Menu(-title => M"Orte");
2412    menu_entry_choose_ort($ocm, 'o', -accelerator_p => 'O',
2413			  -pchooseortargs => {'-markstartifactive' => 1});
2414    $ocm->separator;
2415    $ocm->checkbutton(-label => M"Ortsnamen",
2416		      -variable => \$p_name_draw{'o'},
2417		      -command => sub {
2418			  pending(1, 'replot-p-o');
2419		      },
2420		     );
2421    $ocm->cascade(-label => M"Kategorie");
2422    {
2423	my $m = $ocm->Menu(-title => M"Ortkategorie");
2424	$ocm->entryconfigure('last', -menu => $m);
2425	for my $cat ('auto', 0 .. 5) {
2426	    $m->radiobutton(-label => ($cat eq 'auto' ? M"Auto" :
2427				       $cat == 0 ? M"Alle" : $cat),
2428			    -variable => \$place_category,
2429			    -value => $cat,
2430			    -command => sub {
2431				pending(1, 'replot-p-o');
2432			    },
2433			   );
2434	}
2435    }
2436    unless ($skip_features{wideregion}) {
2437	$ocm->checkbutton(-label => M"Orte jwd zeichnen",
2438			  -variable => \$p_far_away{'o'},
2439			  -command => sub {
2440			      pending(1, 'replot-p-o');
2441			  },
2442			  -accelerator => 'Shift-O',
2443			 );
2444	$ort_jwd_check_index = $ocm->index('last');
2445    }
2446    $ocm->separator;
2447    $ocm->cascade(-label => M"Schriftgr��e");
2448    {
2449	my $m = $ocm->Menu(-title => M"Ort-Schriftgr��e");
2450	$ocm->entryconfigure('last', -menu => $m);
2451	foreach my $fontsize ([M"klein",       0],
2452			      [M"normal",      1],
2453			      [M"gro�",        2],
2454			      [M"sehr gro�",   3],
2455			     ) {
2456	    $m->radiobutton(-label    => $fontsize->[0],
2457			    -variable => \$orte_label_size,
2458			    -value    => $fontsize->[1],
2459			    -command => sub {
2460				pending(1, 'replot-p-o');
2461			    },
2462			   );
2463	}
2464    }
2465    $ocm->checkbutton(-label => M"�berlappungen vermeiden",
2466		      -variable => \$no_overlap_label{'o'},
2467		      -command => sub {
2468			  pending(1, 'replot-p-o');
2469		      },
2470		     );
2471    if ($advanced) { # XXX funktioniert noch nicht mit no_verlap zusammen
2472	$ocm->checkbutton(-label => M"Umrandung um Labels",
2473			  -variable => \$do_outline_text{'o'},
2474			  -command => sub {
2475			      pending(1, 'replot-p-o');
2476			  },
2477			 );
2478    }
2479    menu_entry_up_down($ocm, $tag_group{'p_o'});
2480    menuright($ort_check, $ocm);
2481    menuarrow($ocmb, $ocm, $col++, -special => 'LAYER');
2482}
2483
2484##### U-Bahn #####
2485my $ubahn_check;
2486unless ($skip_features{"u-bahn"}) {
2487    $ubahn_photo = load_photo($misc_frame, 'ubahn');
2488    $ubahn_check = $misc_frame->$Checkbutton
2489      (image_or_text($ubahn_photo, 'U'),
2490       -variable => \$str_draw{'u'},
2491       -command => $check_sub{'u'},
2492      )->grid(-row => $curr_row, -column => $col, -sticky => 's');
2493    $balloon->attach($ubahn_check, -msg => M"U-Bahn");
2494    $ch->attach($ubahn_check, -pod => "^\\s*U-Bahn-Symbol");
2495
2496    my $ubcmb = $misc_frame->Menubutton;
2497    my $ubcm = $ubcmb->Menu(-title => M"U-Bahn");
2498    menu_entry_choose_ort($ubcm, 'u', -accelerator => 'U',
2499			  -pchooseortargs => {'-markstartifactive' => 1},
2500			  -strblockings => 1,
2501			 );
2502    $ubcm->checkbutton(-label => M"U-Bhf-Namen",
2503		       -variable => \$p_name_draw{'u'},
2504		       -command => sub {
2505			   pending(1, 'replot-p-u');
2506		       },
2507		      );
2508    $ubcm->checkbutton(-label => M"�berlappungen vermeiden",
2509		       -variable => \$no_overlap_label{'u'},
2510		       -command => sub {
2511			   pending(1, 'replot-p-u');
2512		       },
2513		      );
2514    $ubcm->checkbutton(-label => M"Fahrradfreundliche Zug�nge",
2515		       -variable => \$p_draw{'u_bg'},
2516		       -command => sub {
2517			   plot('p', 'u_bg');
2518		       },
2519		      );
2520    $ubcm->separator;
2521    foreach ($skip_features{"vbb"} ? ["Strecken in Betrieb", "UA"] :
2522	     ([M"VBB-Zone Berlin A", 'UA'],
2523	      [M"VBB-Zone Berlin B", 'UB'],
2524	     ),
2525	     [M"nur Betriebsfahrten", "UBetrieb"],
2526	     [M"in Bau", 'UBau'],
2527	     [M"stillgelegt", 'U0'],
2528	    ) {
2529	my($label,$cat) = @$_;
2530	$ubcm->checkbutton(-label => $label,
2531			   -variable => \$str_restrict{'u'}->{$cat},
2532			   -command => sub {
2533			       $progress->InitGroup;
2534			       pending(1, 'replot-str-u');
2535			       pending(1, 'replot-p-u');
2536			       $progress->FinishGroup;
2537			   },
2538			  );
2539    }
2540    menu_entry_up_down($ubcm, $tag_group{'str_u'});
2541    menuright($ubahn_check, $ubcm);
2542    menuarrow($ubcmb, $ubcm, $col++,
2543	      -menulabel => M"U-Bahn", -special => 'LAYER');
2544}
2545##### S-Bahn #####
2546my $sbahn_check;
2547unless ($skip_features{"s-bahn"}) {
2548    $sbahn_photo = load_photo($misc_frame, 'sbahn');
2549    $sbahn_check = $misc_frame->$Checkbutton
2550      (image_or_text($sbahn_photo, 'S'),
2551       -variable => \$str_draw{'b'},
2552       -command => $check_sub{'b'},
2553      )->grid(-row => $curr_row, -column => $col, -sticky => 's');
2554    $balloon->attach($sbahn_check, -msg => M"S-Bahn");
2555    $ch->attach($sbahn_check, -pod => "^\\s*S-Bahn-Symbol");
2556    my $sbcmb = $misc_frame->Menubutton;
2557    my $sbcm = $sbcmb->Menu(-title => M"S-Bahn");
2558    menu_entry_choose_ort($sbcm, 'b', -accelerator => 'B',
2559			  -pchooseortargs => {'-markstartifactive' => 1},
2560			  -strblockings => 1,
2561			 );
2562    $sbcm->checkbutton(-label => M"S-Bhf-Namen",
2563		       -variable => \$p_name_draw{'b'},
2564		       -command => sub {
2565			   pending(1, 'replot-p-b');
2566		       },
2567		      );
2568    $sbcm->checkbutton(-label => M"�berlappungen vermeiden",
2569		       -variable => \$no_overlap_label{'b'},
2570		       -command => sub {
2571			   pending(1, 'replot-p-b');
2572		       },
2573		      );
2574    $sbcm->checkbutton(-label => M"Fahrradfreundliche Zug�nge",
2575		       -variable => \$p_draw{'b_bg'},
2576		       -command => sub {
2577			   plot('p', 'b_bg');
2578		       },
2579		      );
2580    $sbcm->separator;
2581    foreach ($skip_features{"vbb"} ? ["Strecken in Betrieb", "SA"] :
2582	     ([M"VBB-Zone Berlin A", 'SA'],
2583	      [M"VBB-Zone Berlin B", 'SB'],
2584	      [M"VBB-Zone Berlin C", 'SC'],
2585	     ),
2586	     [M"nur Betriebsfahrten", "SBetrieb"],
2587	     [M"in Bau", 'SBau'],
2588	     [M"stillgelegt", 'S0'],
2589	    ) {
2590	my($label,$cat) = @$_;
2591	$sbcm->checkbutton(-label => $label,
2592			   -variable => \$str_restrict{'b'}->{$cat},
2593			   -command => sub {
2594			       $progress->InitGroup;
2595			       pending(1, 'replot-str-b');
2596			       pending(1, 'replot-p-b');
2597			       $progress->FinishGroup;
2598			   },
2599			  );
2600    }
2601    menu_entry_up_down($sbcm, $tag_group{'str_b'});
2602    menuright($sbahn_check, $sbcm);
2603    menuarrow($sbcmb, $sbcm, $col++,
2604	      -menulabel => M"S-Bahn", -special => 'LAYER');
2605}
2606##### RB #####
2607my $rbahn_check;
2608unless ($skip_features{"r-bahn"}) {
2609    if ($XXX_use_old_R_symbol) {
2610	$rbahn_photo = load_photo($misc_frame, 'rbahn');
2611    } else {
2612	$rbahn_photo = load_photo($misc_frame, 'eisenbahn15');
2613    }
2614    $rbahn_check = $misc_frame->$Checkbutton
2615      (image_or_text($rbahn_photo, 'RB'),
2616       -variable => \$str_draw{'r'},
2617       -command => $check_sub{'r'},
2618      )->grid(-row => $curr_row, -column => $col, -sticky => 's');
2619    $balloon->attach($rbahn_check, -msg => M"Regionalbahn");
2620    $ch->attach($rbahn_check, -pod => "^\\s*RB-Symbol");
2621    my $rbcmb = $misc_frame->Menubutton;
2622    my $rbcm = $rbcmb->Menu(-title => M"Regionalbahn");
2623    menu_entry_choose_ort($rbcm, 'r', -accelerator => 'R',
2624			  -pchooseortargs => {'-markstartifactive' => 1},
2625			  -strblockings => 1,
2626			 );
2627    $rbcm->checkbutton(-label => M"R-Bhf-Namen",
2628		       -variable => \$p_name_draw{'r'},
2629		       -command => sub {
2630			   pending(1, 'replot-p-r');
2631		       },
2632		      );
2633    $rbcm->checkbutton(-label => M"�berlappungen vermeiden",
2634		       -variable => \$no_overlap_label{'r'},
2635		       -command => sub {
2636			   pending(1, 'replot-p-r');
2637		       },
2638		      );
2639    $rbcm->separator;
2640    foreach ($skip_features{"vbb"} ? ["Strecken in Betrieb", "R"] :
2641	     ([M"VBB-Zonen Berlin A und B", 'RB'],
2642	      [M"VBB-Zone Berlin C", 'RC'],
2643	      [M"au�erhalb Berlin ABC", 'R'],
2644	     ),
2645	     [M"stillgelegt", 'R0'],
2646	     [M"in Bau", 'RBau'],
2647	     [M"G�terbahnen/Verbindungsstrecken", 'RG'],
2648	     [M"Parkbahnen/Kleinbahnen", 'RP'],
2649	    ) {
2650	my($label,$cat) = @$_;
2651	$rbcm->checkbutton(-label => $label,
2652			   -variable => \$str_restrict{'r'}->{$cat},
2653			   -command => sub {
2654			       $progress->InitGroup;
2655			       pending(1, 'replot-str-r');
2656			       pending(1, 'replot-p-r');
2657			       $progress->FinishGroup;
2658			   },
2659			  );
2660    }
2661    menu_entry_up_down($rbcm, $tag_group{'str_r'});
2662    menuright($rbahn_check, $rbcm);
2663    menuarrow($rbcmb, $rbcm, $col++,
2664	      -menulabel => M"R-Bahn", -special => 'LAYER');
2665}
2666##### Ferries #####
2667unless ($skip_features{'faehren'}) {
2668    $ferry_photo = load_photo($misc_frame, 'ferry');
2669    my $ferry_check = $misc_frame->$Checkbutton
2670      (image_or_text($ferry_photo, 'F'),
2671       -variable => \$str_draw{'e'},
2672       -command => sub { plot('str','e') },
2673      )->grid(-row => $curr_row, -column => $col, -sticky => 's');
2674    $balloon->attach($ferry_check, -msg => M"F�hren");
2675    my $ferrycmb = $misc_frame->Menubutton;
2676    my $ferrycm = $ferrycmb->Menu(-title => M"F�hren");
2677    menu_entry_choose_ort($ferrycm, 'e',
2678			  -pchooseortargs => {'-markstartifactive' => 1},
2679			 );
2680    menuright($ferry_check, $ferrycm);
2681    menuarrow($ferrycmb, $ferrycm, $col++,
2682	      -menulabel => M"F�hren", -special => 'LAYER');
2683}
2684##### Gew�sser #####
2685my $wasser_check;
2686my $wasserumland_check_index;
2687my $wcm;
2688unless ($skip_features{"wasser"}) {
2689    $wasser_photo = load_photo($misc_frame, 'wasser');
2690    $wasser_check = $misc_frame->$Checkbutton
2691      (image_or_text($wasser_photo, 'H20'),
2692       -variable => \$str_draw{'w'},
2693       -command => $check_sub{'w'},
2694      )->grid(-row => $curr_row, -column => $col, -sticky => 's');
2695    $balloon->attach($wasser_check, -msg => M"Gew�sser");
2696    $ch->attach($wasser_check, -pod => "^\\s*Gew�sser-Symbol");
2697    my $wcmb = $misc_frame->Menubutton;
2698    $wcm = $wcmb->Menu(-title => M"Gew�sser");
2699    menu_entry_choose_ort($wcm, 'w', -accelerator => 'W');
2700    $wcm->separator;
2701    $wcm->checkbutton(-label => M"Outline zeichnen",
2702		      -variable => \$str_outline{'w'},
2703		      -command => sub {
2704			  $str_outline{'i'} = $str_outline{'w'};
2705			  pending(1, 'replot-str-w');
2706		      },
2707		     );
2708    $wcm->checkbutton(-label => M"Namen der Gew�sser",
2709		      -variable => \$str_name_draw{'w'},
2710		      -command => sub {
2711			  $str_name_draw{'i'} = $str_name_draw{'w'};
2712			  pending(1, 'replot-str-w');
2713		      },
2714		     );
2715    unless ($skip_features{"wasserumland"}) {
2716	$wcm->checkbutton(-label => M"Gew�sser in der Stadt zeichnen",
2717			  -variable => \$wasserstadt,
2718			  -command => sub {
2719			      pending(1, 'replot-str-w');
2720			  },
2721			 );
2722	$wcm->checkbutton(-label => M"Gew�sser im Umland zeichnen",
2723			  -variable => \$wasserumland,
2724			  -command => sub {
2725			      pending(1, 'replot-str-w');
2726			  },
2727			  -accelerator => 'Shift-W',
2728			 );
2729	$wasserumland_check_index = $wcm->index('last');
2730	unless ($skip_features{"wideregion"}) {
2731	    $wcm->checkbutton(-label => M"Gew�sser jwd zeichnen",
2732			      -variable => \$str_far_away{'w'},
2733			      -command => sub {
2734				  pending(1, 'replot-str-w');
2735			      },
2736			     );
2737	}
2738    }
2739    menu_entry_up_down($wcm, $tag_group{'str_w'});
2740    menuright($wasser_check, $wcm);
2741    menuarrow($wcmb, $wcm, $col++, -special => 'LAYER');
2742}
2743##### Fl�chen #####
2744my $flaechen_check;
2745unless ($skip_features{"flaechen"}) {
2746    $flaechen_photo = load_photo($misc_frame, 'flaechen');
2747    $flaechen_check = $misc_frame->$Checkbutton
2748      (image_or_text($flaechen_photo, 'Fl'),
2749       -variable => \$str_draw{'f'},
2750       -command => $check_sub{'f'},
2751      )->grid(-row => $curr_row, -column => $col, -sticky => 's');
2752    $balloon->attach($flaechen_check, -msg => M"sonstige Fl�chen");
2753    $ch->attach($flaechen_check, -pod => "^\\s*Fl�chen-Symbol");
2754    my $fcmb = $misc_frame->Menubutton;
2755    my $fcm = $fcmb->Menu(-title => M"sonstige Fl�chen");
2756    menu_entry_choose_ort($fcm, 'f', -accelerator => 'F');
2757    $fcm->checkbutton(-label => M"Namen der Fl�chen",
2758		      -variable => \$str_name_draw{'f'},
2759		      -command => sub {
2760			  pending(1, 'replot-str-f');
2761		      },
2762		     );
2763    $fcm->separator;
2764
2765    if ($advanced) {
2766	menu_entry_choose_ort($fcm, 'z');
2767	$fcm->separator;
2768    }
2769    $fcm->checkbutton(-label => $str_attrib{g}->[ATTRIB_PLURAL],
2770		      -variable => \$str_draw{'g'},
2771		      -command => sub { plot('str','g') });
2772    if ($advanced && $devel_host) {
2773	$fcm->checkbutton(-label => $str_attrib{gBO}->[ATTRIB_PLURAL],
2774			  -variable => \$str_draw{'gBO'},
2775			  -command => sub { plot('str', 'gBO') });
2776	$str_name_draw{"gBO"} = 1; # force drawing of labels
2777	$fcm->checkbutton(-label => defined $city && $city eq 'Berlin' ? M"Berliner Ortsteilnamen" : M"Ortsteilnamen",
2778			  -variable => \$str_name_draw{'gBO'},
2779			  -command => sub {
2780			      pending(1, 'replot-str-gBO');
2781			  },
2782			 );
2783    }
2784    if (defined $city && $city eq 'Berlin') {
2785	$fcm->checkbutton(-label => M"Grenzen von Potsdam",
2786			  -variable => \$str_draw{'gP'},
2787			  -command => sub { plot('str','gP') });
2788    }
2789    $fcm->checkbutton(-label => M"Staatsgrenzen",
2790		      -variable => \$str_draw{'gD'},
2791		      -command => sub { plot('str','gD') });
2792    $fcm->checkbutton(-label => M"Grenz�berg�nge",
2793		      -variable => \$p_draw{'GU'},
2794		      -command => sub { plot('p', 'GU') },
2795		     );
2796
2797    menu_entry_up_down($fcm, $tag_group{'str_f'});
2798    menuright($flaechen_check, $fcm);
2799    menuarrow($fcmb, $fcm, $col++, -special => 'LAYER');
2800}
2801##### Sehensw�rdigkeiten, Kneipen etc. #####
2802my $sehenswuerdigkeiten_check;
2803unless ($skip_features{"sehenswuerdigkeiten"}) {
2804    $sehenswuerdigkeiten_check = $misc_frame->$Checkbutton
2805      (image_or_text($star_photo, '*'),
2806       -variable => \$str_draw{'v'},
2807       -command => sub { plot('str','v') },
2808      )->grid(-row => $curr_row, -column => $col, -sticky => 's');
2809    $balloon->attach($sehenswuerdigkeiten_check, -msg => M"Sehensw�rdigkeiten etc.");
2810    $ch->attach($sehenswuerdigkeiten_check, -pod => "^\\s*Sehensw�rdigkeiten-Symbol");
2811    my $knmb = $misc_frame->Menubutton;
2812    my $knm = $knmb->Menu(-title => M"Sehensw�rdigkeiten etc.",
2813		       -disabledforeground => $wb->cget(-foreground));
2814
2815    $knm->checkbutton(-label => M"Sehensw�rdigkeiten",
2816		      -variable => \$str_draw{'v'},
2817		      -command => sub { plot('str','v') });
2818    $knm->command(-label => M"Sehensw�rdigkeit ausw�hlen",
2819		  -command => sub { choose_ort(qw(s v),
2820					       -markstartifactive => 1) });
2821    $knm->checkbutton(-label => M"Namen der Sehensw�rdigkeiten",
2822		      -variable => \$str_name_draw{'v'},
2823		      -command => sub {
2824			  pending(1, 'replot-str-v');
2825		      },
2826		     );
2827    $knm->checkbutton(-label => M"�berlappungen vermeiden",
2828		      -variable => \$no_overlap_label{'v'},
2829		      -command => sub {
2830			  pending(1, 'replot-str-v');
2831		      },
2832		     );
2833    $knm->separator;
2834
2835    $knm->command(-label => M"Pers�nliche Orte",
2836		  -command => sub {
2837		      require BBBikePersonal;
2838		      BBBikePersonal::dialog();
2839		  });
2840
2841    unless ($skip_features{obst}) {
2842	$knm->checkbutton(-label => M"Obst",
2843			  -variable => \$p_draw{'obst'},
2844			  -command => sub { plot('p','obst') });
2845    }
2846
2847    if ($advanced || $city_obj->is_osm_source) {
2848	my @try_kneipen_list = qw(kn rest ki);
2849	my @kneipen_list;
2850	foreach my $f (@try_kneipen_list) {
2851	    if (-f "$datadir/$p_file{$f}") {
2852		push @kneipen_list, $f;
2853	    }
2854	}
2855	if (@kneipen_list) {
2856	    $knm->separator;
2857	    if (!$city_obj->is_osm_source) {
2858		$knm->command(-label => M("Nicht mehr gepflegt").":",
2859			      -state => 'disabled',
2860			      -font => $font{'bold'},
2861			     );
2862	    }
2863	    foreach my $f (@kneipen_list) {
2864		if (-f "$datadir/$p_file{$f}") {
2865		    $knm->checkbutton(-label => $p_attrib{$f}->[ATTRIB_PLURAL],
2866				      -variable => \$p_draw{$f},
2867				      -command => sub { plot('p',$f) });
2868		    $knm->command(-label => Mfmt("%s ausw�hlen", $p_attrib{$f}->[ATTRIB_SINGULAR]),
2869				  -command => sub { choose_ort('p', $f) });
2870		}
2871	    }
2872	}
2873    }
2874
2875    #XXXX menu_entry_up_down($knm, $tag_group{'str_f'});
2876    menuright($sehenswuerdigkeiten_check, $knm);
2877    menuarrow($knmb, $knm, $col++, -special => 'LAYER');
2878}
2879##### Zus�tzliche Kartenebenen #####
2880    my $newlayer_label = $misc_frame->Label
2881      (image_or_text($newlayer_photo, '*'),
2882      )->grid(-row => $curr_row, -column => $col, -sticky => 's');
2883    $balloon->attach($newlayer_label, -msg => M"Zus�tzliche Kartenebenen");
2884    $ch->attach($newlayer_label, -pod => "^\\s*Zus�tzliche Kartenebenen");
2885    my $nlmb = $misc_frame->Menubutton;
2886    my $nlm = $nlmb->Menu(-title => M"Zus�tzliche Kartenebenen");
2887    {
2888	# XXX this used to be LazyMenu to postpone loading of layers
2889	# XXX maybe re-enable this one day if I find a possibility to
2890	# update the cascade menu without showing the menu first.
2891	my $cusm = $nlm;
2892#XXX del:
2893# 	$BBBike::Menubar::additional_layer_menu = $cusm;
2894# 	$BBBike::Menubar::additional_layer_menu = $BBBike::Menubar::additional_layer_menu; # peacify -w
2895 	$cusm->{BBBike_Menulabel} = M"Zus�tzliche Kartenebenen";
2896#	$opbm->entryconfigure('last', -menu => $cusm);
2897# 	$cusm->command(-label => M"Zus�tzliche Layer",
2898# 		       -state => 'disabled',
2899# 		       -font => $font{'bold'});
2900	$cusm->command(-label => M"Stra�en-Layer zeichnen",
2901		       -command => sub {
2902			   require BBBikeAdvanced;
2903			   tk_plot_additional_layer('str') });
2904	if ($advanced) {
2905	    $cusm->command(-label => M"Sperrungen-Layer zeichnen", # XXX label? in advanced mode because there is no way to delete the blockings from net!
2906			   -command => sub {
2907			       require BBBikeAdvanced;
2908			       plot_additional_sperre_layer() });
2909	}
2910	$cusm->command(-label => M"Punkte-Layer zeichnen",
2911		       -command => sub {
2912			   require BBBikeAdvanced;
2913			   tk_plot_additional_layer('p') });
2914	$cusm->command(-label => M"Stra�en/Punkte ausw�hlen",
2915		       -command => sub {
2916			   require BBBikeAdvanced;
2917			   choose_from_additional_layer() });
2918	$cusm->cascade(-label => M("Letzte ge�ffnete Layer")."...");
2919	{
2920	    my $m = $cusm->Menu(-title => M("Letzte ge�ffnete Layer")."...");
2921	    $cusm->entryconfigure("last", -menu => $m);
2922	    $last_loaded_layers_obj =
2923		{
2924		 List => [],
2925		 File => "$main::bbbike_configdir/last_layers",
2926		 Menu => $m,
2927		 Title => M("Letzte Layer").":",
2928		 Cb => sub {
2929		     my($file, %args) = @_;
2930		     my $linetype = delete $args{-linetype};
2931		     require BBBikeAdvanced;
2932		     plot_additional_layer($linetype, $file, %args);
2933		 },
2934		 Max => ($devel_host ? 20 : 12),
2935		};
2936	    load_last_loaded($last_loaded_layers_obj);
2937	}
2938	if ($Tk::platform ne 'MSWin32') {
2939	    $cusm->command(-label => M"Umordnen",
2940			   -accelerator => 'Shift-X',
2941			   -command => sub {
2942			       require BBBikeAdvanced;
2943			       layer_editor() });
2944	}
2945	$cusm->command(-label => M"Layer l�schen",
2946		       -command => sub {
2947			   require BBBikeAdvanced;
2948			   delete_additional_layer() });
2949	if ($devel_host) {
2950	    $cusm->command(-label => M"Layer in �bersichtskarte zeichnen",
2951			   -command => sub {
2952			       require BBBikeAdvanced;
2953			       tk_draw_layer_in_overview();
2954			   });
2955	}
2956	$cusm->command(-label => M"Ausschnitt an Layer anpassen",
2957		       -command => sub {
2958			   require BBBikeAdvanced;
2959			   tk_zoom_view_for_layer() });
2960	$cusm->command(-label => M"Scrollregion an Layer anpassen",
2961		       -command => sub {
2962			   require BBBikeAdvanced;
2963			   tk_set_scrollregion_for_layer() });
2964	$cusm->command(-label => M"Scrollregion f�r Layer vergr��ern",
2965		       -command => sub {
2966			   require BBBikeAdvanced;
2967			   tk_enlarge_scrollregion_for_layer() });
2968	if ($advanced) {
2969	    $cusm->checkbutton(-label => M"Linienbreite 1 Punkt",
2970			       -variable => \$default_line_width,
2971			       -offvalue => undef, # XXX don't work,
2972                                                   # set to 0... ???
2973			       -onvalue => 1,
2974			      );
2975	}
2976	$cusm->radiobutton(-label => M"WWW-Klickmodus", # XXX bessere Bezeichnung
2977			   -variable => \$map_mode,
2978			   -value => MM_URL_SELECT,
2979			   -command => \&set_map_mode,
2980			  );
2981	$cusm->separator;
2982	$cusm->command(-label => M"Gpsman-Daten zeichnen",
2983		       -command => sub {
2984			   draw_gpsman_data($top);
2985		       });
2986	$cusm->cascade(-label => M("Letzte ge�ffnete Tracks/Waypoints")."...");
2987	{
2988	    my $m = $cusm->Menu(-title => M("Letzte ge�ffnete Tracks/Waypoints")."...");
2989	    $cusm->entryconfigure("last", -menu => $m);
2990	    $last_loaded_tracks_obj =
2991		{
2992		 List => [],
2993		 File => "$main::bbbike_configdir/last_tracks",
2994		 Menu => $m,
2995		 Title => M("Letzte Tracks").":",
2996		 Cb => sub {
2997		     my($file, %args) = @_;
2998		     my %draw_args;
2999		     if ($args{-serialized}) {
3000			 eval {
3001			     require Storable;
3002			     require MIME::Base64;
3003			     %draw_args = %{ Storable::thaw(MIME::Base64::decode_base64($args{-serialized})) };
3004			 };
3005			 warn $@ if $@;
3006		     }
3007
3008		     require BBBikeGPS;
3009		     BBBikeGPS::do_draw_gpsman_data($top, $file, %draw_args);
3010		 },
3011		 Max => ($devel_host ? 20 : 12),
3012		};
3013	    load_last_loaded($last_loaded_tracks_obj);
3014	}
3015	$cusm->command(-label => M"GPS-Track-Animation",
3016		       -command => sub {
3017			   require BBBikeAdvanced;
3018			   gps_animation($top);
3019		       });
3020    }
3021    menuright($newlayer_label, $nlm);
3022    menuarrow($nlmb, $nlm, $col++, -special => 'LAYER');
3023
3024    # room for plugin buttons
3025    my $mode_layer_plugin_frame = $misc_frame->Frame->grid
3026	(-row => $curr_row, -column => $col, -sticky => 's');
3027    $top->Advertise(ModeLayerPluginFrame => $mode_layer_plugin_frame);
3028    my $mode_layer_menu_plugin_frame = $misc_frame->Frame->grid
3029	(-row => $curr_row+1, -column => $col, -sticky => 'news');
3030    $top->Advertise(ModeLayerMenuPluginFrame => $mode_layer_menu_plugin_frame);
3031    $col++;
3032
3033    $misc_frame->Label(-text => ' ')->grid(-row => $curr_row,
3034					   -column => $col++);
3035
3036    if (0 && !$no_map) { # no map anymore...
3037	require BBBikeAdvanced;
3038	map_button($misc_frame, $curr_row, \$col);
3039    }
3040
3041###### Vergr��ern #####
3042    my $mapscale_plus_photo = load_photo($misc_frame, 'viewmag+');
3043    my $mapscale_plus_button = $misc_frame->Button
3044      (image_or_text($mapscale_plus_photo, '+'),
3045       -command => sub { scalecanvas($c, 2) },
3046      )->grid(-row => $curr_row, -column => $col, -sticky => 's');
3047    $balloon->attach($mapscale_plus_button, -msg => M"Vergr��ern");
3048    $ch->attach($mapscale_plus_button, -pod => "^\\s*Vergr��ern-Symbol");
3049    $col++;
3050
3051###### Verkleinern #####
3052    my $mapscale_minus_photo = load_photo($misc_frame, 'viewmag-');
3053    my $mapscale_minus_button = $misc_frame->Button
3054      (image_or_text($mapscale_minus_photo, '-'),
3055       -command => sub { scalecanvas($c, 0.5) },
3056      )->grid(-row => $curr_row, -column => $col, -sticky => 's');
3057    $balloon->attach($mapscale_minus_button, -msg => M"Verkleinern");
3058    $ch->attach($mapscale_minus_button, -pod => "^\\s*Verkleinern-Symbol");
3059    $col++;
3060
3061##### Scale of the map #####
3062    my $scale_button = $misc_frame->Button
3063      (-textvariable => \$mapscale,
3064       -width => 9,
3065       -relief => 'ridge',
3066       -bd => ($small_icons ? 0 : 2),
3067       -command => sub { enter_scale() },
3068       -font => $font{'fix15'},
3069      )->grid(-row => $curr_row, -column => $col, -sticky => 's');
3070    $balloon->attach($scale_button, -msg => M"Ma�stab");
3071    $ch->attach($scale_button, -pod => "^\\s*Ma�stab-Feld");
3072    $default_mapscale = calc_mapscale();
3073    $col++;
3074
3075##### �bersichtskarte
3076    my $berlin_overview_small_photo
3077      = load_photo($top, 'berlin_overview_small');
3078    my $overview_check = $misc_frame->$Checkbutton
3079      (image_or_text($berlin_overview_small_photo, 'Ovw'),
3080       -variable => \$show_overview,
3081       -command => sub { show_overview() },
3082      )->grid(-row => $curr_row, -column => $col, -sticky => 's');
3083    $overview_check->bind('<Button-3>' => sub { $show_overview = 1;
3084						show_overview(1) });
3085    enter_leave_bind_for_help($overview_check,
3086			      [M"�bersichtskarte zeigen",
3087			       "",
3088			       M"�bersichtskarte neu laden",
3089			      ]);
3090
3091    $balloon->attach($overview_check, -msg => M"�bersichtskarte");
3092    $ch->attach($overview_check, -pod => "^\\s*�bersichtskarten-Symbol");
3093    $col++;
3094
3095    $misc_frame->Label(-text => ' ')->grid(-row => $curr_row,
3096					   -column => $col++);
3097
3098##### Windrose #####
3099    my $windrose_photo = load_photo($misc_frame, 'windrose');
3100    eval {
3101	die "Low memory" if $lowmem;
3102	require Tk::FireButton;
3103	Tk::FireButton->VERSION(0.04);
3104    };
3105    my $err = $@;
3106    warn $err if $verbose and $err;
3107    my $firebutton = (!$err ? 'FireButton' : 'Button');
3108    $windrose_button = $misc_frame->$firebutton
3109	(image_or_text($windrose_photo, "Wind\nrose"),
3110	 -command => \&windrose,
3111	 -takefocus => 0,
3112	);
3113    if ($windrose_button->isa('Tk::FireButton')) {
3114	$windrose_button->configure(-repeatinterval => 300);
3115    }
3116    $windrose_button->grid(-row => $curr_row, -column => $col, -rowspan => 2);
3117    $windrose_button->bind("<ButtonPress-2>" => sub { windrose(5) });
3118    $windrose_button->bind("<ButtonPress-3>" => sub { center_best() });
3119    enter_leave_bind_for_help($windrose_button,
3120			      [M"Karte scrollen",
3121			       M"Karte schneller scrollen",
3122			       M"Karte zentrieren"]);
3123    $balloon->attach($windrose_button, -msg => M"Kartenausschnitt bewegen");
3124    $ch->attach($windrose_button, -pod => "^\\s*Windrosen-Symbol");
3125    $col++;
3126
3127    $misc_frame->Label(-text => ' ')->grid(-row => $curr_row,
3128					   -column => $col++);
3129
3130    $top->Advertise(MapFrame => $misc_frame);
3131
3132##### misc_frame2 ... #####
3133
3134    $misc_frame2 = $sym_frame->$DockFrame
3135	(-bd => 1, -relief => 'raised',
3136       ($DockFrame eq 'DockFrame' ? ('-dock' => $dock_port2) : ()));
3137    $col = 0;
3138
3139##### Komplex: Suche/Route ... #####
3140    $search_photo = load_photo($misc_frame2, 'search');
3141    my $search_button = $misc_frame2->$Radiobutton
3142      (image_or_text($search_photo, 'Route'),
3143       -variable => \$map_mode,
3144       -value => MM_SEARCH,
3145       -command => \&set_map_mode,
3146      )->grid(-row => $curr_row, -column => $col, -sticky => 's');
3147    $balloon->attach($search_button, -msg => M"Route suchen");
3148    $ch->attach($search_button, -pod => "^\\s*Route suchen");
3149
3150    my $sbmb = $misc_frame2->Menubutton;
3151    my $sbm = $sbmb->Menu(-title => M"Route suchen");
3152
3153    $sbm->radiobutton(-label => M"Suchmodus",
3154		      -variable => \$map_mode,
3155		      -value => MM_SEARCH,
3156		      -command => \&set_map_mode,
3157		      -accelerator => "Shift-S",
3158		     );
3159    $sbm->cascade(-label => M('Route l�schen'));
3160    my $sbm_reset_menu_index = $sbm->index("last");
3161
3162    $sbm->command(-label => M"Route wiederherstellen (Undo)",
3163		  -command =>\&get_undo_route,
3164		  -accelerator => 'Ctrl-Z');
3165    $sbm->command(-label => M"Suche wiederholen",
3166		  -command => \&re_search_gui);
3167    $sbm->command(-label => M"R�ckweg",
3168		  -command => \&way_back_gui);
3169    $sbm->command(-label => M"Register",
3170		  -command => \&show_register,
3171		  -accelerator => '*',
3172		 );
3173    $sbm->command(-label => M"Ausschnitt an Route anpassen",
3174		  -command => sub { zoom_view() });
3175    $sbm->cascade(-label => M"Automatische Anpassung");
3176    {
3177	my $aasm = $sbm->Menu(-title => M"Automatische Anpassung");
3178	$sbm->entryconfigure('last', -menu => $aasm);
3179	$aasm->checkbutton(-label => M"nach dem Laden anpassen",
3180			   -variable => \$zoom_loaded_route,
3181			   -onvalue => 1,
3182			   -offvalue => 0);
3183	$aasm->checkbutton(-label => M"nach dem Laden zentrieren",
3184			   -variable => \$center_loaded_route);
3185	$aasm->checkbutton(-label => M"nach der Berechnung anpassen",
3186			   -variable => \$zoom_new_route,
3187			   -onvalue => 1,
3188			   -offvalue => 0);
3189	$aasm->checkbutton(-label => M"nach der Berechnung aus der Stra�enliste anpassen",
3190			   -variable => \$zoom_new_route_chooseort,
3191			   -onvalue => 1,
3192			   -offvalue => 0);
3193    }
3194    $sbm->separator;
3195
3196    if ($advanced) {
3197	add_search_menu_entries($sbm);
3198    }
3199    if ($advanced || $lowmem) {
3200	$sbm->command(-label => M"Stra�ennetz neu berechnen",
3201		      -command => sub {
3202			  make_net();
3203			  read_sperre_tragen() unless $lowmem; # XXX should be called automatically, maybe?
3204		      });
3205	$sbm->command(-label => M"undef netz",
3206		      -command => sub {
3207			  undef $net;
3208			  undef $comments_net;
3209			  undef $comments_pos_net
3210		      });
3211    }
3212    if ($advanced) {
3213	add_search_net_menu_entries($sbm);
3214	$sbm->separator;
3215    }
3216
3217    unless ($skip_features{"hoehe"}) {
3218	$sbm->checkbutton(-label => M"Steigungen/Gef�lle zeigen",
3219			  -variable => \$show_grade);
3220    }
3221    $sbm->cascade(-label => M('Einf�rben der Route').' ...');
3222    {
3223	my $fbm = $sbm->Menu(-title => M('Einf�rben der Route').' ...');
3224	$sbm->entryconfigure('last', -menu => $fbm);
3225	foreach my $d ([M"Wind", 'wind'],
3226		       [M"Leistung", 'power'],
3227		       [M"schwarz", 'black'],
3228		       [M"rot", 'red'],
3229		       [M"blau", 'blue'],
3230		      ) {
3231	    my $val = $d->[1];
3232	    $fbm->radiobutton(-label => $d->[0],
3233			      -variable => \$coloring,
3234			      -value => $val,
3235			      -command => \&redraw_path,
3236			      );
3237	}
3238	$fbm->checkbutton(-label => M"gestrichelt",
3239			  -variable => \$route_dashed,
3240			  -command => \&redraw_path,
3241			 );
3242	$fbm->checkbutton(-label => M"mit Richtungspfeil",
3243			  -variable => \$route_arrowed,
3244			  -command => \&redraw_path,
3245			 );
3246	$fbm->checkbutton(-label => M"unterhalb liegend",
3247			  -variable => \$route_below,
3248			  -command => \&redraw_path,
3249			 );
3250	if ($advanced && $devel_host) {
3251	    $fbm->command(-label => "spezial gestrichelt",
3252			  -command => sub {
3253			      # XXX this functionality should probably go into addpoint_xy
3254			      for ($c->find("withtag"=>"route"))  { $c->createLine($c->coords($_),-fill=>"black",-dash=>[1,3],-tags=>["route"],-width=>$c->itemcget($_,-width)) if $c->type($_) eq "line"}
3255			  });
3256	}
3257    }
3258
3259    $sbm->command
3260	(-label => M"Streckenprofil",
3261	 -command => sub {
3262	     require BBBikeProfil;
3263	     @{$bbbike_context}{qw/Profil Coords Hoehe Transient Canvas/} =
3264		 (new BBBikeProfil,
3265		  \@realcoords,
3266		  \%hoehe,
3267		  $transient,
3268		  $c);
3269	     $bbbike_context->{Profil}->Show($top, $bbbike_context);
3270	 });
3271    require BBBikeVia;
3272    {
3273	$sbm->cascade(-label => M('Start/Via/Ziel').' ...');
3274	my $viam = $sbm->Menu(-title => M('Start/Via/Ziel').' ...');
3275	$sbm->entryconfigure('last', -menu => $viam);
3276	BBBikeVia::menu_entries($viam);
3277    }
3278
3279    $sbm->separator;
3280    $sbm->checkbutton(-label => M"Kalorienverbrauch anzeigen",
3281		      -variable => \$show_calories,
3282		      -command => sub { show_calories() },
3283		     );
3284
3285    menuright($search_button, $sbm);
3286    menuarrow($sbmb, $sbm, $col++, -menulabel => M"R~oute");
3287
3288    #####
3289
3290    $search_pref_photo = load_photo($misc_frame2, 'search_pref');
3291    my $search_pref_button = $misc_frame2->$Checkbutton
3292      (image_or_text($search_pref_photo, 'Sucheinst.'),
3293       -variable => \$show_enter_opt_preferences,
3294       -command => \&toggle_enter_opt_preferences,
3295      )->grid(-row => $curr_row, -column => $col, -sticky => 's');
3296    $balloon->attach($search_pref_button, -msg => M"Sucheinstellungen");
3297    $ch->attach($search_pref_button,
3298                -pod => "^\\s*Sucheinstellungen");
3299
3300    my $sb2mb = $misc_frame2->Menubutton;
3301    my $sb2m = $sb2mb->Menu(-title => M"Sucheinstellungen");
3302
3303    # Note interplay between these two checkbuttons:
3304    $sb2m->checkbutton(-label => M"Einbahn-/gesperrte Stra�en beachten",
3305		       -variable => \$sperre{'sperre'},
3306		       -command => sub {
3307			   $sperre{'einbahn'} = $sperre{'wegfuehrung'} = $sperre{'sperre'};
3308			   if (!$sperre{'sperre'}) {
3309			       $sperre{'einbahn-strict'} = 0;
3310			   }
3311			   pending(1, 'recalc-net');
3312		       },
3313		      );
3314    $sb2m->checkbutton(-label => M"Einbahn-/gesperrte Stra�en *strikt* beachten",
3315		       -variable => \$sperre{'einbahn-strict'},
3316		       -command => sub {
3317			   if ($sperre{'einbahn-strict'}) {
3318			       $sperre{'einbahn'} = $sperre{'wegfuehrung'} = $sperre{'sperre'} = 1;
3319			   }
3320			   pending(1, 'recalc-net');
3321		       },
3322		      );
3323    $sb2m->cascade(-label => M"Aktuelle Sperrungen");
3324    {
3325	my $am = $sb2m->Menu(-title => M"Aktuelle Sperrungen");
3326	$sb2m->entryconfigure('last', -menu => $am);
3327	$am->checkbutton
3328	    (-label => M"Aktuelle Sperrungen zeichnen und beachten",
3329	     -variable => \$show_active_temp_blockings,
3330	     -command => sub {
3331		 activate_temp_blockings($show_active_temp_blockings);
3332	     },
3333	    );
3334	$am->command(-label => M"In dieser Session aktive Sperrungen",
3335		     -command => sub {
3336			 show_blockings();
3337		     });
3338	$am->command
3339	    (-label => M"Auffrischen der aktuellen Sperrungen",
3340	     -command => sub {
3341		 gui_activate_temp_blockings();
3342	     },
3343	    );
3344	if ($advanced) {
3345	    $am->separator;
3346	    $am->command
3347		(-label => M"Aktuelle und zuk�nftige Sperrungen zeichnen",
3348		 -command => sub {
3349		     $show_active_temp_blockings = 1;
3350		     activate_temp_blockings($show_active_temp_blockings, -from => time);
3351		 },
3352		);
3353	    $am->command
3354		(-label => M"Speichern f�r temp_blockings",
3355		 -command => sub {
3356		     require BBBikeEdit;
3357		     BBBikeEdit::temp_blockings_editor();
3358		 }
3359		);
3360	    $am->separator;
3361	    $am->command
3362		(-label => M"Sperrungen zeichnen f�r Datum",
3363		 -command => \&active_temp_blockings_for_date_dialog,
3364		);
3365	    $am->command
3366		(-label => M"Fr�here und zuk�nftige Sperrungen zeichnen",
3367		 -command => sub {
3368		     $show_active_temp_blockings = 1;
3369		     activate_temp_blockings($show_active_temp_blockings, -from => 0);
3370		 },
3371		);
3372	}
3373    }
3374    $sb2m->cascade(-label => M"Benutzerdefinierte Sperrungen");
3375    {
3376	my $bdm = $sb2m->Menu(-title => M"Benutzerdefinierte Sperrungen");
3377	$sb2m->entryconfigure('last', -menu => $bdm);
3378	$bdm->radiobutton(-label => M"Definieren",
3379			  -variable => \$map_mode,
3380			  -value => MM_USEREDIT,
3381			  -accelerator => "Shift-U",
3382			  -command => sub { # XXX don't duplicate code, see <U>
3383			      set_cursor('delnet', 'X_cursor');
3384			  });
3385	$bdm->command(-label => M"Standard laden",
3386		      -command => sub { load_user_dels() });
3387	$bdm->command(-label => M"Standard speichern",
3388		      -command => sub { save_user_dels() });
3389	$bdm->command(-label => M"Laden",
3390		      -command => sub {
3391			  my $file = $top->getOpenFile;
3392			  if (defined $file) {
3393			      load_user_dels($file);
3394			  }
3395		      });
3396	$bdm->command(-label => M"Speichern",
3397		      -command => sub {
3398			  my $file = $top->getSaveFile;
3399			  if (defined $file) {
3400			      save_user_dels($file);
3401			  }
3402		      });
3403	$bdm->command(-label => M"Alle l�schen",
3404		      -command => sub { delete_user_dels() });
3405	if ($advanced) {
3406	    $bdm->command(-label => M"In die Zwischenablage kopieren",
3407			  -command => sub {
3408			      my $s = $net->create_user_deletions_object;
3409			      # XXX usage of @inslauf_selection is a hack!
3410			      $c->SelectionOwn;
3411			      @inslauf_selection = $s->as_string;
3412			  },
3413			 );
3414	}
3415    }
3416
3417    $sb2m->checkbutton(-label => M"Tragen strikt vermeiden",
3418		      -variable => \$sperre{'tragen'},
3419		      -command => sub {
3420			  pending(1, 'recalc-net');
3421		      },
3422		     );
3423    $sb2m->checkbutton(-label => M"Schlechte Wege vermeiden",
3424		      -variable => \$sperre{'Q3'},
3425		      -command => sub {
3426			  pending(1, 'recalc-net');
3427		      },);
3428    unless ($skip_features{faehren}) {
3429	$sb2m->checkbutton(-label => M"F�hren verwenden",
3430			   -variable => \$use_faehre,
3431			   -command => sub {
3432			       pending(1, 'recalc-net');
3433			   },
3434			  );
3435    }
3436    $sb2m->separator;
3437    $sb2m->checkbutton(-label => M"Stra�enqualit�t-Optimierung",
3438		      -variable => \$qualitaet_s_optimierung,
3439		     );
3440    $sb2m->checkbutton(-label => M"Stra�enkategorie-Optimierung",
3441		       -variable => \$strcat_optimierung,
3442		       -command => sub {
3443			   if ($strcat_optimierung) {
3444			       $N_RW_optimization = 0;
3445			       $N_RW1_optimization = 0;
3446			   }
3447		       },
3448		     );
3449    $sb2m->checkbutton(-label => M"Optimierung der sonstigen Beeintr�chtigungen",
3450		      -variable => \$handicap_s_optimierung,
3451		     );
3452    $sb2m->checkbutton(-label => M"Ampel-Optimierung",
3453		      -variable => \$ampel_optimierung,
3454		      -command => \&calc_ampel_optimierung,
3455		     );
3456    $sb2m->checkbutton(-label => M"Radwege-Optimierung",
3457		       -variable => \$radwege_optimierung,
3458		       -command => sub {
3459			   if ($radwege_optimierung) {
3460			       $N_RW_optimization = 0;
3461			       $N_RW1_optimization = 0;
3462			   }
3463		       }
3464		      );
3465    $sb2m->checkbutton(-label => M"Hauptstra�en ohne Radwege/Busspuren meiden",
3466		       -variable => \$N_RW_optimization,
3467		       -command => sub {
3468			   if ($N_RW_optimization) {
3469			       $radwege_optimierung = 0;
3470			       $strcat_optimierung = 0;
3471			       $N_RW1_optimization = 0;
3472			   }
3473		       }
3474		      );
3475    $sb2m->checkbutton(-label => M"Hauptstra�en ohne Radwege meiden",
3476		       -variable => \$N_RW1_optimization,
3477		       -command => sub {
3478			   if ($N_RW1_optimization) {
3479			       $radwege_optimierung = 0;
3480			       $strcat_optimierung = 0;
3481			       $N_RW_optimization = 0;
3482			   }
3483		       }
3484		      );
3485    unless ($skip_features{"green"}) {
3486	$sb2m->cascade(-label => M("Gr�ne Wege")."...");
3487	my $gwm = $sb2m->Menu(-title => M"Gr�ne Wege");
3488	$sb2m->entryconfigure('last', -menu => $gwm);
3489	$gwm->radiobutton(-label => M"egal",
3490			  -variable => \$green_optimization,
3491			  -value => 0,
3492			 );
3493	$gwm->radiobutton(-label => M"bevorzugen",
3494			  -variable => \$green_optimization,
3495			  -value => 1,
3496			 );
3497	$gwm->radiobutton(-label => M"stark bevorzugen",
3498			  -variable => \$green_optimization,
3499			  -value => 2,
3500			 );
3501    }
3502    {
3503	$sb2m->cascade(-label => M("Unterwegs mit")."...");
3504	my $umm = $sb2m->Menu(-title => M"Unterwegs mit");
3505	$sb2m->entryconfigure('last', -menu => $umm);
3506	$umm->radiobutton(-label => M"nichts weiter", # XXX expr?
3507			  -variable => \$special_vehicle_rb,
3508			  -value => 'normal', # used to be $special_vehicle="", but this does not work with Perl/Tk
3509			  -command => sub { pending(1, 'recalc-net') },
3510			 );
3511	$umm->radiobutton(-label => M"Anh�nger",
3512			  -variable => \$special_vehicle_rb,
3513			  -value => 'trailer',
3514			  -command => sub { pending(1, 'recalc-net') },
3515			 );
3516	$umm->radiobutton(-label => M"Kindersitz mit Kind",
3517			  -variable => \$special_vehicle_rb,
3518			  -value => 'childseat',
3519			  -command => sub { pending(1, 'recalc-net') },
3520			 );
3521    }
3522    unless ($skip_features{"nolighting"}) {
3523	$sb2m->checkbutton(-label => M"Unbeleuchtete Stra�en meiden",
3524			   -variable => \$unlit_streets_optimization,
3525			  );
3526    }
3527    if ($advanced) { # XXX
3528	unless ($skip_features{"tram"}) {
3529	    $sb2m->checkbutton(-label => M"Stra�enbahnschienen meiden",
3530			       -variable => \$tram_optimization,
3531			      );
3532	}
3533    }
3534    unless ($skip_features{"hoehe"}) {
3535	$sb2m->checkbutton(-label => M"Steigungsoptimierung",
3536			   -variable => \$steigung_optimierung,
3537			  );
3538    }
3539    if ($advanced && $devel_host) {
3540	# sowieso vorerst sinnlos...
3541	$sb2m->checkbutton(-label => M"Abbiege-Optimierung",
3542			  -variable => \$abbiege_optimierung,
3543			  );
3544    }
3545    $sb2m->separator;
3546    $sb2m->command(-label => M"Optimierungsparameter einstellen",
3547		  -command => \&enter_opt_preferences,
3548		 );
3549    if ($advanced) {
3550	# experimenteller Code
3551	$sb2m->command(-label => M"Optimierungsparameter einstellen Nr.2",
3552		      -command => \&enter_opt_preferences2,
3553		     );
3554	require BBBikeAdvanced;
3555	penalty_menu($sb2m);
3556    }
3557
3558    menuright($search_pref_button, $sb2m);
3559    menuarrow($sb2mb, $sb2m, $col++, -menulabel => M"~Sucheinstellungen");
3560
3561    #####
3562
3563    my $strlist_photo = load_photo($misc_frame2, 'strlist');
3564    my $strlist_button = $misc_frame2->$Checkbutton
3565	(image_or_text($strlist_photo, 'StrL'),
3566	 -variable => \$show_strlist,
3567	 -command => sub { show_route_strname() },
3568	 )->grid(-row => $curr_row, -column => $col, -sticky => 's');
3569    $balloon->attach($strlist_button,
3570		     -msg => M"Beschreibung der aktuellen Route");
3571    $ch->attach($strlist_button,
3572                -pod => "^\\s*Beschreibung der aktuellen Route");
3573    my $slbmb = $misc_frame2->Menubutton;
3574    my $slbm = $slbmb->Menu(-title => M"Beschreibung der aktuellen Route");
3575    $slbm->checkbutton
3576	(-label    => M"Routenliste",
3577	 -accelerator => "Shift-B",
3578	 -variable => \$show_strlist,
3579	 -command  => sub { show_route_strname() },
3580	);
3581    $slbm->checkbutton
3582	(-label    => M"Automatisches Anzeigen",
3583	 -variable => \$auto_show_list,
3584	);
3585    $slbm->command
3586	(-label    => M"Statistik",
3587	 -command  => \&show_statistics,
3588	);
3589    if ($advanced) {
3590	$slbm->command(-label => M"Ampeln an der aktuellen Route",
3591		       -command => sub { ampeln_on_route(@realcoords) });
3592	$slbm->command(-label => M"GPS-Upload mit Ampelschaltungen",
3593		       -command => sub {
3594			   require "$FindBin::RealBin/GpsmanDataAmpeln.pm";
3595			   make_ampel_route();
3596		       });
3597    }
3598    menuright($strlist_button, $slbm);
3599    menuarrow($slbmb, $slbm, $col, -menulabel => M"Routen~liste");
3600    $col++;
3601
3602    my $reset_photo = load_photo($misc_frame2, 'cross');
3603    my $reset_button = $misc_frame2->Button
3604	(image_or_text($reset_photo, 'X'),
3605	 -command => \&delete_route,
3606	 )->grid(-row => $curr_row, -column => $col, -sticky => 's');
3607    $balloon->attach($reset_button, -msg => M"Route l�schen");
3608    $ch->attach($reset_button, -pod => "^\\s*Route l�schen");
3609    my $resetmb = $misc_frame2->Menubutton;
3610    my $resetm = $resetmb->Menu(-title => M"Route l�schen");
3611    $resetm->command(-label => M"Gesamte Route l�schen",
3612		     -command => \&delete_route,
3613		     -accelerator => 'Ctrl-X',
3614		    );
3615    $resetm->command(-label => M"Letzten Punkt der Route l�schen",
3616		     -command => \&mouse_dellast,
3617		     -accelerator => '<-',
3618		    );
3619    $resetm->command(-label => M"Bis zum letzten Via l�schen",
3620		     -command => \&deltovia,
3621		     -accelerator => 'Del',
3622		    );
3623    menuright($reset_button, $resetm);
3624    menuarrow($resetmb, $resetm, $col, -menulabel => M"Route l�schen");
3625    $col++;
3626    # XXX Check this on Windows! XXX The Tk::Menu manual says: do not
3627    # use "clone" outside of the Tk library!
3628    $sbm->entryconfigure($sbm_reset_menu_index, -menu => $resetm->clone($sbmb, "normal"));
3629
3630    my $reverse_photo = load_photo($misc_frame2, 'rueckweg');
3631    my $reverse_button = $misc_frame2->Button
3632	(image_or_text($reverse_photo, 'Rev'),
3633	 -command => \&way_back_gui,
3634	 )->grid(-row => $curr_row, -column => $col, -sticky => 's');
3635    $reverse_button->bind("<ButtonPress-3>" => sub {
3636	IncBusy($top);
3637	eval {
3638	    reverse_route();
3639	};
3640	DecBusy($top);
3641    });
3642    $balloon->attach($reverse_button, -msg => M"R�ckweg");
3643    $ch->attach($reverse_button, -pod => "^\\s*R�ckweg-Symbol");
3644    $col++;
3645
3646    my $koord_photo = load_photo($misc_frame2, 'koord');
3647    my $buttonpoint_check = $misc_frame2->$Radiobutton
3648      (image_or_text($koord_photo, 'Koord'),
3649       -variable => \$map_mode,
3650       -value => MM_BUTTONPOINT,
3651       -command => \&set_map_mode,
3652      )->grid(-row => $curr_row, -column => $col, -sticky => 's');
3653    $balloon->attach($buttonpoint_check, -msg => M"Koordinaten in Zwischenablage");
3654    $ch->attach($buttonpoint_check, -pod => "^\\s*Koordinaten-Symbol");
3655
3656    my($bpcm);
3657    if (!$advanced) {
3658	$buttonpoint_check->configure(-state => 'disabled');
3659    } else {
3660	my $bpcmb = $misc_frame2->Menubutton;
3661	$bpcm = $bpcmb->Menu(-title => M"Bearbeiten");
3662	advanced_coord_menu($bpcm);
3663	menuright($buttonpoint_check, $bpcm);
3664	menuarrow($bpcmb, $bpcm, $col, -menulabel => M"~Bearbeiten");
3665    }
3666    $col++;
3667
3668    my $info_photo = load_photo($misc_frame2, 'info');
3669    my $info_check = $misc_frame2->$Radiobutton
3670	(image_or_text($info_photo, 'Info'),
3671	 -variable => \$map_mode,
3672	 -value => MM_INFO,
3673	 -command => \&set_map_mode,
3674	)->grid(-row => $curr_row, -column => $col, -sticky => 's');
3675    $balloon->attach($info_check, -msg => M"Information");
3676    $ch->attach($info_check, -pod => "^\\s*Info-Symbol");
3677    $col++;
3678
3679if (!$MM_DRAG_IS_OBSOLETE) {
3680    my $drag_photo = load_photo($misc_frame2, 'movehand');
3681    my $drag_check = $misc_frame2->$Radiobutton
3682	(image_or_text($drag_photo, 'Drag'),
3683	 -variable => \$map_mode,
3684	 -value => MM_DRAG,
3685	 -command => \&set_map_mode,
3686	)->grid(-row => $curr_row, -column => $col, -sticky => 's');
3687    $balloon->attach($drag_check, -msg => M"Karte verschieben");
3688    # XXX $ch->attach($drag_check, -pod => "^\\s*Karte verschieben");
3689    $col++;
3690}
3691
3692    # room for plugin buttons
3693    my $mode_plugin_frame = $misc_frame2->Frame->grid
3694	(-row => $curr_row, -column => $col, -sticky => 's');
3695    $top->Advertise(ModePluginFrame => $mode_plugin_frame);
3696    my $mode_menu_plugin_frame = $misc_frame2->Frame->grid
3697	(-row => $curr_row+1, -column => $col, -sticky => 'news');
3698    $top->Advertise(ModeMenuPluginFrame => $mode_menu_plugin_frame);
3699    $col++;
3700
3701    $misc_frame2->Label(-text => ' ')->grid(-row => $curr_row,
3702					    -column => $col++);
3703
3704## DEBUG_BEGIN
3705#mymstat("iconframe: load/save/print buttons");
3706## DEBUG_END
3707##### Komplex: Laden/Speichern/Drucken #####
3708    my $load_photo = load_photo($misc_frame2, 'open');
3709    my $load_button = $misc_frame2->Button
3710      (image_or_text($load_photo, 'Load'),
3711       -command => sub { load_save_route(0) }
3712      )->grid(-row => $curr_row, -column => $col, -sticky => 's');
3713    $balloon->attach($load_button, -msg => M"Laden einer Route");
3714    $ch->attach($load_button, -pod => "^\\s*�ffnen-Symbol");
3715    my $last_loaded_mb = $misc_frame2->Menubutton;
3716    $last_loaded_menu = $last_loaded_mb->Menu
3717	(-title => M"letzte ge�ffnete Routen",
3718	 -disabledforeground => $wb->cget(-foreground));
3719    menuright($load_button, $last_loaded_menu);
3720    menuarrow($last_loaded_mb, $last_loaded_menu, $col,
3721	      -menulabel => M"letzte ge�ffnete Routen",
3722	      -special   => "OPEN");
3723    $col++;
3724
3725    my $save_photo = load_photo($misc_frame2, 'save');
3726    my $save_button = $misc_frame2->Button
3727      (image_or_text($save_photo, 'Save'),
3728       -command => sub { load_save_route(1) }
3729      )->grid(-row => $curr_row, -column => $col, -sticky => 's');
3730    $balloon->attach($save_button, -msg => M"Sichern einer Route");
3731    $ch->attach($save_button, -pod => "^\\s*Speichern-Symbol");
3732    my $svmb = $misc_frame2->Menubutton;
3733    my $svm = $svmb->Menu(-title => M"Exportieren",
3734			  -disabledforeground => $save_button->cget(-foreground));
3735    $svm->command(-label => M('Karte speichern als').' ...',
3736		  -state => "disabled",
3737		  -font => $font{"bold"});
3738
3739    foreach my $fmt (['PDF',        'pdf'],
3740		     ['PNG',        'png'],
3741		     ['GIF',        'gif'],
3742		     ['JPEG',       'jpeg'],
3743		     ['PPM',        'ppm'],
3744		     ['Postscript', 'ps'],
3745		    ) {
3746	$svm->command(-label => "$fmt->[0]",
3747		      -command => sub {
3748			  $svm->after(50, sub { export_visible_map($fmt->[1]) });
3749		      });
3750	if ($fmt->[1] eq 'ps') {
3751	    $svm->cascade(-label => M("Postscript-Aufl�sung").' ...');
3752	    my $psm = $svm->Menu(-title => M("Postscript-Aufl�sung").' ...');
3753	    $svm->entryconfigure("last", -menu => $psm);
3754	    my(%sizes) = (36 => 0, 72 => 0, 100 => 0, 150 => 0);
3755	    $sizes{int($top_dpi)}++;
3756	    foreach my $size (sort { $a <=> $b } keys %sizes) {
3757		$psm->radiobutton(-label => $size . " dpi"
3758				  . ($size == int($top_dpi) ? " ".M"(normal)" : ""),
3759				  -variable => \$ps_image_res,
3760				  -value => $size . "x" . $size,
3761				 );
3762	    }
3763	}
3764    }
3765
3766    $svm->separator;
3767    $svm->command(-label => M('Route speichern als').' ...',
3768		  -state => "disabled",
3769		  -font => $font{"bold"});
3770    foreach my $fmt (
3771		     # GPS
3772		     ['GPX (Track)', 'GPX/track'],
3773		     'GPX (Route)',
3774		     ($advanced ? ['KML (GoogleEarth)', 'KML/track'] : ()),
3775		     ['GPSMAN (Track)', 'GpsmanData'],
3776		     'GPSMAN (Route)',
3777		     ['G7toWin (ASCII)', 'G7toWin_ASCII'],
3778		     ['Waypoint+ (Track)', 'WaypointPlus'],
3779
3780		     # map/gis
3781		     'bbd (BBBike data)',
3782		     ($advanced ? ('ESRI') : ()),
3783		     # XXX not yet ready: ($devel_host ? ('OVL (TOP50)') : ()),
3784
3785		     # vector oriented
3786		     'PDF',
3787		     'XFig',
3788		     ($advanced ? ('SVG') : ()),
3789
3790		     '-',
3791		     'GPS direkt',
3792		     [M('Route zu einem Garmin senden'), 'DirectGarmin'],
3793		     [M('Senden der Route zu einem Garmin simulieren'), 'DirectGarmin_Test'],
3794		     ($devel_host ? [M("Route mit gpsbabel senden"), "GpsbabelSend"] : ()),
3795		     ($devel_host ? [M("Route mit MapSource senden"), "MapSourceSend"] : ()),
3796		    ) {
3797	if ($fmt eq '-') {
3798	    $svm->separator;
3799	} elsif ($fmt eq 'GPS direkt') {
3800	    $svm->command(-label => M($fmt),
3801			  -state => "disabled",
3802			  -font => $font{"bold"});
3803	} elsif ($fmt eq 'PDF') {
3804	    $svm->command
3805		(-label => $fmt,
3806		 -command => \&pdf_export,
3807		);
3808	} elsif ($fmt eq 'SVG') {
3809	    $svm->command
3810		(-label => $fmt,
3811		 -command => \&svg_export,
3812		);
3813	} elsif ($fmt eq 'XFig') {
3814	    $svm->command
3815		(-label => $fmt,
3816		 -command => sub {
3817		     my $file = $top->getSaveFile
3818			 (-defaultextension => '.fig',
3819			  -filetypes => [[M"FIG-Dateien" => '.fig'],
3820					 [M"Alle Dateien" => '*']],
3821			 );
3822		     return unless defined $file;
3823		     require Tk::CanvasFig;
3824		     IncBusy($top);
3825		     eval {
3826			 mkdir $file."-images", 0755;
3827			 $c->fig(-file => $file,
3828				 -imagetype => (is_in_path("ppmtopcx") ? 'pcx' : 'xpm'),
3829				 -imagedir => $file."-images");
3830		     };
3831		     warn __LINE__ . ": $@" if $@;
3832		     DecBusy($top);
3833		 });
3834	} elsif ($fmt =~ /^ovl/i) {
3835	    $svm->command
3836		(-label => $fmt,
3837		 -command => sub {
3838		     require GPS::Ovl;
3839		     GPS::Ovl->new->tk_export(coords => \@realcoords);
3840		 }
3841		);
3842	} elsif ($fmt =~ /^bbd/) {
3843	    $svm->command
3844		(-label => $fmt,
3845		 -command => \&save_route_as_bbd
3846		);
3847	} elsif ($fmt eq 'GPSMAN (Route)') {
3848	    $svm->command
3849		(-label => $fmt,
3850		 -command => sub {
3851		     gps_interface('BBBikeGPS::GpsmanRoute', -noloading => 1);
3852		 });
3853	} elsif ($fmt eq 'GPX (Route)') {
3854	    $svm->command
3855		(-label => $fmt,
3856		 -command => sub { save_route_as_optimized_gpx() },
3857		);
3858	} elsif ($fmt =~ /^esri/i) {
3859	    if (-x "$FindBin::RealBin/miscsrc/bbd2esri" &&
3860		-x "$FindBin::RealBin/miscsrc/bbr2bbd"
3861	       ) {
3862		$svm->command
3863		    (-label => $fmt,
3864		     -command => \&save_route_as_esri
3865		    );
3866	    }
3867	} elsif (ref $fmt eq 'ARRAY') {
3868	    my($label, $module) = @$fmt;
3869	    if ($module =~ m{^GPX/(.*)$}) {
3870		my $as = $1;
3871		$svm->command
3872		    (-label => $label,
3873		     -command => sub { save_route_as_gpx(-as => $as) },
3874		    );
3875	    } elsif ($module =~ m{^KML/(.*)$}) {
3876		my $as = $1;
3877		$svm->command
3878		    (-label => $label,
3879		     -command => sub { save_route_as_kml(-as => $as) },
3880		    );
3881	    } elsif ($module =~ m{^(GpsbabelSend|MapSourceSend)$}) {
3882		$svm->command
3883		    (-label => $label,
3884		     -command => sub {
3885			 gps_interface('BBBikeGPS::'.$module, -noloading => 1);
3886		     });
3887	    } elsif ($module eq 'DirectGarmin') {
3888		$svm->command
3889		    (-label => $label,
3890		     -command => sub { send_route_to_gps() },
3891		     -accelerator => 'Ctrl-G',
3892		    );
3893	    } else {
3894		$svm->command
3895		    (-label => $label,
3896		     -command => sub { gps_interface($module) },
3897		    );
3898	    }
3899	} else {
3900	    warn "XXX SHOULD NOT HAPPEN XXX";
3901	}
3902    }
3903
3904    menuright($save_button, $svm);
3905    menuarrow($svmb, $svm, $col++, -menulabel => M"Speichern",
3906	      -special   => 'SAVE');
3907
3908    my $print_photo = load_photo($misc_frame2, 'printer');
3909    my $print_button = $misc_frame2->Button
3910	(image_or_text($print_photo, 'Print'),
3911	 -command => sub { print_function() },
3912	 )->grid(-row => $curr_row, -column => $col, -sticky => 's');
3913    $balloon->attach($print_button, -msg => M"Drucken der Karte");
3914    $ch->attach($print_button, -pod => "^\\s*Drucken-Symbol");
3915    my $prmb = $misc_frame2->Menubutton;
3916    my $prm = $prmb->Menu(-title => M"Druckeinstellungen");
3917    foreach my $color ([M"Farbe", 'color'],
3918		       [M"Graustufen", 'gray'],
3919		       [M"Schwarz/Wei�", 'mono'],
3920		      ) {
3921	$prm->radiobutton(-label => $color->[0],
3922			  -value => $color->[1],
3923			  -variable => \$ps_color,
3924			 );
3925    }
3926    $prm->separator;
3927    $prm->radiobutton(-label => M"Landscape",
3928		      -value => 1,
3929		      -variable => \$ps_rotate,
3930		     );
3931    $prm->radiobutton(-label => M"Portrait",
3932		      -value => 0,
3933		      -variable => \$ps_rotate,
3934		     );
3935    $prm->separator;
3936    $prm->checkbutton(-label    => M"auf A4 skalieren",
3937		      -variable => \$ps_scale_a4,
3938		     );
3939    $prm->checkbutton(-label    => M"Legende",
3940		      -variable => \$use_legend,
3941		     );
3942    $prm->checkbutton(-label    => M"Legende rechts statt links",
3943		      -variable => \$use_legend_right,
3944		     );
3945    menuright($print_button, $prm);
3946    menuarrow($prmb, $prm, $col++, -menulabel => M"Drucken",
3947	      -special   => 'PRINT');
3948
3949    $misc_frame2->Label(-text => ' ')->grid(-row => $curr_row,
3950					    -column => $col++);
3951
3952##### Bikepower #####
3953    my $bike_photo = load_photo($misc_frame2, 'bicycle');
3954    my $bike_button = $misc_frame2->Button
3955      (image_or_text($bike_photo, 'Bike'),
3956       -command => sub { my %args;
3957			 unless (defined $ENV{LANG} && $ENV{LANG} !~ /^de/) {
3958			     $args{-lang} = 'de';
3959			 }
3960			 $args{-applyhook} = $args{-savedefaultshook} = sub {
3961			     # XXX
3962			 };
3963			 eval {
3964			     my $bp = $bp_obj->tk_interface($top, %args);
3965			     set_as_toolwindow($bp);
3966			 };
3967			 if ($@) { status_message($@, 'err') }
3968		     }
3969      )->grid(-row => $curr_row, -column => $col, -rowspan => 2);
3970    $bike_button->configure(-state => 'disabled') if !$bikepwr;
3971    $balloon->attach
3972      ($bike_button,
3973       -balloonmsg => M"Bikepower",
3974       -statusmsg => M"Bikepower: Eingeben von fahrradspezifischen Daten");
3975    $ch->attach($bike_button, -pod => "^\\s*Fahrrad-Symbol");
3976    $col++;
3977
3978    $misc_frame2->Label(-text => ' ')->grid(-row => $curr_row,
3979					   -column => $col++);
3980
3981##### Komplex: sonstige Optionen #####
3982    my $opt_photo = load_photo($misc_frame2, 'opt');
3983    my $opt_button = $misc_frame2->Button
3984      (image_or_text($opt_photo, 'Opt'),
3985       -command => \&optedit,
3986      )->grid(-row => $curr_row, -column => $col, -sticky => 's');
3987    if (!$opt) {
3988	$opt_button->configure(-state => 'disabled');
3989    }
3990    $balloon->attach($opt_button, -msg => M"Optionen");
3991    $ch->attach($opt_button, -pod => "^\\s*Options-Symbol");
3992
3993    my $opbmb = $misc_frame2->Menubutton;
3994    my $opbm = $BBBike::Menubar::option_menu = $opbmb->Menu(-title => M"Einstellungen");
3995    $BBBike::Menubar::option_menu = $BBBike::Menubar::option_menu; # peacify -w
3996    # XXX wenn die Save-Funktion funktioniert, folgendes immer ausf�hren:
3997    if ($advanced && $devel_host) {
3998	$opbm->command(-label => M("Konfigurations-Wizard"),
3999		       -command => sub { require Wizards;
4000					 config_wizard($top);
4001				     });
4002	$opbm->separator;
4003    }
4004    if (0) {
4005	# The portrait/landscape switch is never active. But keep the
4006	# code nevertheless, maybe it will be useful one day if we
4007	# have turnable screen support (but maybe it will be never
4008	# needed)
4009        $opbm->radiobutton(-label => M"Landscape",
4010			   -variable => \$orientation,
4011			   -value => 'landscape',
4012			   -command => sub {
4013			       my $replotsub = get_plotted();
4014			       set_landscape();
4015			       $replotsub->();
4016			   });
4017        $opbm->radiobutton(-label => M"Portrait",
4018			   -variable => \$orientation,
4019			   -value => 'portrait',
4020			   -command => sub {
4021			       my $replotsub = get_plotted();
4022			       set_portrait();
4023			       $replotsub->();
4024			   });
4025    }
4026    if (!$city_obj->is_osm_source) { # no scope with osm data
4027	$opbm->cascade(-label => M('Scope').' ...');
4028	{
4029	    my $sbm = $opbm->Menu(-title => M('Scope').' ...');
4030	    $opbm->entryconfigure('last', -menu => $sbm);
4031	    $sbm->command(-label => M"Stadt",
4032			  -command => \&city_settings);
4033	    $sbm->command(-label => M"n�heres Umland",
4034			  -command => \&region_settings);
4035	    unless ($skip_features{wideregion}) {
4036		$sbm->command(-label => M"jwd",
4037			      -command => \&jwd_settings);
4038	    }
4039	}
4040    }
4041    $opbm->separator;
4042    if (defined $c_balloon) {
4043	$opbm->cascade(-label => M('Canvas balloon').' ...');
4044	{
4045	    my $cbm = $opbm->Menu(-title => M('Canvas balloon').' ...');
4046	    $opbm->entryconfigure('last', -menu => $cbm);
4047	    foreach my $d ([M('kein'), 0],
4048			   [M('nur Route'), 1],
4049			   [M('�berall'), 2]) {
4050		my $val = $d->[1];
4051		$cbm->radiobutton(-label => $d->[0],
4052				  -variable => \$use_c_balloon,
4053				  -value => $val,
4054				  -command => \&c_balloon_update,
4055				 );
4056	    }
4057	}
4058    }
4059    $opbm->command
4060      (-label => M"Farben �ndern",
4061       -command => sub {
4062	   require Tk::ColorEditor;
4063	   my $cedit = $top->ColorEditor;
4064	   $cedit->Show;
4065       },
4066      );
4067    $opbm->command
4068      (-label => M"Schriftart �ndern",
4069       -command => sub { change_font() },
4070      );
4071    $opbm->checkbutton(-label => M"gedrehte Zeichens�tze",
4072		       -variable => \$use_font_rot);
4073    $opbm->checkbutton(-label => M"St�ndige Markierung",
4074		       -variable => \$steady_mark,
4075		      );
4076    $opbm->command(-label => M"Markierung l�schen",
4077		   -command => \&delete_markers,
4078		  );
4079    $opbm->cascade(-label => M"Mittlere Maustaste");
4080    {
4081	my $sopbm = $opbm->Menu(-title => M"Mittlere Maustaste");
4082	$opbm->entryconfigure('last', -menu => $sopbm);
4083	foreach my $val (B2M_NONE, B2M_SCAN, B2M_FASTSCAN,
4084			 B2M_AUTOSCROLL, B2M_DELLAST,
4085			) {
4086	    my $label = $b2_mode_desc{$val};
4087	    $label = "???" if (!defined $label);
4088	    $sopbm->radiobutton(-label => $label,
4089				-variable => \$b2_mode,
4090				-value => $val,
4091				-command => \&set_b2,
4092			       );
4093	}
4094    }
4095
4096    {
4097	$opbm->cascade(-label => M('Aktualisieren').' ...');
4098	my $am = $opbm->Menu(-title => M('Aktualisieren').' ...');
4099	$opbm->entryconfigure("last", -menu => $am);
4100
4101	my $set_immediate_sub = sub {
4102	    my($val) = @_;
4103	    foreach (qw(replot-str-s replot-str-l
4104			replot-str-qs replot-str-ql
4105			replot-str-hs replot-str-hl
4106			replot-str-r replot-str-b
4107			replot-str-u replot-str-rw
4108			replot-str-v replot-str-f
4109			replot-p-r   replot-p-b
4110			replot-p-u
4111			replot-p-o replot-str-w
4112		       )) { # XXX weitere replots???
4113		$immediate{$_} = $val;
4114	    }
4115	};
4116
4117	my $rp; # XXX ein bi�chen hacky (weiter unten)
4118	foreach my $def ([M"Auf Anfrage aktualisieren", 0],
4119			 [M"Ausgabe sofort aktualisieren", 1],
4120			 [M"Ausgabe verz�gert aktualisieren", 2],
4121			) {
4122	    my $val = $def->[1];
4123	    my $button = $am->radiobutton
4124	      (-label => $def->[0],
4125	       -variable => \$immediate_replot,
4126	       -value => $val,
4127	       -command => sub { $set_immediate_sub->($val) });
4128	    $rp = $button if ($val == $immediate_replot);
4129	}
4130	# XXX hier m��ten eigentlich auch die drei Alternativen stehen
4131	my $rc = $am->checkbutton
4132	  (-label => M"Netz sofort aktualisieren",
4133	   -variable => \$immediate_recalc,
4134	   -command => sub {
4135	       $immediate{'recalc-net'} = $immediate_recalc;
4136	   },
4137	  );
4138
4139	if ($Tk::VERSION < 803 || $Tk::VERSION >= 804.025) {
4140	    $rp->cget(-command)->Call if $rp;
4141	    $rc->cget(-command)->Call;
4142	} else {
4143	    $rp->cget(-command)->() if $rp;
4144	    $rc->cget(-command)->();
4145	}
4146	$am->command(-label => M"Alles aktualisieren",
4147		     -command => sub { update() });
4148    }
4149
4150### not yet..., see start_followmouse()
4151#      $opbm->checkbutton(-label => M"Followmouse",
4152#  		       -variable => \$followmouse,
4153#  		       -command => sub {
4154#  			   if ($followmouse) {
4155#  			       start_followmouse();
4156#  			   } else {
4157#  			       stop_followmouse();
4158#  			   }
4159#  		       },
4160#  		      );
4161    if ($advanced) {
4162	stderr_menu($opbm);
4163    }
4164    $opbm->checkbutton(-label => M"Wortreich (verbose)",
4165		       -variable => \$verbose,
4166		       -command => \&set_verbose);
4167
4168    if (!$city_obj->is_osm_source) {
4169	$opbm->command
4170	    (-label => M"Daten-Update �ber das Internet",
4171	     -command => \&update_via_internet,
4172	    );
4173    }
4174
4175    $opbm->command(-label => M"Alarmliste",
4176		   -command => sub {
4177		       require BBBikeAlarm;
4178		       BBBikeAlarm::tk_show_all();
4179		   },
4180		  );
4181
4182    if ($advanced && $os ne "win") {
4183	$opbm->command(-label => M"Start BBBike-Server",
4184		       -command => sub { gui_start_bbbike_server() },
4185		      );
4186    }
4187
4188    if (!$standard_menubar) {
4189	plugin_menu($opbm);
4190    }
4191    if ($advanced) {
4192	advanced_option_menu($opbm);
4193    }
4194    menuright($opt_button, $opbm);
4195    menuarrow($opbmb, $opbm, $col++,
4196	      -menulabel => M"~Einstellungen", -special => 'OPTIONS');
4197
4198    my $help_photo = load_photo($misc_frame2, 'help');
4199    my $help_button = $misc_frame2->Button
4200      (image_or_text($help_photo, '?'),
4201       -command => sub {
4202	   eval {
4203	       require Tk::Pod;
4204	       Tk::Pod->Dir($FindBin::Bin);
4205	       $top->Pod(-file => $FindBin::Script . ".pod",
4206			 -title => M"Dokumentation zu BBBike");
4207	   };
4208	   if ($@) {
4209	       my $r;
4210	       my $bbbike_html = Tk::findINC("doc/bbbike.html");
4211	       my $url;
4212	       if (defined $bbbike_html && -r $bbbike_html) {
4213		   $url = "file:$bbbike_html";
4214		   require WWWBrowser;
4215		   $r = WWWBrowser::start_browser($url);
4216	       }
4217	       if (!$r) {
4218		   return if !perlmod_install_advice('Tk::Pod');
4219	       }
4220	   }
4221       },
4222      )->grid(-row => $curr_row, -column => $col, -sticky => 's');
4223    $balloon->attach($help_button, -msg => M"Hilfe");
4224    $ch->attach($help_button, -pod => "^\\s*Hilfe-Symbol");
4225
4226    my $hpbmb = $misc_frame2->Menubutton;
4227    my $hpbm = $hpbmb->Menu(-title => M"Hilfe");
4228    $hpbm->checkbutton(-label => M"Legende",
4229		       -command => sub {
4230			   toggle_legend($top, -realcanvas => $c);
4231		       },
4232		       -variable => \$show_legend,
4233		       -accelerator => 'F1');
4234    my $this_index = $hpbm->index("last");
4235    $top->bind("<F1>" => sub { $hpbm->invoke($this_index) });
4236
4237    $hpbm->checkbutton(-label => M"Maushilfe",
4238		       -command => \&toggle_mouse_help,
4239		       -variable => \$show_mouse_help,
4240		      );
4241    if ($use_contexthelp) {
4242	$hpbm->command(-label => M"Kontexthilfe",
4243		       -command => sub { $ch->activate });
4244    }
4245    my $bbbike_html = Tk::findINC("doc/bbbike.html");
4246    my $url;
4247    if (defined $bbbike_html && -r $bbbike_html) {
4248	$url = "file:$bbbike_html";
4249	$hpbm->command
4250	  (-label => M"Dokumentation (lokal)",
4251	   -command => sub {
4252	       require WWWBrowser;
4253	       WWWBrowser::start_browser($url);
4254	   });
4255    }
4256    $hpbm->command
4257      (-label => M"Dokumentation (WWW)",
4258       -command => sub {
4259	   my $url = "$BBBike::BBBIKE_SF_WWW/bbbike/doc/bbbike.html";
4260	   require WWWBrowser;
4261	   WWWBrowser::start_browser($url);
4262       });
4263    if ($advanced) {
4264	$hpbm->cascade
4265	    (-label => M("Mehr Dokumentation")." ...");
4266	my $m2 = $hpbm->Menu(-title => M("Mehr Dokumentation")." ...");
4267	$hpbm->entryconfigure("last", -menu => $m2);
4268	for my $doc_def (["doc/links.pod", M"Linkliste"],
4269			 ["doc/watchsites.org", M"Watchsites"],
4270			 ["doc/qualitaetskategorien.html", M"Qualit�tskategorien"],
4271			 ["doc/HOWTO_edit_bbbike_data.html", M"Daten in BBBike editieren"],
4272			 ["doc/bbd.pod", M"Beschreibung des bbd-Formats"],
4273			 ["doc/tests.pod", M"Manuelle Testanweisung"],
4274			) {
4275	    my($file, $label) = @$doc_def;
4276	    my $full_path = $FindBin::RealBin . "/" . $file;
4277	    $m2->command
4278		(-label => $label,
4279		 -command => sub {
4280		     if ($file =~ m{\.pod$}) {
4281			 eval {
4282			     require Tk::Pod;
4283			 };
4284			 if ($@) {
4285			     perlmod_install_advice('Tk::Pod');
4286			 } else {
4287			     eval {
4288			         my $pod = $top->Pod(-file => $full_path,
4289						     -title => $label);
4290				 set_as_toolwindow($pod);
4291				 $toplevel{"pod-$label"} = $pod;
4292			     };
4293			     if ($@) {
4294				 status_message($@, "die");
4295			     }
4296			 }
4297		     } elsif ($file =~ m{\.org$}) {
4298			 require BBBikeAdvanced;
4299			 start_emacsclient("$FindBin::RealBin/$file");
4300		     } else {
4301			 require WWWBrowser;
4302			 my $url = "file:$full_path";
4303			 WWWBrowser::start_browser($url);
4304		     }
4305		 },
4306		);
4307	}
4308    }
4309    $hpbm->command(-label => M('�ber').' ...',
4310		   -command => sub { show_logo('as_about') });
4311    $hpbm->command(-label => M"Copyright",
4312		   -command => sub { copying_viewer($top) });
4313    $hpbm->command(-label => M"Changes",
4314		   -command => sub { simple_file_viewer
4315					 ($top,	"$FindBin::RealBin/CHANGES",
4316					  -title => M"Changes",
4317					  -class => "BBBike Changes",
4318					 );
4319				 });
4320    menuright($help_button, $hpbm);
4321    menuarrow($hpbmb, $hpbm, $col++, -menulabel => M"~Hilfe");
4322
4323    my $context_help_button;
4324    if (!$small_icons) {
4325	# The only reason for the restriction: the image on the button
4326	# is too large.
4327	$context_help_button =
4328	    $ch->HelpButton($misc_frame2)->grid
4329		(-row => $curr_row, -column => $col,
4330		 -rowspan => 2);
4331	$balloon->attach($context_help_button, -msg => M"Kontexthilfe");
4332	$col++;
4333    }
4334
4335    if (!$standard_menubar) {
4336	# No need for yet another close button if there's already a
4337	# standard menu:
4338
4339	$misc_frame2->Label(-text => ' ')->grid(-row => $curr_row,
4340						-column => $col++);
4341
4342	my $exit_photo = load_photo($misc_frame2, 'exit');
4343	my $exit_button = $misc_frame2->Button
4344	    (image_or_text($exit_photo, 'Exit'),
4345	     -command => \&exit_app,
4346	    )->grid(-row => $curr_row, -column => $col, -sticky => 's');
4347	$balloon->attach($exit_button, -msg => M"BBBike beenden");
4348	$ch->attach($exit_button, -pod => "^\\s*Ende-Symbol");
4349	$col++;
4350    }
4351
4352## DEBUG_BEGIN
4353#mymstat("before iconframe: underline all");
4354## DEBUG_END
4355    if ($misc_frame->can('UnderlineAll'))  { $misc_frame->UnderlineAll }
4356    if ($misc_frame2->can('UnderlineAll')) { $misc_frame2->UnderlineAll }
4357
4358    arrange_symframe();
4359
4360#XXX del: (now in "Aktuelle Route")
4361#    $ampelstatus_label = $sym_frame->Label(-justify => "left")->grid
4362#      (-row => 0, -column => 2, -sticky => 'n');
4363
4364## DEBUG_BEGIN
4365#mymstat("before iconframe: bindings");
4366## DEBUG_END
4367    bind_nomod($top, "<s>" => sub { $strasse_check->invoke}) if $strasse_check;
4368    bind_nomod($top, "<l>" => sub { $landstrasse_check->invoke }) if $landstrasse_check;
4369    bind_nomod($top, "<o>" => sub { $ort_check->invoke }) if $ort_check;
4370    bind_nomod($top, "<u>" => sub { $ubahn_check->invoke }) if $ubahn_check;
4371    bind_nomod($top, "<b>" => sub { $sbahn_check->invoke }) if $sbahn_check;
4372    bind_nomod($top, "<r>" => sub { $rbahn_check->invoke }) if $rbahn_check;
4373    bind_nomod($top, "<w>" => sub { $wasser_check->invoke }) if $wasser_check;
4374    bind_nomod($top, "<f>" => sub { $flaechen_check->invoke }) if $flaechen_check;
4375    bind_nomod($top, "<p>" => sub { $hs_check->invoke }) if $hs_check;
4376
4377    bind_nomod($top, "<R>" => sub {
4378		   # Same problems as in <Q>, see below.
4379		   if ($str_draw{'l'} || $str_draw{'comm-cyclepath'}) {
4380		       $lstrcm->invoke($radwege_l_check_index) if $lstrcm && defined $radwege_l_check_index;
4381		   }
4382		   if ($str_draw{'s'} || $str_draw{'rw'} || !$str_draw{'l'}) {
4383		       $strcm->invoke($radwege_check_index) if $strcm && defined $radwege_check_index;
4384		   }
4385	       });
4386    bind_nomod($top, "<a>" => sub { $strcm->invoke($ampeln_check_index) }) if $strcm && defined $ampeln_check_index;
4387    bind_nomod($top, "<g>" => sub { $strcm->invoke($sperre_check_index) }) if $strcm && defined $sperre_check_index;
4388    bind_nomod($top, "<Q>" => sub {
4389		   # XXX hmmm... nicht gerade ideal. Beispiel: Landstra�en
4390		   # sind aktiv, Q, Stra�en werden aktiv gemacht, Q
4391		   # togglet jetzt genau entgegengesetzt...
4392		   if ($str_draw{'l'} || $str_draw{'ql'}) {
4393		       $lstrcm->invoke($qualitaet_l_check_index) if $lstrcm && defined $qualitaet_l_check_index;
4394		   }
4395		   if ($str_draw{'s'} || $str_draw{'qs'} || !$str_draw{'l'}) {
4396		       $strcm->invoke($qualitaet_check_index) if $strcm && defined $qualitaet_check_index;
4397		   }
4398	       });
4399    bind_nomod($top, "<H>" => sub {
4400		   # XXX hmmm... nicht gerade ideal. Beispiel: Landstra�en
4401		   # sind aktiv, H, Stra�en werden aktiv gemacht, H
4402		   # togglet jetzt genau entgegengesetzt...
4403		   if ($str_draw{'l'} || $str_draw{'hl'}) {
4404		       $lstrcm->invoke($handicap_l_check_index) if $lstrcm && defined $handicap_l_check_index;
4405		   }
4406		   if ($str_draw{'s'} || $str_draw{'hs'} || !$str_draw{'l'}) {
4407		       $strcm->invoke($handicap_check_index) if $strcm && defined $handicap_check_index;
4408		   }
4409	       });
4410    bind_nomod($top, '<N>' => sub { $strcm->invoke($nolighting_check_index) })
4411	if defined $nolighting_check_index;
4412    bind_nomod($top, '<G>' => sub { $strcm->invoke($gruene_wege_check_index) })
4413	if defined $gruene_wege_check_index;
4414    bind_nomod($top, '<C>' => sub { $c_bpcm->invoke($comments_all_check_index) })
4415	if defined $comments_all_check_index;
4416    bind_nomod($top, '<V>' => sub { $strcm->invoke($vorfahrt_check_index) })
4417	if defined $vorfahrt_check_index;
4418    bind_nomod($top, "<question>" => sub {
4419	$strcm->invoke($fragezeichen_check_index)
4420    })
4421	if defined $fragezeichen_check_index;
4422    bind_nomod($top, "<Y>" => sub { $strcm->invoke($cycle_routes_check_index) }) if $strcm && defined $cycle_routes_check_index;
4423
4424    bind_nomod($top, "<L>" => sub { $lstrcm->invoke($land_jwd_check_index) }) if $lstrcm && defined $land_jwd_check_index;
4425    bind_nomod($top, "<O>" => sub { $ocm->invoke($ort_jwd_check_index) }) if $ocm && defined $ort_jwd_check_index;
4426    bind_nomod($top, "<W>" => sub { $wcm->invoke($wasserumland_check_index) }) if $wcm && defined $wasserumland_check_index;
4427    bind_nomod($top, "<B>" => sub { $strlist_button->invoke });
4428
4429    # XXX restliche Widgets fehlen noch
4430    for my $w ($strasse_check, $landstrasse_check, $ort_check,
4431	       $ubahn_check, $sbahn_check, $rbahn_check, $wasser_check,
4432	       $flaechen_check) {
4433	next if !$w;
4434	enter_leave_bind_for_help($w, [M"Option umschalten", '', M"Men�"]);
4435    }
4436
4437} # do_iconframe
4438
4439$splash_screen->Update(0.3, 'load photos') if $splash_screen;
4440
4441##### sonstige Bilder #####
4442## DEBUG_BEGIN
4443#mymstat("before load photos");
4444## DEBUG_END
4445load_photos();
4446
4447my $linestip = eval { Tk::findINC('images/stip.xbm') };
4448
4449##### configure Canvas/Scrollbars #####
4450## DEBUG_BEGIN
4451#mymstat("create/config canvas");
4452## DEBUG_END
4453my $canvas_frame = $frame->Frame->pack(-fill => 'both', -expand => 1);
4454$canvas_frame->gridColumnconfigure(0, -weight => 1);
4455$canvas_frame->gridRowconfigure(0, -weight => 1);
4456
4457$c = $canvas_frame->Canvas
4458  (Name => 'karte',
4459   -bg => $map_bg,
4460   -closeenough => 3, # XXX hmmm ... manchmal gut, manchmal schlect
4461   -scrollregion => \@scrollregion,
4462   #-xscrollincrement => 4, -yscrollincrement => 4,
4463  )->grid(-row => 0, -column => 0, -sticky => 'eswn');
4464$top->Advertise(Map => $c);
4465$c->{Configure}{-seeview} = \&Tk::Canvas::smooth_scroll;
4466#XXX$c->BindMouseWheel if defined &Tk::Widget::BindMouseWheel;
4467{
4468    # Re-shuffle bindtags: the "Tk::Canvas" tag is moved from 1st to
4469    # 2nd position. A better solution would be to use a separate class
4470    # for the map canvas.
4471    my @c_bindtags = $c->bindtags;
4472    @c_bindtags = @c_bindtags[1,0,2..$#c_bindtags];
4473    $c->bindtags([@c_bindtags]);
4474}
4475
4476$sy = $canvas_frame->Scrollbar(-command => ["yview", $c],
4477			       -takefocus => 0,
4478			       -highlightthickness => 0,
4479			      );
4480$sx = $canvas_frame->Scrollbar(-orient => "horiz",
4481			       -command => ["xview", $c],
4482			       -takefocus => 0,
4483			       -highlightthickness => 0,
4484			      );
4485
4486$c->configure(-yscrollcommand =>
4487	      sub { $sy->set(@_);
4488		    overview_update();
4489		    if (defined &plotstr_on_demand
4490			and $BBBikeLazy::mode) {
4491			my($x1,$y1,$x2,$y2) = $c->get_corners;
4492			plotstr_on_demand(anti_transpose($x1,$y1),
4493					  anti_transpose($x2,$y2));
4494		    }
4495		    $c_balloon->Deactivate(1) if defined $c_balloon;
4496	      },
4497              -xscrollcommand =>
4498              sub { $sx->set(@_);
4499		    overview_update();
4500		    if (defined &plotstr_on_demand
4501			and $BBBikeLazy::mode) {
4502			my($x1,$y1,$x2,$y2) = $c->get_corners;
4503			plotstr_on_demand(anti_transpose($x1,$y1),
4504					  anti_transpose($x2,$y2));
4505		    }
4506		    $c_balloon->Deactivate(1) if defined $c_balloon;
4507		},
4508	     );
4509
4510## XXX Enable after some rethaught...
4511## XXX and remove the scrollregion code from scalecanvas
4512# for my $hook (qw(after_plot after_resize)) {
4513#     Hooks::get_hooks($hook)->add
4514# 	    (sub {
4515# 		 # XXX Is this fast enough?
4516# 		 $c->configure(-scrollregion => [ $c->bbox("all") ]);
4517# 	     }, "bbbike-scrollregion");
4518#     $c->OnDestroy
4519# 	(sub {
4520# 	     Hooks::get_hooks($hook)->del("bbbike-scrollregion");
4521# 	 });
4522# }
4523
4524# Additional MouseWheel bindings
4525$c->Tk::bind("<4>" => [sub { return if $_[1] ne "" && $_[1] ne "B4-";
4526			     $c->yviewScroll(-1,"units") },
4527		       Tk::Ev('s')]);
4528$c->Tk::bind("<5>" => [sub { return if $_[1] ne "" && $_[1] ne "B5-";
4529			     $c->yviewScroll(+1,"units") },
4530		       Tk::Ev('s')]);
4531for ("<Shift-5>", "<B1-5>") {
4532    $c->Tk::bind($_ => sub { $c->xviewScroll(+1,"units") });
4533}
4534for ("<Shift-4>", "<B1-4>") {
4535    $c->Tk::bind($_ => sub { $c->xviewScroll(-1,"units") });
4536}
4537$c->Tk::bind('<Control-4>' => sub { scalecanvas_from_canvas_event($c, 2); Tk->break; });
4538$c->Tk::bind('<Control-5>' => sub { scalecanvas_from_canvas_event($c, 0.5); Tk->break; });
4539
4540if ($c->can('DropSite')) {
4541    eval {
4542	$c->DropSite
4543	  (-dropcommand => [\&accept_drop, $c],
4544	   -droptypes => ($os eq 'win' ?
4545			  'Win32' :
4546			  # KDE is removed from Tk804.02x
4547			  [($Tk::VERSION >= 804 ? () : 'KDE'), 'XDND', 'Sun']
4548			 )
4549	  );
4550	print STDERR M("Datei-DND wird akzeptiert") . "\n" if $verbose;
4551    };
4552    warn __LINE__ . ": $@" if $@ && $verbose;
4553}
4554
4555# erst hier setzen, weil die Hintergrundfarbe von -xrm und dem Window-System
4556# abh�ngt
4557$category_color{'I'} = $c->cget(-background);
4558
4559standard_selection_handle();
4560
4561$sy->grid(-row => 0, -column => 1, -sticky => 'ns');
4562$sx->grid(-row => 1, -column => 0, -sticky => 'ew');
4563
4564##### Statuszeile/Progress Bar #####
4565{
4566    my $status_frame = $frame->Frame(-height => 16)->pack(-fill => 'x');
4567    # XXX hmmm, das kriege ich nicht so gut hin....
4568    $status_frame->gridColumnconfigure(0, -weight => 1);
4569    $status_frame->gridColumnconfigure(1, -weight => 5);
4570    $status_frame->gridColumnconfigure(2, -weight => 0);
4571    $status_frame->gridColumnconfigure(3, -weight => 0);
4572    my $gridx = 0;
4573
4574    require Tk::SRTProgress;
4575    Tk::SRTProgress->VERSION(0.06);
4576    $progress = $status_frame->SRTProgress
4577	(-relief => 'sunken',
4578	 -borderwidth => 2,
4579	 -visible => 0,
4580	 -width => $top->width/10,
4581	 -labelfont => $font{'reduced'},
4582	)->grid(-row => 0,
4583		-column => $gridx++,
4584		-sticky => 'ew');
4585    $status_label = $status_frame->Label(-justify => 'left', -anchor => 'w')
4586      ->grid(-row => 0, -column => $gridx++, -sticky => 'ew');
4587
4588    $status_button_column = $gridx;
4589    $status_button = $status_frame->Button(-padx => 0, -pady => 0); $gridx++; # do not map
4590
4591    $indicator_frame = $status_frame->Frame
4592	->grid(-row => 0, -column => $gridx++, -sticky => "ew");
4593    if ($advanced) {
4594	$edit_mode_type = $indicator_frame->Label
4595	    (-text => '', -relief => 'sunken')
4596		->pack(-side => "left");
4597	$edit_mode_indicator = $indicator_frame->$Checkbutton
4598	    (-text => 'EDIT',
4599	     -variable => \$edit_mode_flag,
4600	     -command => sub {
4601		 set_edit_mode();
4602	     })->pack(-side => "left");
4603	gui_set_edit_mode($edit_mode);
4604    }
4605    $balloon->configure(-statusbar => $status_label);
4606}
4607
4608use constant UPDATE_FRAC_BEFORE_PLOTTING => 0.4;
4609use constant UPDATE_FRAC_AFTER_PLOTTING => 0.7;
4610
4611$splash_screen->Update(UPDATE_FRAC_BEFORE_PLOTTING, 'start plotting') if $splash_screen;
4612
4613##### initiales Zeichnen ######################################
4614## DEBUG_BEGIN
4615#BEGIN{mymstat("before init draw BEGIN");} mymstat("before init draw");
4616## DEBUG_END
4617$progress->InitGroup;
4618
4619######################################################################
4620# Custom Cursors
4621# Load it before possible use, e.g. in set_edit_mode
4622foreach my $def (qw(start watch ziel addnet delnet info salesman xy
4623		    movehand www)) {
4624    load_cursor($def);
4625}
4626if ($cursor{"watch"}) {
4627    $busy_watch_args{-cursor} = ['@' . $cursor{"watch"}, $cursor_mask{"watch"},
4628				 'black', 'white'];
4629}
4630######################################################################
4631
4632# Read as early as possible; to prevent inconsinstencies especially in lazy mode
4633read_ampeln() unless $lowmem;
4634
4635if (defined $set_mode && $set_mode eq 'edit') {
4636    require BBBikeAdvanced;
4637    set_edit_mode(1);
4638    $init_p_draw{pp} = 1;
4639}
4640# XXX hack: if any of $wasserstadt/umland/... is set, then
4641# $init_str_draw{w} should also be set
4642if ($wasserstadt || $wasserumland || $str_far_away{w}) {
4643    $init_str_draw{w} = 1;
4644}
4645my $_update_steps = ((scalar keys %init_str_draw) +
4646		     (scalar keys %init_p_draw));
4647my $_update_i = 0;
4648foreach (keys %init_str_draw) {
4649    $str_draw{$_} = $init_str_draw{$_};
4650    eval {
4651	plot('str',$_)   if $str_draw{$_};   # Strecken plotten
4652    };
4653    if ($@ && !$no_original_datadir) {
4654	die $@;
4655    }
4656    $splash_screen->Update((($_update_i++)/$_update_steps)*(UPDATE_FRAC_AFTER_PLOTTING - UPDATE_FRAC_BEFORE_PLOTTING)+UPDATE_FRAC_BEFORE_PLOTTING, "plot str $_")
4657	if $splash_screen;
4658}
4659foreach (keys %init_p_draw) {
4660    $p_draw{$_} = $init_p_draw{$_};
4661    eval {
4662	plot('p',$_)     if $p_draw{$_};     # Punkte (z.B. Ampeln) zeichnen
4663    };
4664    if ($@ && !$no_original_datadir) {
4665	die $@;
4666    }
4667    $splash_screen->Update((($_update_i++)/$_update_steps)*(UPDATE_FRAC_AFTER_PLOTTING - UPDATE_FRAC_BEFORE_PLOTTING)+UPDATE_FRAC_BEFORE_PLOTTING, "plot p $_")
4668	if $splash_screen;
4669}
4670# H�hen einlesen
4671read_hoehe()  if $show_grade || $steigung_optimierung || $use_hoehe;
4672read_sperre_tragen() unless $lowmem;
4673plot_sperre() if $p_draw{'sperre'};
4674activate_temp_blockings(1) if $do_activate_temp_blockings;
4675
4676if ($net_type =~ /^(us|r|rus|wr)$/) {
4677    make_net();
4678}
4679
4680if (!$search_route_flag && !(defined $set_mode && $set_mode eq 'edit')) {
4681    search_route_mouse(1);
4682}
4683
4684## DEBUG_BEGIN
4685#BEGIN{mymstat("after init draw BEGIN");} mymstat("after init draw");
4686## DEBUG_END
4687$progress->FinishGroup;
4688
4689$splash_screen->Update(UPDATE_FRAC_AFTER_PLOTTING, 'finished plotting') if $splash_screen;
4690
4691set_bindings();
4692
4693$splash_screen->Update(0.8, 'after plotting') if $splash_screen;
4694
4695$last_loaded_obj =
4696    {
4697     List => [],
4698     File => "$bbbike_configdir/last",
4699     Menu => $last_loaded_menu,
4700     Title => M('Letzte Routen-Dateien').':',
4701     Cb => sub { load_save_route(0, $_[0]) },
4702     Max => 12,
4703    };
4704load_last_loaded($last_loaded_obj);
4705
4706hide_logo();
4707if ($top->{initial_iconic}) {
4708    $top->iconify; # may be necessary to undo ->withdraw
4709} else {
4710    $top->deiconify;
4711}
4712
4713scrollregion_best();
4714
4715# XXX should be after deiconify, otherwise center does not work (?)
4716center_best();
4717
4718$splash_screen->Update(0.9, 'finalization') if $splash_screen;
4719
4720set_mouse_desc();
4721
4722if ($map_mode eq MM_SEARCH) {
4723    set_cursor("start");
4724}
4725
4726if ($preload_file) {
4727    load_save_route(0, $preload_file);
4728}
4729
4730if ($init_from) {
4731    set_route_start_street($init_from);
4732}
4733if ($init_to) {
4734    set_route_ziel_street($init_to);
4735}
4736
4737eval { local $SIG{'__DIE__'};
4738       require $progname . "_2.config" };
4739
4740if ($advanced) {
4741    # Besser w�re es, wenn mit "use" die aktuelle Zeit des Moduls
4742    # aufgezeichnet werden k�nnte. So beschr�nke ich mich auf
4743    # minutenweise �berpr�fen, ob neue Module geladen wurden.
4744    check_new_modules();
4745    $top->repeat(60*1000, \&check_new_modules);
4746}
4747
4748if ($stderr_window) {
4749    require BBBikeAdvanced;
4750    stderr_window_command();
4751}
4752
4753## DEBUG_BEGIN
4754#BEGIN{mymstat("before mainloop BEGIN");} mymstat("before mainloop");
4755## DEBUG_END
4756
4757#use Devel::Symdump;
4758#my $symdump = rnew Devel::Symdump;
4759#print $symdump->as_string;
4760
4761if ($use_server and $os ne 'win') { # Win32 unterst�tzt kein fork etc.
4762    require BBBikeServer;
4763    BBBikeServer::create_server($top);
4764}
4765
4766if ($turbo) {
4767    bbbikelazy_init();
4768}
4769
4770if (defined $initial_plugins && $initial_plugins ne "") {
4771    load_plugins([split /,/, $initial_plugins]);
4772}
4773
4774if (defined $initial_layers && $initial_layers ne "") {
4775    require BBBikeAdvanced;
4776    foreach my $layer_def (split /,/, $initial_layers) {
4777	plot_additional_layer_cmdline($layer_def);
4778    }
4779}
4780
4781if ($splash_screen) {
4782    $splash_screen->Update(1, 'destroying splash');
4783    $splash_screen->Destroy;
4784    undef $splash_screen;
4785}
4786
4787choose_streets()                            if $init_choose_street;
4788
4789if ($ENV{BBBIKE_GUI_TEST}) {
4790    eval qq{
4791      require $ENV{BBBIKE_GUI_TEST};
4792      \$top->afterIdle(\\&$ENV{BBBIKE_GUI_TEST}::start_guitest);
4793    };
4794    warn $@ if $@;
4795}
4796
4797if ($init_with_edittools) {
4798    require BBBikeEdit;
4799    BBBikeEdit::init_with_edittools();
4800}
4801
4802$booting = 0;
4803
4804# Call this after creating the main window, otherwise
4805# bbbike -xrm '*Desk:...'
4806# does not work.
4807$top->afterIdle(sub {
4808		    $top->command([$^X, $0]);
4809		});
4810
4811MainLoop unless $ENV{BBBIKE_TEST_PERFORMANCE};
4812
4813##### Subs ### RELOADER_START ############################################
4814
4815sub update_via_internet {
4816    if ($devel_host && $ENV{HOST} !~ /^devpc01/) {
4817	status_message("Kein Update auf biokovo/cabulja/vran/cvrsnica/spiff m�glich!", "die");
4818	die;
4819    }
4820    my $Dialog = LongOrNormalDialog();
4821    my $d = $top->$Dialog
4822	(-title => M"Update",
4823	 -text => M("Soll das Update gestartet werden?\nJe nach Internet-Verbindung und Stand der Daten kann das Update 5 bis 10 Minuten dauern. Alternativ k�nnen die Dateien als ZIP-Datei von\n$BBBike::BBBIKE_UPDATE_DATA_CGI\ngeholt und in das Verzeichnis\n$FindBin::RealBin/data\nausgepackt werden.\n"),
4824	 -bitmap => 'question',
4825	 -background => Tk::NORMAL_BG,
4826	 -highlightbackground => Tk::NORMAL_BG,
4827	 -buttons => [M"Ja", M"Nein"]);
4828    if ($Dialog eq 'LongDialog') {
4829	$d->configure(-height => 10);
4830    }
4831    if ($d->Show eq M"Ja") {
4832	require Update;
4833	Update::bbbike_data_update();
4834    }
4835}
4836
4837sub telefonbuch_dialog {
4838    my $type = shift;
4839    require Telefonbuch;
4840    my $get_coord = sub {
4841	my($x, $y) = @_;
4842	transpose($x, $y);
4843    };
4844    my $mark = sub {
4845	my($x, $y, %args) = @_;
4846	my $tcoords = [[]];
4847	$tcoords->[0][0] = [ transpose($x, $y) ];
4848	mark_point(-coords => $tcoords, %args,
4849		   -clever_center => 1);
4850    };
4851    if ($type eq 'str') {
4852	Telefonbuch::tk_str_dialog($top, $mark, $get_coord);
4853    } else {
4854	Telefonbuch::tk_tel_dialog($top, $mark, $get_coord);
4855    }
4856}
4857
4858# Berechnet das Layout des obersten Frames neu (z.B. bei einem Resize)
4859sub arrange_topframe {
4860    my(@order) = ($hslabel_frame, $km_frame, $speed_frame[0],
4861		  $power_frame[0], $wind_frame, $percent_frame, $temp_frame,
4862		  @speed_frame[1..$#speed_frame],
4863		  @power_frame[1..$#power_frame],
4864		 );
4865    my(@col)   = (0, 1, 3, 4+$#speed_frame, 5+$#speed_frame+$#power_frame,
4866		  2, 6+$#speed_frame+$#power_frame,
4867		  4..3+$#speed_frame,
4868		  5+$#speed_frame..4+$#speed_frame+$#power_frame);
4869    $top->idletasks;
4870    my $width = 0;
4871    my(%gridslaves) = map {($_, 1)} $top_frame->gridSlaves;
4872    for(my $i = 0; $i <= $#order; $i++) {
4873	my $w = $order[$i];
4874	next unless Tk::Exists($w);
4875	my $col = $col[$i] || 0;
4876	my $reqwidth = $w->reqwidth;
4877	# Special handling for Place/Street label: it shrinks as necessary.
4878	if ($w == $hslabel_frame && $reqwidth > $top->width/3) {
4879	    $reqwidth = $top->width/3;
4880	}
4881	$width += $reqwidth;
4882	if ($gridslaves{$w}) {
4883	    $w->gridForget;
4884	}
4885	if ($width <= $top->width) {
4886	    $w->grid(-row => 0,
4887		     -column => $col,
4888		     -sticky => 'nsew'); # XXX
4889	} elsif ($devel_host) { # XXX only for debugging, remove one day
4890	    require Data::Dumper;
4891	    warn "No space for widget\n" .
4892		Data::Dumper->new([$w->class, $w->PathName],[qw(class pathname)])->Indent(1)->Useqq(1)->Dump .
4893			" with i=$i, $width <= " . $top->width;
4894	}
4895    }
4896}
4897
4898# Berechnet das Layout des Symbol-Frames (das die Icons enth�lt) neu
4899sub arrange_symframe {
4900    my($old_row, $new_row);
4901    return unless $misc_frame2 || $DockFrame eq 'DockFrame';
4902    my $p = $misc_frame2->parent;
4903    if (grep($_ eq $misc_frame2, $p->gridSlaves)) {
4904	# already gridded
4905	my %a = $misc_frame2->gridInfo;
4906	$old_row = $a{-row};
4907    } else {
4908	# force computation of reqwidth
4909	$misc_frame2->idletasks;
4910    }
4911    my $new_col;
4912    my $is_two_row;
4913    if ($misc_frame->reqwidth + $misc_frame2->reqwidth + 10
4914	> $top->width) {
4915	$new_row = 1;
4916	$new_col = 0;
4917	$is_two_row = 1;
4918    } else {
4919	$new_row = 0;
4920	$new_col = 1;
4921	$is_two_row = 0;
4922    }
4923    if (!defined $old_row || $old_row != $new_row) {
4924	if (defined $old_row) {
4925	    $misc_frame2->gridForget;
4926	}
4927	$misc_frame2->grid(-row => $new_row,
4928			   -column => $new_col,
4929			   -sticky => 'nsw');
4930    }
4931
4932    # Maybe remove borders between two frames
4933    if ($os eq 'unix' && $devel_host) { # not tested yet on Windows XXX
4934	my $lf = $p->Subwidget("HideLeftBorder");
4935	my $lc = $p->Subwidget("HideLeftCorner");
4936	my $rf = $p->Subwidget("HideRightBorder");
4937	if (!$is_two_row) {
4938	    if (!Tk::Exists($rf)) {
4939		$rf = $misc_frame->Frame(-bg => $misc_frame->cget(-bg));
4940		$p->Advertise("HideRightBorder" => $rf);
4941	    }
4942	    if (!Tk::Exists($lf)) {
4943		$lf = $misc_frame2->Frame(-bg => $misc_frame->cget(-bg));
4944		$p->Advertise("HideLeftBorder" => $lf);
4945	    }
4946	    if (!Tk::Exists($lc)) {
4947		$lc = $misc_frame2->Frame
4948		    (-bd => 0, -bg => $misc_frame->Darken($misc_frame->cget(-bg), 60));
4949		$p->Advertise("HideLeftCorner" => $lc);
4950	    }
4951	    $lf->place(-rely => 0, -relx => 0, -x => -1,
4952		       -width => 1, -relheight => 1);
4953	    $lc->place(-rely => 1, -relx => 0, -x => -1,
4954		       -width => 1, -height => 1);
4955	    $rf->place(-rely => 0, -relx => 1,
4956		       -width => 1, -relheight => 1);
4957	} else {
4958	    for my $w ($rf, $lf, $lc) {
4959		$w->placeForget if Tk::Exists($w) && $w->manager eq 'place';
4960	    }
4961	}
4962    }
4963}
4964
4965sub handle_options {
4966    @opttable =
4967	(M"Strecken/Punkte",
4968	 ['','',M"Strecken und Punkte, die beim Start von BBBike\ngezeichnet werden sollen."],
4969	 ['str','!',1,	alias=>[qw(strasse strassen)],
4970	  label => M"Stra�en",	var => \$init_str_draw{'s'}],
4971	 ['landstr','!',0,	alias=>[qw(landstrasse landstrassen)],
4972	  label => M"Landstra�en",	var => \$init_str_draw{'l'}],
4973	 ['landstrjwd','!',0,
4974	  label => M"Landstra�en jwd", var => \$str_far_away{'l'}], # XXX init_str_far_away?
4975	 ['sbahn','!',1,
4976	  label => M"S-Bahnlinien",	var => \$init_str_draw{'b'}],
4977	 ['sbahnhof','!',1,
4978	  label => M"S-Bahnh�fe",	var => \$init_p_draw{'b'}],
4979	 ['ubahn','!',1,
4980	  label => M"U-Bahnlinien",	var => \$init_str_draw{'u'}],
4981	 ['ubahnhof','!',1,
4982	  label => M"U-Bahnh�fe",	var => \$init_p_draw{'u'}],
4983	 ['rbahn','!',0,
4984	  label => M"R-Bahnlinien",	var => \$init_str_draw{'r'}],
4985	 ['rbahnhof','!',0,
4986	  label => M"R-Bahnh�fe",	var => \$init_p_draw{'r'}],
4987	 ['wasser','!',1,	alias=>[qw(gewaesser)],
4988	  label => M"Gew�sser",	var =>\$init_str_draw{'w'}],
4989	 ['wasserstadt','!',1,
4990	  label => M"Gew�sser in der Stadt", var => \$wasserstadt],
4991	 ['wasserumland','!',0,
4992	  label => M"Gew�sser im Umland", var => \$wasserumland], # XXX auch init!
4993	 ['wasserjwd','!',0,
4994	  label => M"Gew�sser jwd", var => \$str_far_away{'w'}],
4995	 ['faehre','!',0,	alias=>[qw(faehren)],
4996	  label => M"F�hren",	var => \$init_str_draw{'e'}],
4997	 ['flaeche','!',1,	alias=>[qw(flaechen)],
4998	  label => M"Fl�chen",	var => \$init_str_draw{'f'}],
4999	 ['ort','!',0,	alias=>[qw(orte)],
5000	  label => M"Orte",	var => \$init_p_draw{'o'}],
5001	 ['ortsteil','!',0,	alias=>[qw(ortsteile)],
5002	  label => M"Ortsteile",var => \$init_str_draw{'gBO'}],
5003	 ['ortjwd','!',0,
5004	  label => M"Orte jwd",	var => \$p_far_away{'o'}],
5005	 ['sehenswuerdigkeiten','!',0,
5006	  label => M"Sehensw�rdigkeiten", var => \$init_str_draw{'v'}],
5007	 ['cyclepath', '!',0,	alias => [qw(radweg radwege)],
5008	  label => M"Radwege",	var => \$init_str_draw{'rw'}],
5009	 ['cycleroute', '!',0,	alias => [qw(radroute radrouten)],
5010	  label => M"Radrouten", var => \$init_str_draw{'comm-route'}],
5011	 ['greenway', '!',0,	alias => [qw(gruenerweg gruenewege)],
5012	  label => M"Gr�ne Wege", var => \$init_str_draw{'gr'}],
5013	 ['ampel','!',1,	alias=>[qw(ampeln|lsa)],
5014	  label => M"Ampeln zeichnen", var => \$init_p_draw{'lsa'}],
5015	 ['fragezeichen','!',0,
5016	  label => M"Fragezeichen",	var => \$init_str_draw{'fz'}],
5017
5018	 M"Plot-Attribute",
5019	 ['outline','!',0,
5020	  label => M"Outline zeichnen", var => \$all_outline],
5021	 ['lsamaybe','!',undef, nogui => 1, # XXX remove this option???
5022	  label => M"unsichere Ampeln", var => sub { $str_restrict{'lsa'} = {qw(? 1 X 0 B 0 F 0)} }],
5023	 ['plothoehe','!',0,
5024	  label => M"H�henangaben zeichnen",	var => \$init_p_draw{'hoehe'}],
5025	 ['showgrade','!',1,
5026	  label => M"Anzeige der Steigungen/Gef�lle", var => \$show_grade],
5027	 ['grademinimum','=f',0.01, # ab 1% Steigungen/Gef�lle zeigen
5028	  label => M"minimal angezeigte Steigung",	var => \$grade_minimum],
5029	 ['grademinimumshort','=f',0.02, # kurze St�cke erst ab 2% zeigen
5030	  label => Mfmt("minimale Steigung (kurze Strecken bis %dm)", $grade_minimum_short_length),	var => \$grade_minimum_short],
5031	 ['strname','!',0,
5032	  label => M"Stra�ennamen plotten",	var => \$str_name_draw{'s'}],
5033	 ['ubahnname','!',1,
5034	  label => M"Namen von U-Bahnh�fen anzeigen", var => \$p_name_draw{'u'}],
5035	 ['sbahnname','!',1,
5036	  label => M"Namen von S-Bahnh�fen anzeigen", var => \$p_name_draw{'b'}],
5037	 ['ortname','!',1,
5038	  label => M"Ortsnamen plotten",	var => \$p_name_draw{'o'}],
5039	 ['ortkategorie','=s','auto',
5040	  label => M"Ortskategorie",
5041	  longhelp => M"Minimale Ortskategorie, die gezeichnet werden soll",
5042	  choices => [qw(auto), MIN_ORT_CAT .. MAX_ORT_CAT],
5043	  var =>  \$place_category],
5044	 ['wassername','!',1,		alias => [qw(gewaessername)],
5045	  label => M"Gew�ssernamen plotten",	var => \$str_name_draw{'w'}],
5046	 ['rbahnnetz','!',undef, nogui => 1,
5047	  label => M"R-Bahnnetz",	var => sub { $net_type = "r" }],
5048	 ['usbahnetz','!',undef, nogui => 1,
5049	  label => M"U/S-Bahnnetz",	var => sub { $net_type = "us" }],
5050	 ['bahnnetz','!',undef, nogui => 1,
5051	  label => M"Gesamtes Bahnnetz", var => sub { $net_type = "rus" }],
5052	 ['scope','=s',undef,
5053	  label => M"Scope", var => \$init_scope,
5054	  choices => ["", qw/city region jwd/]],
5055	 ['fast','!',undef,	nogui => 1, var => \&fast_settings],
5056	 ['turbo','!',undef,  nogui => 1, var => sub { fast_settings();
5057						       $turbo = 1;
5058						   },
5059	 ],
5060	 #XXX -nolazy geht nicht!
5061	 ['lazy','!',undef,   nogui => 1, var => sub {
5062	      $lazy_plot = 1;
5063	      #        $p_far_away{'o'}   = 1;
5064	      #        $str_far_away{'w'} = 1;
5065	      #        $str_far_away{'l'} = 1;
5066	      #        $wasserumland      = 1;
5067	      #        $str_draw{'l'}     = $str_draw{'s'};
5068	      #        $p_draw{'o'}       = 1;
5069	  }],
5070	 ['lowmem','!',undef, nogui => 1, var => sub {
5071	      fast_settings();
5072	      $lowmem = 1;
5073	      $use_contexthelp = 0;
5074	      $use_balloon = 0;
5075	      $use_c_balloon = 0;
5076	      $want_wind = 0;
5077	      $bikepwr = 0;
5078	      @speed = (20);
5079	      $init_p_draw{'lsa'} = 0;
5080	      $map_color = 'pixmap';
5081	      $show_grade = 0;
5082	      $use_hoehe = 0;
5083	  }],
5084	 ['slowcpu','!',undef, nogui => 1, var => sub {
5085	      $slowcpu = 1;
5086	      # XXX more
5087	  }],
5088	 ['center','=s',undef,
5089	  label => M"Beim Starten auf Stra�e zentrieren", var => \$center_on_str],
5090	 ['centerc','=s',undef,
5091	  label => M"Beim Starten auf Koordinaten zentrieren",
5092	  widget => sub {
5093	      my($self, $frame, $opt) = @_;
5094	      my $vref = $self->varref($opt);
5095	      my $f2 = $frame->Frame;
5096	      $f2->Entry(-textvariable => $vref)->pack(-side => "left");
5097	      $f2->Button(-text => M"Aktueller Kartenausschnitt",
5098			  -command => sub {
5099			      my(@corner) = $c->get_corners;
5100			      my $c_w = ($corner[2]-$corner[0]);
5101			      my $c_h = ($corner[3]-$corner[1]);
5102			      $$vref = join ",", map { int } anti_transpose($corner[0]+$c_w/2, $corner[1]+$c_h/2);
5103			  })->pack(-side => "left");
5104	      $f2;
5105	  },
5106	  var => \$center_on_coord],
5107	 ['center2c','=s',undef, # XXX currently not really used, but some day may be used together with center_view2
5108	  nogui => 1,
5109	  var => \$center_on_coord2],
5110	 ['choosestreet','!',1,
5111	  label => M"Beim Starten Stra�enauswahl zeigen",
5112	  var => \$init_choose_street],
5113	 ['autoshowlist','!',1,
5114	  label => M"Automatisches Anzeigen der Beschreibung",
5115	  var => \$auto_show_list],
5116	 ['city','=s',undef,
5117	  label => M"Stadt", var => \$city, nosave => 1],
5118	 ['country','=s',undef,
5119	  label => M"Land", var => \$country, nosave => 1],
5120	 ['datadir','=s',undef,
5121	  label => M"Verzeichnis mit Stra�endaten",
5122	  subtype => 'dir', nosave => 1, var => \$datadir],
5123
5124	 M"Anzeige",
5125	 ['','',M"Bei den meisten Optionen muss BBBike neu gestartet werden,\num die �nderungen sichtbar zu machen."],
5126	 ['fontrot','!',1,
5127	  label => M"Rotierte Zeichens�tze", var => \$use_font_rot],
5128	 ['fontfamily','=s',undef, #'helvetica',#XXX no defaults!
5129	  label => M"Zeichensatz (Proportional)", var =>        \$font_family],
5130	 ['fixedfontfamily','=s','courier',
5131	  label => M"Zeichensatz (Fixed)", var =>   \$fixed_font_family],
5132	 ['fontheight','=i',undef, #12,#XXX no defaults!
5133	  alias => [qw(fontsize)],
5134	  label => M"Zeichensatzgr��e", var => \$font_size,
5135	  longhelp => M"Negative Gr��en sind in Pixeln, positive in Points",
5136	 ],
5137	 ['labelfontheight','=i',10,
5138	  alias => [qw(labelfontsize)],
5139	  label => M"Zeichensatzgr��e f�r Labels", var => \$label_font_size,
5140	  longhelp => M"Negative Gr��en sind in Pixeln, positive in Points",
5141	 ],
5142	 ['fontweight','=s',undef,
5143	  label => M"Zeichensatzform", var => \$font_weight],
5144	 ['geometry','=s',undef,
5145	  subtype => "geometry", # XXX use fix_geometry for tk::getopt editor
5146	  label => M"Geometry", var => \$geometry],
5147	 ['maximized','!',0,
5148	  label => M"immer maximiert �ffnen", var => \$open_maximized],
5149	 ['scaling','=f',undef, nogui => 1,
5150	  label => M"X11-Skalierung", var => \$scaling],
5151	 ['visual','=s',undef, nogui => 1,
5152	  label => M"Visual", var => \$visual],
5153	 ['scale','=s',undef,
5154	  label => M"Skalierung", nogui => 1,
5155	  var => \$init_scale_massstab,
5156	 ],
5157	 ['coloring','=s','red',
5158	  label => M"Einf�rben der Route", var => \$coloring,
5159	  choices => [qw(red blue black power wind)]],
5160	 ['', '', '-'],
5161	 ['overviewwasser','!',1,
5162	  label => M"�bersichtskarte mit Gew�ssern", var => \$overview_draw{'w'}],
5163	 ['overviewsbahn','!',0,
5164	  label => M"�bersichtskarte mit S-Bahnen", var => \$overview_draw{'b'}],
5165	 ['overviewrbahn','!',0,
5166	  label => M"�bersichtskarte mit Regionalbahnen", var => \$overview_draw{'r'}],
5167	 ['overviewstr','!',0,
5168	  label => M"�bersichtskarte mit Hauptstra�en", var => \$overview_draw{'s'}],
5169
5170	 M"GUI",
5171	 ['menu','!',1,	# XXX hier stand mal "menu|stdmenu|standardmenu" => aber Aliase werden anscheinend von Tk::GetOpt nicht unterst�tzt?!
5172	  label => M"Standard-Men�", var => \$standard_menubar,
5173	 'callback-interactive' => \&restart_bbbike_hint,
5174	 ],
5175	 ['balloon','!',1,
5176	  label => M"Balloons", var => \$use_balloon,
5177	 'callback-interactive' => \&restart_bbbike_hint,
5178	 ],
5179	 ['cballoon','=i',2,    # 0 = nie, 1 = auf der Route, 2 = immer
5180	  strict => 1,
5181	  choices => [[M"nie" => 0],
5182		      [M"nur auf der Route" => 1],
5183		      [M"�berall" => 2],
5184		     ],
5185	  callback => \&c_balloon_update,
5186	  label => M"Canvas balloons", var => \$use_c_balloon],
5187	 ['cballoonwait','=i',350,
5188	  label => M"Wartezeit f�r Canvas balloons", var => \$c_balloon_wait],
5189	 ['flat','!',1,
5190	  label => M"Flaches Relief", var => \$flat_relief],
5191	 ['contexthelp','!',1,
5192	  label => M"Kontextsensitive Hilfe", var => \$use_contexthelp],
5193	 ['rightispopup','!',1,
5194	  label => M"Popup-Men� rechts", var => \$right_is_popup],
5195	 ['smoothscroll','!',0,
5196	  label => M"Weiches Scrollen", var => \$use_smooth_scroll],
5197	 ['followmouse','!',0,
5198	  label => M"Kartenausschnitt folgt Cursor", var => \$followmouse],
5199	 ['dialog','!',1,
5200	  label => M"Verwendung von Dialog-Fenstern", var => \$use_dialog],
5201	 ['transient','!',1,
5202	  label => M"Transiente Fenster", var => \$transient,
5203	  longhelp => M('Verwendung von transienten Fenster oder "Toolwindows"')],
5204	 ($os eq 'unix' ?
5205	  ['pathentrydialog','!',undef, nogui => 1,
5206	   label => M"Alternative Dateiauswahl verwenden",
5207	   var => sub {
5208	       if (1) {	# XXX determine current value --- Tk::GetOpt update necessary
5209		   eval 'use Tk::PathEntry::Dialog qw(as_default)';
5210	       } else {
5211		   eval 'use Tk::FBox qw(as_default)';
5212	       }
5213	       warn $@ if $@;
5214	   },
5215	  ] : ()),		# do not change dialog on Windows
5216	 ['askquit','!',1,
5217	  label => M"vor Beenden fragen", var => \$ask_quit],
5218	 ['b2mode','=i',B2M_FASTSCAN, nogui => 1,
5219	  var => \$b2_mode],
5220	 ['autoscroll','!',undef, # XXX make nogui => 0, choices!
5221	  label => M"Autoscrolling", nogui => 1, var => sub { $b2_mode = B2M_AUTOSCROLL }],
5222	 ['autoscrollspeed','=s','normal',
5223	  choices => [qw(slow normal fast)],
5224	  label => M"Autoscrolling-Geschwindigkeit", var =>   \$autoscroll_speed],
5225	 ['autoscrollmiddle','!',undef,
5226	  label => M"Autoscrollpunkt in der Mitte", var =>   \$autoscroll_middle],
5227	 ['focuspolicy','=s',undef,
5228	  label => M"Focus-Policy",
5229	  longhelp => 'click:'.M("Click-to-focus")."\n".
5230	  'follow:'.M("Focus-follows-mouse")."\n",
5231	  var => \$focus_policy,
5232	  choices => [qw(click follow)],
5233	 ],
5234
5235	 M"Suchoptionen",
5236	 ['qualitaetoptimierung','!',0,
5237	  label => M"Stra�enqualit�t beachten", var => \$qualitaet_s_optimierung],
5238	 ['qualitaetwerte','!',{Q0 => 100,
5239				Q1 => 25,
5240				Q2 => 18,
5241				Q3 => 13},
5242	  label => M"Stra�enqualit�t konfigurieren", var => \%qualitaet_s_speed,
5243	  nogui => 1],		# XXX Tk::Getopt can't handle this yet
5244	 ['kategorieoptimierung','!',0,
5245	  label => M"Stra�enkategorien beachten", var => \$strcat_optimierung],
5246	 ['kategoriewerte','!',{B  => 100,
5247				HH => 100,
5248				#BAB => 100,
5249				H  => 100,
5250				NH => 100,
5251				N  => 100,
5252				NN => 100},
5253	  label => M"Stra�enkategorien konfigurieren", var => \%strcat_speed,
5254	  nogui => 1],		# XXX Tk::Getopt can't handle this yet
5255	 ['radwegeoptimierung','!',0, var => \$radwege_optimierung,
5256	  label => M"Radwege-Optimierung"],
5257	 ['N_RW_optimization', '!', 0, var => \$N_RW_optimization, nogui => 1],#XXX N_RW vs. N_RW1 missing!
5258	 ['tram_optimization', '!', 0, var => \$tram_optimization, nogui => 1],
5259	 ['greenoptimierung', '=i', 0, choices => [0,1,2],
5260	  longhelp => "0: ".M("egal")."\n".
5261	  "1: ".M("bevorzugen")."\n".
5262	  "2: ".M("stark bevorzugen")."\n",
5263	  label => M"Gr�ne Wege bevorzugen", var => \$green_optimization,
5264	 ],
5265	 ['unbeleuchtetoptimierung', '!', 0, var => \$unlit_streets_optimization,
5266	  label => M"Unbeleuchtete Stra�en meiden"],
5267	 ['steigungoptimierung', '!', 0, var => \$steigung_optimierung,
5268	  label => M"Steigungsoptimierung"],
5269	 ['handicapoptimierung','!',0,
5270	  label => M"Sonstige Beeintr�chtigungen beachten", var => \$handicap_s_optimierung],
5271	 ['handicapwerte','!',{q0 => 100,
5272			       q1 => 25,
5273			       q2 => 18,
5274			       q3 => 13,
5275			       q4 => 5, # z.B. Fu�g�ngerzonen
5276			      },
5277	  label => M"Sonstige Beeintr�chtigungen konfigurieren", var => \%handicap_s_speed,
5278	  nogui => 1],		# XXX Tk::Getopt can't handle this yet
5279	 ['sperre','!',undef,		alias => [qw(gesperrt)],
5280	  label => M"Gesperrte Stra�en beachten", nogui => 1,
5281	  var => sub {
5282	      $sperre{'einbahn'} = $sperre{'sperre'} = $sperre{'wegfuehrung'} = 1;
5283	  },
5284	  savevar => \$sperre{'einbahn'},
5285	 ],
5286	 ['einbahn-strict','!',undef,
5287	  label => M"Alle Einbahnstra�en *strikt* beachten", nogui => 1,
5288	  var => sub {
5289	      $sperre{'einbahn-strict'} = 1;
5290	  },
5291	  savevar => \$sperre{'einbahn-strict'},
5292	 ],
5293	 ['nichttragen','!',0,
5294	  label => M"Tragen strikt vermeiden", var => \$sperre{'tragen'}],
5295	 ['tempblockings','!',1,
5296	  label => M"Aktuelle Sperrungen verwenden", var => \$do_activate_temp_blockings],
5297	 ['ampeloptimierung','!',0,
5298	  label => M"Ampeloptimierung verwenden", var => \$ampel_optimierung],
5299	 ['beschleunigung','=f',1,
5300	  label => M"Beschleunigung (m/s^2)", var => \$beschleunigung],
5301	 ['wind','!',1,
5302	  label => M"Windgeschwindigkeit beachten", var => \$want_wind],
5303	 ['faehre','!',0,
5304	  label => M"F�hren verwenden", var => \$use_faehre],
5305	 ## Without bikepower things like Steigungsoptimierung do not work anymore
5306	 #['bikepwr','!',1,	alias => [qw(bikepower)], label => M"Bikepower verwenden", var => \$bikepwr],
5307	 ['resetpower','!',undef, nogui => 1, var => sub { @power = () }],
5308	 ['power','=i@',undef, nogui => 1, var => \@power], # XXX gui => 1
5309	 ['resetspeed','!',undef, nogui => 1, var => sub { @speed = () }],
5310	 ['speed','=i@',[qw(15 20)], nogui => 1, var => => \@speed], # XXX gui => 1
5311	 ['speedpowerreference','=s',undef, nogui => 1, var => \$speed_power_reference_string],
5312	 ['from','=s',undef, nogui => 1, -var => \$init_from],
5313	 ['to','=s',undef, nogui => 1, -var => \$init_to],
5314
5315	 M"WWW",
5316	 ['www','!',0, # 1, wenn Wetterdaten vom Web geholt werden sollen
5317	  label => M"WWW verwenden", var => \$do_www],
5318	 (0&&$devel_host ?
5319	  (
5320	   ['wwwmap','!',undef,
5321	    label => M"Karten �bers WWW holen", var => \$do_wwwmap],
5322	   ['wwwcache','!',0,
5323	    label => M"Cache f�r WWW-Karten verwenden", var => \$use_wwwcache],
5324	  ) : ()
5325	 ),
5326	 ['proxy','=s', undef,
5327	  label => M"HTTP-Proxy (Format: http://host:port/)", var => \$proxy],
5328	 ['cachedir','=s',undef,
5329	  label => M"Cacheverzeichnis", subtype => 'dir',
5330	  var => \$cache_root],
5331
5332	 M"GPS",
5333	 ['exporttxtmode','=i',EXPORT_TXT_SIMPLIFY_AUTO,
5334	  label => M"Vereinfachung von Routen",
5335	  longhelp => M"GPS-Ger�te k�nnen nur eine begrenzte Anzahl von Waypoints pro Route verwenden.
5336Eine von BBBike berechnete Route erzeugt meist mehr Waypoints.
5337Mit dieser Option kann eingestellt werden, welche Strategie
5338dazu verwendet wird",
5339	  choices => [[M("Komplette Route"), EXPORT_TXT_FULL],
5340		      [M("Unterschiedliche Stra�ennamen"), EXPORT_TXT_SIMPLIFY_NAME],
5341		      [M("Abbiegevorg�nge"), EXPORT_TXT_SIMPLIFY_ANGLE],
5342		      [M("Abbiegevorg�nge/unterschiedliche Stra�ennamen"), EXPORT_TXT_SIMPLIFY_NAME_OR_ANGLE],
5343		      [M("automatisch"), EXPORT_TXT_SIMPLIFY_AUTO],
5344		     ],
5345	  strict => 1,
5346	  var =>  \$export_txt_mode],
5347	 ['exporttxtminangle','=s',30,
5348	  choices => [5,15,30,45,60],
5349	  label => M"Minimalwinkel bei Route-Vereinfachung",
5350	  longhelp => M"Minimalwinkel in Grad bei der Vereinfachung von Routen\n",
5351	  var => \$export_txt_min_angle],
5352	 ['gpswaypoints','=i',50,
5353	  choices => [20,50,100,250],
5354	  label => M"Maximale Anzahl der GPS-Waypoints",
5355	  longhelp => M"Moderne Garmin-Ger�te wie der eTrex Vista HCx k�nnen 250 Waypoints pro Route verwenden,\netwas �ltere wie der eTrex Vista 50 Waypoints,\nw�hrend noch �ltere nur 20 Waypoints laden k�nnen\n",
5356	  var => \$gps_waypoints,
5357	 ],
5358	 ['gpswaypointlength','=i',10,
5359	  choices => [10, 14, 20],
5360	  label => M"Maximale L�nge von GPS-Waypoint-Namen",
5361	  longhelp => M"Typischerweise 10 bei �lteren Garmin-Ger�ten, aber neuere Ger�te k�nnen l�ngere Namen verwenden (eTrex Vista HCx z.B. offiziell 14 Zeichen, tats�chlich sogar 20 Zeichen)",
5362	  var => \$gps_waypointlength,
5363	 ],
5364	 ['gpswaypointcharset','=s','simpleascii',
5365	  label => 'Zeichensatz f�r Waypoints',
5366	  strict => 1,
5367	  choices => [['Nur Gro�buchstaben' => 'simpleascii'],
5368		      ['Gro�/Kleinbuchstaben' => 'ascii'],
5369		      ['Gro�/Kleinbuchstaben, Umlaute' => 'latin1'],
5370		     ],
5371	  var => \$gps_waypointcharset,
5372	 ],
5373	 ['gpswaypointsymbol','=i','',
5374	  label => M"Waypointsymbol",
5375	  longhelp => M"Garmin-Symbol-ID. Falls leer gelassen, wird das Summit-Symbol verwendet",
5376	  var => \$gps_waypointsymbol,
5377	 ],
5378	 ['gpsneeduniqueroutenumber','!',0,
5379	  label => M"GPS-Ger�t ben�tigt eindeutige Routennummern",
5380	  longhelp => M"Laut Garmin-Spezifikationen m�ssen �betragene Routen mit einer eindeutigen Routennummer versehen werden.\nExperimente haben aber gezeigt, dass die meisten (oder alle?) Garmin-Ger�te dieses nicht ben�tigen.",
5381	  var => \$gps_needuniqueroutenumber,
5382	 ],
5383	 ['gpsdevice','=s',($os eq 'win'   ? "USB" :
5384			    $os_bsd        ? '/dev/cuaa0' :
5385			    $^O eq 'linux' ? '/dev/ttyUSB0'
5386				           : '/dev/ttyS0'
5387			   ),
5388	  choices => (  $os eq 'win' ? ["USB", (map {  "COM$_" 			       } (1..4))]
5389		      : $os_bsd      ? [map {  "/dev/cuaa$_"		       } (0..3) ]
5390		      :                [map { ($_."0", $_."1", $_."2", $_."3") } ("/dev/ttyUSB", "/dev/usb/ttyUSB", "/dev/tts/USB", "/dev/ttyS") ]
5391		     ),
5392	  label => M"GPS-Device", var => \$gps_device],
5393
5394	 M"Sonstiges",
5395	 ['kde','!',undef,
5396	  label => M"F�r KDE optimieren", var => \$run_under_kde],
5397	 ['handheld','!',undef,
5398	  label => M"F�r kleine Bildschirme optimieren", var => \$is_handheld,
5399	  longhelp => M"F�r kleine Bildschirme (Handhelds, PDAs, mobile Telefone) optimieren. Bei dieser Einstellung werden kleine Symbole verwendet und das normale Men� wird entfernt",
5400	 ],
5401	 ['coordout','=s','standard',
5402	  label => M"Koordinatenausgabe", var => \$coord_output],
5403	 ['printcmd','=s',undef,
5404	  label => M"Druckerkommando", var => \$print_cmd],
5405	 ['printbackend','=s',undef,
5406	  label => M"Druck-Backend", var => \$print_backend,
5407	  choices => ["", qw(ps pdf)],
5408	 ],
5409	 ['ps_fixed_font','=s',"Courier7",
5410	  label => M"Druckerzeichensatz (fixed)", var => \$ps_fixed_font],
5411	 ['mapcolor','=s','color',
5412	  choices => [qw(mono pixmap gray color)],
5413	  label => M"Farbeinstellung beim Drucken", var => \$map_color],
5414	 ['gvreuse','!',0,	# 1: alten gv-Prozess wiederverwenden
5415	  label => M"GV-Fenster wiederverwenden", var => \$gv_reuse],
5416	 ['server','!',undef,
5417	  label => M"Server-Modus", var => \$use_server],
5418	 ['autosave','!',1,
5419	  label => M"Speichern beim Beenden", var => \$autosave_opts],
5420	 ['environment','=s','normal',
5421	  # "novacom" (f�r GDF-Daten als Standard)
5422	  # "onlineoffice" (f�r Onlineoffice-Pr�sentationen)
5423	  nogui => 1, var => \$environment],
5424	 ['mldbm','!',0,
5425	  label => M"Verwendung von MLDBM",
5426	  longhelp => M"Die interne Stra�ennetz-Struktur wird als MLDBM-Hash
5427auf der Festplatte statt im RAM gehalten. Langsamer, aber
5428speicherplatzsparender.",
5429	  var => \$use_mldbm],
5430	 ['palmdocfmt','=s','isilo',
5431	  choices => [qw(isilo pdbdoc)],
5432	  label => M"Palm-Doc-Format", var => \$palm_doc_format],
5433	 ['usexwd','!',undef,
5434	  label => M"xwd als Screengrabber", var => \$use_xwd_if_possible],
5435
5436	 M"Advanced",
5437	 ['edit','!',undef,
5438	  label => M"Editmodus beim Starten",
5439	  nogui => 1,		# XXX remove some day?
5440	  var => sub {
5441	      $set_mode = "edit";
5442	  }
5443	 ],
5444	 ['edittools','!',undef,
5445	  label => M"Editierwerkzeuge beim Starten �ffnen",
5446	  nogui => 1, 	# XXX remove some day?
5447	  var => \$init_with_edittools,
5448	 ],
5449	 ['texteditor','=s',undef,
5450	  label => M"Externer Texteditor",
5451	  var => \$texteditor,
5452	  longhelp => M"M�gliche Werte sind vi (automatisch in einem xterm gestartet), emacsclient, gnuclient",
5453	 ],
5454	 ['stderr','!',0,
5455	  label => M"Fehlerausgabe auf stderr", var => \$stderr],
5456	 ['stderrwindow','!',undef,
5457	  label => M"STDERR in ein Fenster", var => \$stderr_window],
5458	 ['autoinstall','!',0,
5459	  label => M"Auto-Installation vom CPAN (experimentell!)", var => \$auto_install_cpan],
5460	 ['pp','!',0,
5461	  label => M"Kurvenpunkte und Kreuzungen zeichnen", var => \$init_p_draw{'pp'}, nosave => 1, nogui => 1],
5462	 ['advanced','!',undef, var => \$advanced,
5463	  label => M"Advanced mode"],
5464	 ['public','!',undef, nogui => 1,
5465	  var => \&_set_public],
5466	 ['publicconfig','!',undef, nogui => 1,
5467	  var => \&_set_public],
5468	 ['configfile','=s',undef, nogui => 1], # used only in pre_check_arguments
5469	 ['v','!',0,	alias => [qw(verbose)],
5470	  label => M"Verbose", var => \$verbose,
5471	  longhelp => M"Die Variable \$verbose kann manuell auf 2 oder h�her gesetzt werden, um die Anwendung wortreicher zu machen"],
5472	 ['version','!',undef,
5473	  nogui => 1, var => sub {
5474	      my %git_info;
5475	      if (-r "$FindBin::RealBin/miscsrc/BBBikeGit.pm") {
5476		  require "$FindBin::RealBin/miscsrc/BBBikeGit.pm";
5477		  %git_info = BBBikeGit::git_info();
5478	      }
5479	      print("$progname $VERSION\n",
5480		    ($git_info{patchnum} ? "(patchnum $git_info{patchnum})\n" : ''),
5481		    "perl $]\nTk $Tk::VERSION\n",
5482		   );
5483	      CORE::exit(0);
5484	  }],
5485	 ['plugins','=s',undef,
5486	  label => M"Plugins beim Starten laden", var => \$initial_plugins,
5487	  longhelp => M"Kommaseparierte Liste von Plugins, z.B. BBBikeThunder,BBBikeSalesman,BBBikeRuler", # XXX Auf den PluginLister verweisen, wenn er fertig ist.
5488	  widget => sub {
5489	      my $self = shift;
5490	      my $frame = shift;
5491	      my(@args) = @_;
5492	      my $f = $frame->Frame;
5493	      $self->_string_widget($f, @args)->pack(-side => "left");
5494	      $f->Button(-text => M"Plugin-Lister",
5495			 -padx => 1,
5496			 -pady => 1,
5497			 -command => sub {
5498			     require BBBikePluginLister;
5499			     BBBikePluginLister::plugin_lister($top, $FindBin::RealBin);
5500			 })->pack(-side => "left");
5501	      $f;
5502	  },
5503	 ],
5504	 ['layers','=s',undef,
5505	  label => M"Zus�tzliche Layer zeichnen", var => \$initial_layers],
5506	 ['algorithm','=s','A*', var => \$global_search_args{Algorithm},
5507	  longhelp => M"Nur A* (Perl-Implementation) und C-A* (C-Implementation) sind von Interesse",
5508	  choices => ['A*', 'C-A*', ($devel_host||$advanced ? ("C-A*-2", 'srt') : ())],
5509	  label => M"Suchalgorithmus",
5510	  strict => 1],
5511	 ['h','!',undef, nogui => 1, alias => [qw(help)],
5512	  var => sub {
5513	      if ($opt) {
5514		  print STDERR $opt->usage;
5515	      } else {
5516		  die M"Usage?";
5517	      }
5518	      exit(0);
5519	  }],
5520	 ['nosplash','!',undef, nogui => 1], # pseudo option, handled at BEGIN
5521	);
5522
5523    eval {
5524	require Tk::Getopt;
5525	Tk::Getopt->VERSION(0.4951);
5526    };
5527    if ($@) {			# XXX
5528	die "Please report to author: use opttable_to_getopt!!!! XXX";
5529	warn __LINE__ . ": $@" if $verbose;
5530	my @getopt_list;
5531	foreach (@getopt) {
5532	    push @getopt_list, $_ unless /^=/;
5533	}
5534	# XXX '@' geht nur mit Getopt::Long
5535	push @getopt_list, 'power=i@' => \@power, 'speed=i@' => \@speed;
5536	require Getopt::Long;
5537	#XXX X11-Optionen durchschleifen...
5538	#    if (!Getopt::Long::GetOptions(@getopt_list)) { usage('', \@getopt_list) }
5539	Getopt::Long::config('pass_through');
5540	Getopt::Long::GetOptions(@getopt_list);
5541	#XXX    if (!GetOptions(@getopt_list)) { usage('', \@getopt_list) }
5542    } else {
5543	$Tk::Getopt::x11_pass_through = 1;
5544	pre_check_arguments(); # sets $public
5545#	$opt = Tk::Getopt->new
5546	$opt = My::Tk::Getopt->new
5547	    (-opttable => \@opttable,
5548	     -filename => defined $config_file ? $config_file : catfile($bbbike_configdir, ($public ? "config_publictest" : "config")),
5549	     -useerrordialog => 1,
5550	    );
5551	$opt->set_defaults;
5552	$opt->load_options if !$public || $public_config; # force defaults
5553	if (!$opt->get_options) {
5554	    print $opt->usage;
5555	    exit 1;
5556	}
5557	$opt->process_options;
5558    }
5559    Tk::CmdLine::SetArguments(); # XXX here correct position?
5560    if (@ARGV) {
5561	require Getopt::Long;
5562	Getopt::Long::config('nopass_through');
5563	Getopt::Long::GetOptions() or die;
5564    }
5565}
5566
5567sub _set_public {
5568    $public_test = 1;
5569    $advanced = 0;
5570    $devel_host = 0;
5571    $do_www = 0;
5572    $no_map = 1;
5573    $public = 1;
5574    $autosave_opts = 0;
5575    $lazy_plot = 0;
5576    undef $proxy;
5577    # Not in old standard Tk:
5578    if ($Tk::VERSION < 804) {
5579	$can_handle_image{png} = 0;
5580        $can_handle_image{jpg} = 0;
5581    }
5582}
5583
5584sub c_balloon_update {
5585    if ($c_balloon && Tk::Exists($c_balloon)) {
5586	$c_balloon->configure(-show => $use_c_balloon);
5587    }
5588}
5589
5590# Check for -public and -publicconfig options --- in this case do not
5591# load the config file.
5592sub pre_check_arguments {
5593    for(my $arg_i=0; $arg_i<=$#ARGV; $arg_i++) {
5594	my $arg = $ARGV[$arg_i];
5595	if ($arg eq '-public') {
5596	    $public = 1;
5597	} elsif ($arg eq '-publicconfig') {
5598	    $public = 1;
5599	    $public_config = 1;
5600	} elsif ($arg eq '-configfile') {
5601	    $config_file = $ARGV[$arg_i+1];
5602	    die "Expected argument for -configfile option" if !$config_file;
5603	    $arg_i++;
5604	}
5605    }
5606}
5607
5608# For binding plain keybindings without modifiers
5609sub bind_nomod {
5610    my($top, $ev, $cb) = @_;
5611    $top->bind
5612	($ev, sub {
5613	     my $w = shift;
5614	     my $e = $w->XEvent;
5615	     # auf Alt, Control und CapsLock checken
5616	     # bei Win95/NT ist 8 nicht CapsLock, sondern NumLock
5617	     if ($Tk::VERSION < 800) {
5618		 return if $e->s & (1+($os eq 'win' ? 0 : 8)); # XXX control is missing ... 4? 2 ist Shift?
5619	     } else {
5620		 return if $e->s =~ /\b(Alt|Lock|Control)-/;
5621	     }
5622	     $cb->($w, @_);
5623	 });
5624}
5625
5626# km <=> m (<=> mi)
5627sub change_unit {
5628    my $new_unit = shift;
5629    if (defined $new_unit) {
5630	$unit_s = $new_unit;
5631    } elsif ($Msg::lang eq 'en') {
5632	$unit_s = ($unit_s eq 'km' ? 'mi' :
5633		   $unit_s eq 'mi' ? 'm' :
5634		   'km');
5635    } else {
5636	$unit_s = ($unit_s eq 'km' ? 'm' : 'km');
5637    }
5638    updatekm();
5639}
5640
5641sub standard_selection_handle {
5642    $c->SelectionHandle
5643	(sub {
5644	     my($offset, $maxbytes) = @_;
5645	     my($inslauf) = join(" ", @inslauf_selection);
5646	     return undef if $offset > length($inslauf);
5647	     substr($inslauf, $offset, $maxbytes);
5648	 });
5649}
5650
5651sub load_photos {
5652    # Note that some rarely used photos are loaded on-demand.
5653    $flag_photo{'start'} = load_photo($top, 'flag2_bl_centered');
5654    $flag_photo{'via'}   = load_photo($top, 'flag_via_centered');
5655    $flag_photo{'ziel'}  = load_photo($top, 'flag_ziel_centered');
5656    $ampel_photo         = load_photo($top, 'ampel');
5657    $ampel_klein2_photo  = load_photo($top, 'ampel_klein2');
5658    $ampelf_photo        = load_photo($top, 'ampelf');
5659    $ampelf_klein_photo  = load_photo($top, 'ampelf_klein');
5660    $ampelf_klein2_photo = load_photo($top, 'ampelf_klein2');
5661    $andreaskr_klein_photo = load_photo($top, 'andreaskr_klein');
5662    $andreaskr_klein2_photo= load_photo($top, 'andreaskr_klein2');
5663    $andreaskr_photo     = load_photo($top, 'andreaskr');
5664    $andreaskr_grey_klein_photo = load_photo($top, 'andreaskr_klein', -palette => 256);
5665    $andreaskr_grey_klein2_photo= load_photo($top, 'andreaskr_klein2', -palette => 256);
5666    $andreaskr_grey_photo       = load_photo($top, 'andreaskr', -palette => 256);
5667    $kreisverkehr_photo  = load_photo($top, 'kreisverkehr');
5668    $windrose2_photo     = load_photo($top, 'windrose2');
5669    $kneipen_photo       = load_photo($top, 'glas');
5670    $kneipen_klein_photo = load_photo($top, 'glas_klein');
5671    $essen_photo         = load_photo($top, 'essen');
5672    $essen_klein_photo   = load_photo($top, 'essen_klein');
5673    $kino_klein_photo    = load_photo($top, 'kino_klein');
5674    $steigung_photo      = load_photo($top, 'steigung');
5675    $gefaelle_photo      = load_photo($top, 'gefaelle');
5676    $inwork_photo        = load_photo($top, 'inwork_18');
5677    $inwork_klein_photo  = load_photo($top, 'inwork_12');
5678    $achtung_photo       = load_photo($top, 'achtung');
5679    $cal_photo           = load_photo($top, 'cal');
5680    $cal_questionmark_photo = load_photo($top, 'cal_questionmark');
5681    $clock_photo	 = load_photo($top, 'clock');
5682    $night_photo	 = load_photo($top, 'night');
5683    $ferry_photo         = load_photo($top, 'ferry')
5684	if !$ferry_photo;
5685    $ferry_klein_photo   = load_photo($top, 'ferry_klein');
5686    $ferry_mini_photo    = load_photo($top, 'ferry_mini');
5687    $zugbruecke_photo    = load_photo($top, 'zugbruecke');
5688    $zugbruecke_klein_photo
5689	= load_photo($top, 'zugbruecke_klein');
5690    $notrailer_photo     = load_photo($top, 'notrailer');
5691#XXX not yet necessary:
5692#    $blocked_photo       = load_photo($top, 'redcross');
5693}
5694
5695sub set_default_geometry {
5696    if ($geometry && !$open_maximized) {
5697	@want_extends = parse_geometry_string($geometry);
5698	if (!$want_extends[GEOMETRY_WIDTH] || !$want_extends[GEOMETRY_HEIGHT]) { # test on 0 or undef
5699	    ($want_extends[GEOMETRY_WIDTH], $want_extends[GEOMETRY_HEIGHT]) =
5700		($top->screenwidth, $top->screenheight);
5701	}
5702	if (!defined $want_extends[GEOMETRY_X] || !defined $want_extends[GEOMETRY_Y]) {
5703	    ($want_extends[GEOMETRY_X], $want_extends[GEOMETRY_Y]) = (0, 0);
5704	}
5705    } else {
5706	@want_extends = (0, 0, $top->screenwidth, $top->screenheight);
5707    }
5708    if ($kde) {
5709	@max_extends = $kde->client_window_region();
5710    } elsif ($os eq 'win') {
5711	@max_extends = Win32Util::client_window_region($top);
5712    } elsif ($^O eq 'darwin') {
5713	@max_extends = MacOSXUtil::client_window_region($top);
5714    } else {
5715	if (
5716	    # check for broken ->property on 64bit platforms
5717	    ($Tk::VERSION >= 804.027501 || $Config{longsize} == 4) &&
5718	    $top->property("exists", "_NET_CURRENT_DESKTOP", "root") &&
5719	    $top->property("exists", "_NET_WORKAREA", "root")
5720	   ) {
5721	    (undef, my $desktop) = $top->property("get", "_NET_CURRENT_DESKTOP", "root");
5722	    if (defined $desktop) {
5723		my @vals = ($top->property("get", "_NET_WORKAREA", "root"))[$desktop*4+1 .. $desktop*4+4];
5724		if (@vals && defined $vals[0]) {
5725		    @max_extends = @vals;
5726		}
5727	    }
5728	    #$max_extends[2]-=10; # XXX hmmm, does not need to be necessary on gnome/metacity
5729	    #$max_extends[3]-=24; # XXX "
5730	}
5731    }
5732    if (!@max_extends) {
5733	# XXX guess width/height of wm borders and title bar
5734	@max_extends = (0, 0, $top->screenwidth-10, $top->screenheight-24);
5735    }
5736
5737    crop_geometry(\@want_extends, \@max_extends);
5738}
5739
5740
5741# after geometry processing
5742sub geometry_dependent_settings {
5743    my $win_width = @want_extends ? $want_extends[GEOMETRY_WIDTH] : $top->width;
5744    my $win_height = @want_extends ? $want_extends[GEOMETRY_HEIGHT] : $top->height;
5745    if ($win_width <= 320 || $win_height <= 320 || $is_handheld) {
5746	$small_icons = 1;
5747	$standard_menubar = 0;
5748	set_canvas_scale(DEFAULT_SMALL_SCALE);
5749    }
5750    if ($is_handheld) {
5751	$use_balloon = 0;
5752	$use_c_balloon = 0;
5753	$use_contexthelp = 0;
5754	$right_is_popup = 0;
5755	$followmouse = 0;
5756	$b2_mode = B2M_NONE;
5757    }
5758}
5759
5760sub define_item_attribs {
5761# grey99 wird als Wei�-Ersatz verwendet (damit die Postscript-Umwandlung
5762# besser funktioniert)
5763# grey98 ebenfalls, aber wenn Outlines eingeschaltet sind, dann wird
5764# diese Farbe nach Wei� umgewandelt.
5765# white wird �berall dort verwendet, wo eine andere Hintergrundfarbe an der
5766# Stelle definiert ist, z.B. beim U-Bahn-Symbol oder in der Legende
5767    my @area_cats = qw(P W I Ae ex-Ae Forest Cemetery Orchard Green Sport Industrial Mine);
5768    %category_color =
5769	('NN' => '#bdffbd',
5770	 'N'  => 'grey98',
5771	 'NH' => '#ffffb0',     # noch blasseres gelb --- XXX �berhaupt unterscheidbar?
5772	 'H'  => '#ffff90', 	# blassgelb
5773	 'HH' => '#fff800', 	# kr�ftiges gelb
5774	 'BAB' => 'DarkBlue',
5775	 'B'  => 'red3',
5776	 # zweiter (pragmatischer) Versuch einer Qualit�tskategorisierung
5777	 # sehr guter Asphalt = guter Asphalt (genauere Kategorisierung nicht
5778	 # erforderlich)
5779	 # sehr gutes Kopfsteinpflaster = guter Asphalt		Q0
5780	 # gutes Kopfsteinpflaster      = m��iger Asphalt	Q1
5781	 # m��iges Kopfsteinpflaster    = schlechter Asphalt	Q2
5782	 # schlechtes Kopfsteinpflaster				Q3
5783	 'Q0' => 'DarkSeaGreen4',
5784	 'Q1' => 'YellowGreen',
5785	 'Q2' => 'gold',
5786	 'Q3' => 'red',
5787	 # sonstige Beeintr�chtigungen, die nicht auf schlechte Qualit�t zur�ckzuf�hren
5788	 # sind und nur die Geschwindigkeit reduzieren. Geschwindigkeitsreduktion
5789	 # wie bei Q.
5790	 'q0' => 'DarkSeaGreen4',
5791	 'q1' => 'YellowGreen',
5792	 'q2' => 'gold',
5793	 'q3' => 'red',
5794	 'q4' => '#c00000',
5795	 # sonstiges
5796	 'S'  => 'green3',	# S-Bahn
5797	 'SA' => 'green3',	# S-Bahn, Zone A
5798	 'SB' => 'green3',	# S-Bahn, Zone B
5799	 'SC' => '#008000', 	# S-Bahn, Zone C
5800	 'S0' => '#a0b0a0', 	# stillgelegte S-Bahn
5801	 'SBau' => '#a0b0a0', 	# S-Bahn in Bau
5802	 'SBetrieb' => 'green3', 	# S-Bahn, Betriebsfahrten
5803	 ## neues Farbschema an DB-Farben orientiert
5804	 ## nicht gut, da nicht gut von Bundesstra�en unterscheidbar
5805	 #     'RA' => '#bb171d',  # R-Bahn, Zone A
5806	 #     'RB' => '#bb171d',  # R-Bahn, Zone B
5807	 #     'RC' => '#bb171d', # R-Bahn, Zone C
5808	 #     'R'  => '#bb171d', # R-Bahn, au�erhalb
5809	 #     'R0' => '#d0c0c0', # stillgelegte R-Bahn bzw. in Bau
5810	 ## altes Farbschema
5811	 'RA' => 'green3',	# R-Bahn, Zone A
5812	 'RB' => 'green3',	# R-Bahn, Zone B
5813	 'RC' => '#008000', 	# R-Bahn, Zone C
5814	 'R'  => '#006400', 	# R-Bahn, au�erhalb
5815	 'R0' => '#a0b0a0', 	# stillgelegte R-Bahn
5816	 'RBau' => '#a0b0a0', 	# in Bau
5817	 'RG' => '#a0c8a0',	# G�terbahnen
5818	 'RP' => '#49c043',	# Parkbahnen...
5819	 'U'  => '#000080', 	# U-Bahn
5820	 'UA' => '#000080', 	# U-Bahn, Zone A
5821	 'UB' => '#000080', 	# U-Bahn, Zone B
5822	 'U0' => '#a0a0b0', 	# stillgelegte U-Bahn
5823	 'UBau' => '#a0a0b0', 	# U-Bahn in Bau
5824	 'UBetrieb' => '#000080', 	# U-Bahn, Betriebsfahrten
5825	 'W'  => '#bad5f7', 	# Gew�sser
5826	 'WR' => '#404080',	# Wasserrouten
5827	 'P'  => '#76c48b', 	# Parks
5828	 'Forest'  => '#66b47b', 	# W�lder
5829	 'Cemetery'  => '#70c085', 	# Friedh�fe
5830	 'Green' => '#76c48b', 	# sonstige Gr�nanlagen
5831	 'Orchard' => '#e8f8c8', 	# Kleing�rten (was #80ca94)
5832	 'Sport' => '#c8d898', 	# Sportanlagen (was #86d49b)
5833	 'Industrial' => '#d7b8c8',	# Industriegebiete
5834	 'Ae' => 'white',	# Flugh�fen
5835	 'ex-Ae' => 'white',	# ehemalige Flugh�fen
5836	 'Mine' => 'white',	# Tagebau, Bergbau
5837	 'F'  => 'grey99',	# sonstige Fl�chen
5838	 'SW' => 'red',		# Sehensw�rdigkeit
5839	 'Shop' => 'red',	# Einkaufszentrum, Markthalle
5840	 'Q'  => 'grey99',	# F�hre
5841	 'I'  => 'grey85',	# Inseln (wird sp�ter �berschrieben)
5842	 'Z'  => 'black',	# PLZ-Grenzen
5843
5844	 'RW1' => 'SlateBlue',	# siehe Radwege.pm
5845	 'RW2' => '#00008b',   	# DarkBlue ist in der Win-Version undefiniert
5846	 'RW3' => '#80e599',	# fr�her LightBlue, jetzt fast green, da fast kein Unterschied zwischen Suggestiv-/Radstreifen
5847	 'RW4' => 'green',
5848	 'RW5' => 'orange',
5849	 'RW6' => 'yellow3',
5850	 'RW7' => 'green',
5851	 'RW8' => '#000060',
5852	 'RW9' => 'SlateBlue',
5853	 'RW10' => 'green',
5854	 'RW'  => 'SlateBlue',
5855
5856	 'sperre0' => 'red',	# Tragen
5857	 'sperre1' => 'blue',	# Einbahnstra�en
5858	 'sperre1s' => '#b0b0ff',  # Einbahnstra�en (nur mit "einbahn-strict")
5859	 'sperre2' => 'red',	# voll gesperrt
5860	 'sperre3' => 'red',	# Wegf�hrung gesperrt
5861
5862	 'IN' => 'violet',	# Industrieanlagen
5863	 'HB' => 'DarkViolet',	# Hafenanlagen
5864	 'BU' => '#c08080',    	# Built-up areas
5865	 'FO' => '#46b47b',    	# W�lder
5866	 'MO' => '#008080',    	# Moor
5867
5868	 '?'  => '#9f0000',
5869	 '??' => '#8b0000', 	# DarkRed, bei Win undefiniert
5870	 '?p' => '#af0000',
5871	 'GPS' => 'red',	# GPS Relation
5872	 'GPSs'   => "#c000c0",  # GPS street
5873	 'GPSs~'  => "#f4c0f4",  # inaccurate
5874	 'GPSs~~' => "#e4c8e4",  # even more inaccurate
5875	 'GPSs?'  => "#303030",  # unsure
5876	 'GPSp'   => "#0000a0",  # GPS point
5877	 'GPSp~'  => "#c0c0b0",  # GPS point
5878	 'GPSp~~' => "#c8c8c0",  # GPS point
5879	 'GPSp?'  => "#303030",  # unsure
5880
5881	 'CP' => '#a000a0',
5882	 'CP2'=> '#a000a0',
5883	 'CS' => '#a000a0',
5884	 'St' => '#b00080',
5885	 'Gf' => '#c00080',
5886	 'PI' => '#a000a0',
5887	 'P0' => '#a000a0',
5888
5889	 '-2' => '#008000', # (relativ) verkehrsarme Stra�e
5890	 '-1' => '#00c000',
5891	 '+1' => '#c00000',
5892	 '+2' => '#800000', # (relativ) verkehrsreiche Stra�e
5893
5894	 'green1' => '#7fbb7f',
5895	 'green2' => '#008b00',
5896
5897	 'radroute' => 'SlateBlue',
5898
5899	 'X' => "red", # fallback color
5900	);
5901    for (qw(Q0 Q1 Q2 Q3 q0 q1 q2 q3 q4)) { # same colors for tendencies
5902	$category_color{$_."-"} = $category_color{$_};
5903	$category_color{$_."+"} = $category_color{$_};
5904    }
5905    for (1 .. 10) {
5906	$category_color{"RW".$_."?"} = $category_color{"RW".$_};
5907    }
5908    $layer_category_color{'e'}->{'CS'} = $category_color{'Q'};
5909    %category_font_color =
5910	(
5911	 'W'  => '#2a45b7',
5912	 'U'  => '#000060',
5913	 'S'  => '#006000',
5914	 'R'  => '#006000', 	# altes Farbschema
5915	 #   'R'  => '#a00000',# neues Farbschema
5916	);
5917    for (qw(UA UB U0)) { $category_font_color{$_} = $category_font_color{"U"} }
5918    for (qw(SA SB SC S0)) { $category_font_color{$_} = $category_font_color{"S"} }
5919    for (qw(RA RB RC R0 RBau RG RP)) { $category_font_color{$_} = $category_font_color{"R"} }
5920    %category_font =
5921	(
5922	 'W'  => ($has_xft ? "$sans_serif_font_family:size=%d:matrix=1 -0.15 0 1" : "$sans_serif_font_family %d italic"),
5923	 'I'  => "$sans_serif_font_family %d italic",
5924	 'P'  => "$sans_serif_font_family %d",
5925	 'Ae' => "$sans_serif_font_family %d",
5926	 'ex-Ae' => "$sans_serif_font_family %d",
5927	);
5928    # all other area categories:
5929    for my $cat (@area_cats) {
5930	if (!exists $category_font{$cat}) {
5931	    $category_font{$cat} = $category_font{'P'};
5932	}
5933    }
5934    # 'above' categories share the same attributes like the non-'above' ones:
5935    for my $cat (@area_cats) {
5936	my $cat_above = $cat . 'above';
5937	if (!exists $category_color{$cat_above} && exists $category_color{$cat}) {
5938	    $category_color{$cat_above} = $category_color{$cat};
5939	}
5940	if (!exists $category_font{$cat_above} && exists $category_font{$cat}) {
5941	    $category_font{$cat_above} = $category_font{$cat};
5942	}
5943    }
5944
5945    #$pp_color = '#008000'; # bad contrast with rbahn, not good with Bundesstra�e, but better with fragezeichen
5946    # second element is color for real crossing, but not yet activated
5947    #$pp_color = ['#800000', 'blue'];
5948    #$pp_color = '#800000'; # bad contrast with fragezeichen
5949    $pp_color = '#000080';
5950
5951    for my $nr (0, 1, 2) {
5952	$category_color{'W' . $nr}      = $category_color{'W'};
5953	$category_font_color{'W' . $nr} = $category_font_color{'W'};
5954	$category_font{'W' . $nr}       = $category_font{'W'};
5955    }
5956    # fallback, falls kein %category_color definiert ist
5957    %str_color =
5958	('s'   => 'yellow',
5959	 'L'   => 'red',
5960	 'qs'  => 'red',
5961	 'ql'  => 'red',
5962	 'hs'  => 'red',
5963	 'hl'  => 'red',
5964	 'nl'  => 'black',
5965	 'gr'  => 'green',
5966	);
5967    %p_color =
5968	();
5969
5970    # XXX use klein and klein2 versions, how? array/hash for different scalings?
5971    %category_image =
5972	('bg'  => "aufzug.gif", # bg=behindertengerecht
5973	 'bf'  => "rampe.gif", # bf=behindertenfreundlich
5974	);
5975
5976    %category_stipple =
5977	(
5978	 'Cemetery' => 'crosses.xbm',
5979	 'Cemetery|religion:jewish' => 'stars_of_david.xbm',
5980	 'Cemetery|religion:muslim' => 'halfmoons.xbm',
5981	);
5982
5983    %line_width =
5984	('s-NN'     => [1, 1, 2, 2, 4, 7],
5985	 's-N'      => [1, 1, 2, 2, 4, 7],
5986	 's-NH'     => [1, 1, 2, 3, 5, 8],
5987	 's-H'      => [1, 2, 3, 4, 6, 10],
5988	 's-HH'     => [1, 2, 3, 4, 6, 10],
5989	 's-B'      => [1, 2, 3, 4, 6, 10],
5990	 's-BAB'    => [1, 2, 3, 4, 6, 10],
5991	 'sBAB-BAB' => [1, 2, 3, 4, 6, 10],
5992	 'comm'     => [1, 2, 3, 4, 6, 10],
5993	 'mount'    => [1, 2, 3, 4, 6, 10],
5994	 'qs'       => [3, 4, 5, 6, 8, 12],
5995	 'hs'       => [3, 4, 5, 6, 8, 12],
5996	 'temp_sperre_s' => [5, 6, 7, 8, 10, 14],
5997	 'rw'       => [1, 2, 3, 4, 6, 10],
5998	 'l'        => [2, 2, 3, 4, 6, 10],
5999	 'gr'       => [5, 7, 8, 9, 10, 14], # s-H + 4 pixels
6000	 'ql'       => [3, 4, 5, 6, 8, 12],
6001	 'hl'       => [3, 4, 5, 6, 8, 12],
6002	 'z'        => [1, 1, 2, 3, 5, 8],
6003	 'g'        => [1, 2, 3, 4, 6, 10],
6004	 'e'        => [1, 2, 3, 4, 6, 10],
6005	 #   'sperre0'  => [3, 5, 7, 9, 11,15],
6006	 'sperre0'  => [1, 2, 2, 2, 3, 3],
6007	 'sperre1'  => [0, 0, 2, 3, 4, 6],
6008	 'sperre2'  => [0, 0, 2, 3, 5, 8],
6009	 'sperre3'  => [0, 0, 1, 2, 4, 6],
6010	 'sperre3nocross' => [0, 0, 1, 1, 2, 3],
6011	 'w'        => [1, 1, 2, 2, 4, 7],
6012	 'w-W0'     => [0, 1, 1, 1, 3, 5],
6013	 'w-W1'     => [2, 2, 3, 5, 7, 11],
6014	 'w-W2'     => [3, 4, 6, 8, 10,13],
6015	 'comm-scenic-View' => [4, 7, 9, 12, 16, 20],
6016	 'u-UBetrieb' => [1, 1, 2, 3, 4, 6],
6017	 'b-SBetrieb' => [1, 1, 2, 3, 4, 6],
6018	 'default'  => [1, 2, 3, 4, 6, 10],
6019	);
6020    foreach (qw/NN N NH H HH B BAB/) {
6021	$line_width{"l-$_"} = [@{ $line_width{"s-$_"}}];
6022    }
6023    foreach (qw/sperre1s/) {
6024	$line_width{$_} = [@{ $line_width{"sperre1"}}];
6025    }
6026    foreach (qw/gBO gP gD/) {
6027	$line_width{$_} = [@{ $line_width{"g"}}];
6028    }
6029    my %narrow_comments_types = map {($_,1)} qw(tram misc mount kfzverkehr scenic);
6030    foreach (@comments_types) {
6031	if ($narrow_comments_types{$_}) {
6032	    $line_width{'comm-'.$_} = [1, 1, 1, 1, 2, 3];
6033	} else {
6034	    $line_width{"comm-".$_} = [@{ $line_width{"comm"}}];
6035	}
6036    }
6037
6038    %line_dash =
6039	('qs'   => [5,2],
6040	 'ql'   => [5,2],
6041	 'hs'   => [2,5],
6042	 'hl'   => [2,5],
6043	 'temp_sperre_s' => [2,5],
6044	 'nl'   => [2,4],
6045	 'comm' => [5,2],
6046	 'comm-tram' => [2,6],
6047	 'mount'=> [5,2],
6048	 'e'    => [5,2],	# F�hren
6049	 'g'    => [8,5,2,5],	# Grenzen
6050	 'z'    => [8,5,2,5],	# PLZ-Grenzen
6051	 'sperre3' => [6,2],
6052	 'fz'   => [8,5],
6053	 'Tu'   => [4,5],	# Tunnel (addinfo)
6054	);
6055    %category_dash =
6056	('R0'   => [1,5],
6057	 'U0'   => [1,5],
6058	 'S0'   => [1,5],
6059	 'radroute' => [1,15],
6060	);
6061    %category_capstyle =
6062	('radroute' => 'round'); # XXX $capstyle_round not available at this time!
6063    foreach (qw/gBO gP gD/) {
6064	$line_dash{$_} = [@{ $line_dash{"g"}}];
6065    }
6066    foreach (grep { $_ !~ m{^(tram|ferry|cyclepath)$} } @comments_types) {
6067	$line_dash{"comm-".$_} = [@{ $line_dash{"comm"}}];
6068    }
6069    $line_dash{'comm-ferry'} = $line_dash{'e'};
6070
6071    %line_length =
6072	('sperre1'  => [0, 0, 4, 5, 7, 10],
6073	 'sperre2'  => [0, 0, 3, 4, 6, 8],
6074	 'default'  => [2, 3, 4, 5, 7, 10],
6075	);
6076    foreach (qw/sperre1s/) {
6077	$line_length{$_} = [@{ $line_length{"sperre1"}}];
6078    }
6079
6080    %category_line_arrow =
6081	('PI' => 'last',
6082	 'P0' => 'last',
6083	);
6084    %category_line_shorten =
6085	('CP'  => 1,
6086	 'P0'  => 1,
6087	);
6088    %category_line_shorten_end =
6089	('CP2' => 1,
6090	 'PI'  => 1,
6091	);
6092    # Label size per category
6093    %category_size =
6094	('NN' => 7,
6095	 'N'  => 8,
6096	 'NH' => 9,
6097	 'H'  => 10,
6098	 'HH' => 10,
6099	 'B'  => 10,
6100	 'BAB'=> 10,
6101	 'W'  => 12);
6102    %category_point_size =
6103	('?' => 10,
6104	);
6105    %outline_color =
6106	('s' => 'grey70',
6107	 'l' => 'grey70',
6108	 'w' => 'blue4',
6109	 'i' => 'blue4',
6110	);
6111    %str_file =
6112	(# "primary"
6113	 's'  => 'strassen',
6114	 'l'  => 'landstrassen', # this is really scoped
6115	 'u'  => 'ubahn',
6116	 'b'  => 'sbahn',
6117	 'r'  => 'rbahn',
6118	 'w'  => 'wasserstrassen', # this is really scoped
6119	 'f'  => 'flaechen',
6120	 'v'  => 'sehenswuerdigkeit',
6121	 'z'  => 'plz',
6122	 'g'  => 'berlin',
6123	 'gP' => "potsdam",
6124	 'gD' => "deutschland",
6125	 'gBO'=> "berlin_ortsteile",
6126	 'e'  => 'faehren',
6127	 # dependent
6128	 'rw' => 'radwege',
6129	 'qs' => 'qualitaet_s',
6130	 'ql' => 'qualitaet_l',
6131	 'hs' => 'handicap_s',
6132	 'hl' => 'handicap_l',
6133	 'nl' => 'nolighting',
6134	 'gr' => 'green',
6135	 'comm' => 'comments', # this is splitted into multiple files
6136	 'mount' => 'mount',
6137	 # special
6138	 'fz' => "fragezeichen",
6139	 'wr' => "wasserrouten",
6140	);
6141    foreach my $type (@comments_types) {
6142	$str_file{"comm-$type"} = "comments_$type";
6143    }
6144    if ($devel_host) {
6145	$str_file{"is"} = "$FindBin::RealBin/projects/infrasystem/data/landstrassen-corrected";
6146    }
6147    %p_file =
6148	('lsa'    => 'ampeln',
6149	 'u'      => 'ubahnhof',
6150	 'u_bg'   => 'ubahnhof_bg',
6151	 'b'      => 'sbahnhof',
6152	 'b_bg'   => 'sbahnhof_bg',
6153	 'r'      => 'rbahnhof',
6154	 'o'      => 'orte',	# XXX scoped
6155	 'sperre' => $sperre_file,
6156	 'sperre_u' => 'gesperrt_u',
6157	 'sperre_b' => 'gesperrt_s',
6158	 'sperre_r' => 'gesperrt_r',
6159	 'obst'   => 'obst',
6160	 'pl'     => 'plaetze',
6161	 'vf'     => 'vorfahrt',
6162
6163	 'kn'     => 'kneipen',
6164	 'ki'     => 'kinos',
6165	 'rest'   => 'restaurants',
6166	 'GU'     => 'grenzuebergaenge',
6167	);
6168
6169    # Feld-Elemente
6170    # 0: Bezeichnung, Singular
6171    # 1: Bezeichnung, Plural
6172    # 2: Linien (bool)
6173    # 3: (falls vorhanden) lange Bezeichnung
6174    %str_attrib =
6175	('s' => [M"Stra�e",      M"Stra�en",      0],
6176	 'l' => [M"Landstra�e",  M"Landstra�en",  0],
6177	 'u' => [M"U-Bahnlinie", M"U-Bahnlinien", 1],
6178	 'b' => [M"S-Bahnlinie", M"S-Bahnlinien", 1],
6179	 'r' => [M"R-Bahnlinie", M"R-Bahnlinien", 1],
6180	 'w' => [M"Gew�sser",    M"Gew�sser",     0],
6181	 'f' => [M"Fl�che",      M"Fl�chen",      0],
6182	 'v' => [M"Sehensw�rdigkeit", M"Sehensw�rdigkeiten",      0],
6183	 'z' => [M"PLZ-Gebiet",  M"PLZ-Gebiete",  0],
6184	 'g' => [M"Grenze von Berlin", M"Grenze von Berlin",       0], # see below for override
6185	 'gP' => [M"Grenze von Potsdam", M"Grenze von Potsdam",       0],
6186	 'gD' => [M"Staatsgrenze", M"Staatsgrenze",       0],
6187	 'gBO' => [M"Berliner Ortsteil", M"Berliner Ortsteile", 0], # see below for override
6188	 'e' => [M"F�hre",       M"F�hren",       0],
6189	 'rw' => [M"Radweg",     M"Radwege", 0],
6190	 'qs' => [M"Stra�enqualit�t", M"Stra�enqualit�t", 0],
6191	 'ql' => [M"Stra�enqualit�t (Landstra�e)", M"Stra�enqualit�t (Landstra�e)", 0],
6192	 'hs' => [M"Sonst. Beeintr�chtigungen", M"Sonst. Beeintr�chtigungen", 0],
6193	 'hl' => [M"Sonst. Beeintr�chtigungen (Landstra�e)", M"Sonst. Beeintr�chtigungen (Landstra�e)", 0],
6194	 'nl' => [M"Unbeleuchtete Stra�e", M"Unbeleuchtete Stra�en", 0],
6195	 'gr' => [M"Gr�ner Weg", M"Gr�ne Wege", 0],
6196	 'comm' => [M"Kommentare", M"Kommentare", 0],
6197	 # XXX specific comm types?
6198	 'mount' => [M"Steigung", M"Steigungen", 0],
6199	 'wr'   => [M"Wasserroute", M"Wasserrouten", undef],
6200	 'fz'   => [M"Unbekannte Stra�e", M"Unbekannte Stra�en", 1],
6201	);
6202    if (!defined $city || $city ne 'Berlin') {
6203	$str_attrib{g}   = [M"Ortsgrenze",     M"Ortsgrenzen",     0];
6204	$str_attrib{gBO} = [M"Ortsteilgrenze", M"Ortsteilgrenzen", 0];
6205    }
6206    %p_attrib =
6207	('lsa'  => [M"Ampel",       M"Ampeln",       undef],
6208	 'u'    => [M"U-Bahnhof",   M"U-Bahnh�fe",   undef],
6209	 'u_bg' => [M"Fahrradfreundlicher Zugang (U-Bahn)",   M"Fahrradfreundliche Zug�nge (U-Bahn)",   undef],
6210	 'b'    => [M"S-Bahnhof",   M"S-Bahnh�fe",   undef],
6211	 'u_bg' => [M"Fahrradfreundlicher Zugang (S-Bahn)",   M"Fahrradfreundliche Zug�nge (S-Bahn)",   undef],
6212	 'r'    => [M"R-Bahnhof",   M"R-Bahnh�fe",   undef],
6213	 'r_bg' => [M"Fahrradfreundlicher Zugang (Regionalbahn)",   M"Fahrradfreundliche Zug�nge (Regionalbahn)",   undef],
6214	 'o'    => [M"Ort",         M"Orte",         undef],
6215	 'p'    => [M"Haltestelle", M"Haltestellen", undef],
6216	 'obst' => [M"Obst",        M"Obst",         undef],
6217	 'pl'   => [M"Platz/Br�cke",M"Pl�tze/Br�cken",undef],
6218	 'vf'   => [M"Vorfahrt",    M"Vorfahrt",     undef],
6219	 'pp'   => [M"Kreuzung",    M"Kreuzungen",   undef],
6220	 'kn'   => [M"Kneipe",      M"Kneipen",      undef],
6221	 'ki'   => [M"Kino",        M"Kinos",        undef],
6222	 'rest' => [M"Restaurant",  M"Restaurants",  undef],
6223	 'hoehe' => [M"H�henangabe", M"H�henangaben",  undef],
6224	 'personal' => [M"Pers�nlicher Ort", M"Pers�nliche Orte",  undef],
6225	 'GU'   => [M"Grenz�bergang", M"Grenz�berg�nge", undef],
6226	);
6227    %category_attrib =
6228	('UA' => [M"U-Bahn Zone A", undef, undef],
6229	 'UB' => [M"U-Bahn Zone B", undef, undef],
6230	 'U0' => [M"stillgelegte U-Bahn", undef, undef],
6231	 'UBau' => [M"U-Bahn in Bau", undef, undef],
6232	 'UBetrieb' => [M"U-Bahn, nur Betriebsfahrten", undef, undef],
6233	 'SA' => [M"S-Bahn Zone A", undef, undef],
6234	 'SB' => [M"S-Bahn Zone B", undef, undef],
6235	 'SC' => [M"S-Bahn Zone C", undef, undef],
6236	 'S0' => [M"stillgelegte S-Bahn", undef, undef],
6237	 'SBau' => [M"S-Bahn in Bau", undef, undef],
6238	 'SBetrieb' => [M"S-Bahn, nur Betriebsfahrten", undef, undef],
6239	 'RA' => [M"R-Bahn Zone A", undef, undef],
6240	 'RB' => [M"R-Bahn Zone B", undef, undef],
6241	 'RC' => [M"R-Bahn Zone C", undef, undef],
6242	 'R'  => [M"R-Bahn au�erhalb Berlin ABC", undef, undef],
6243	 'R0' => [M"stillgelegte Bahnstrecke", M"stillgelegte Bahnstrecken", undef],
6244	 'RBau' => [M"Bahnstrecke in Bau", M"Bahnstrecken in Bau", undef],
6245	 'RG' => [M"G�terbahn/Verbindungsstrecke", M"G�terbahnen/Verbindungsstrecken", undef],
6246	 'RP' => [M"Park-/Kleinbahn", M"Park-/Kleinbahnen", undef],
6247	 'HH' => [M"wichtige Hauptstra�e", M"wichtige Hauptstra�en", undef],
6248	 'B'  => [M"Bundesstra�e", M"Bundesstra�en", undef],
6249	 'H'  => [M"Hauptstra�e", M"Hauptstra�en", undef],
6250	 'N'  => [M"Nebenstra�e", M"Nebenstra�en", undef],
6251	 'NH' => [M"wichtige Nebenstra�e", M"wichtige Nebenstra�en", undef],
6252	 'NN' => [M"f�r Kfz gesperrte Stra�e", M"f�r Kfz gesperrte Stra�en", undef],
6253	 'Pl' => [M"Platz", M"Pl�tze", undef],
6254	 'BAB'=> [M"Autobahn", M"Autobahnen", undef],
6255	 'P'  => [M"Park", M"Parks", undef],
6256	 'Forest' => [M"Wald", M"W�lder", undef],
6257	 'Cemetery' => [M"Friedhof", M"Friedh�fe", undef],
6258	 'Green' => [M"Gr�nanlage", M"Gr�nanlagen", undef],
6259	 'Orchard' => [M"Kleing�rten", M"Kleing�rten", undef],
6260	 'Sport' => [M"Sportanlage", M"Sportanlagen", undef],
6261	 'Industrial' => [M"Industriegebiet", M"Industriegebiete", undef],
6262	 'Mine' => [M"Tagebau", undef, undef],
6263	 'Ae' => [M"Flughafen", M"Flugh�fen", undef],
6264	 'ex-Ae' => [M"ehemaliger Flughafen", M"ehemalige Flugh�fen", undef],
6265	 'Q0' => [M"sehr guter Belag", undef, undef,
6266		  M"sehr guter Belag (Asphalt)"],
6267	 'Q1' => [M"guter Belag", undef, undef,
6268		  M"guter Belag (Asphalt oder gutes Kopfsteinpflaster)"],
6269	 'Q2' => [M"m��iger Belag", undef, undef,
6270		  M"m��iger Belag (schlechter Asphalt oder m��iges Kopfsteinpflaster)"],
6271	 'Q3' => [M"schlechter Belag", undef, undef,
6272		  M"schlechter Belag (Katzenkopfsteinpflaster oder unbefestigte Wege)"],
6273	 'q0' => [M"keine", undef, undef,
6274		  M"keine Beeintr�chtigungen"],
6275	 'q1' => [M"auf ca. 25 km/h", undef, undef,
6276		  M"Beeintr�chtigungen auf ca. 25 km/h"],
6277	 'q2' => [M"auf ca. 18 km/h", undef, undef,
6278		  M"Beeintr�chtigungen auf ca. 18 km/h"],
6279	 'q3' => [M"auf ca. 13 km/h", undef, undef,
6280		  M"Beeintr�chtigungen auf ca. 13 km/h"],
6281	 'q4' => [M"auf Schrittgeschwidigkeit", undef, undef,
6282		  M"Beeintr�chtigungen auf Schrittgeschwindigkeit"],
6283
6284	 '6'  => [M"Gro�- oder Millionenstadt", M"Gro�- oder Millionenst�dte", undef],
6285	 '5'  => [M"Gro�stadt", M"Gro�st�dte", undef],
6286	 '4'  => [M"Ortskategorie 4", M"Ortskategorie 4", undef],
6287	 '3'  => [M"Ortskategorie 3", M"Ortskategorie 3", undef],
6288	 '2'  => [M"Ortskategorie 2", M"Ortskategorie 2", undef],
6289	 '1'  => [M"kleiner Ort", M"kleine Orte", undef],
6290	 '0'  => [M"Ortsteil", M"Ortsteile", undef],
6291	 'Zbr'=> [M"Zugbr�cke", M"Zugbr�cken", undef],
6292	 'Br' => [M"Br�cke", M"Br�cken", undef],
6293	 'Tu' => [M"Tunnel", M"Tunnel", undef],
6294	 'CS' => [M"streckenbezogener Kommentar", M"streckenbezogene Kommentare", undef],
6295	 'CP' => [M"punktbezogener Kommentar (A-B-C)", M"punktbezogene Kommentare (A-B-C)", undef],
6296	 'CP2'=> [M"punktbezogener Kommentar (A-B)", M"punktbezogene Kommentare (A-B)", undef],
6297	 'PI' => [M"genaue Wegbeschreibung", undef, undef],
6298	 '-2' => [M"relativ sehr ruhiger Kfz-Verkehr", undef, undef],
6299	 '-1' => [M"relativ ruhiger Kfz-Verkehr", undef, undef],
6300	 '+1' => [M"relativ starker Kfz-Verkehr", undef, undef],
6301	 '+2' => [M"relativ sehr starker Kfz-Verkehr", undef, undef],
6302	 'St' => [M"Steigung", M"Steigungen", undef],
6303	 'Gf' => [M"Gef�lle", M"Gef�lle", undef],
6304	 'Z'  => [M"Grenze", M"Grenzen", undef],
6305	 'Q'  => [M"F�hre", M"F�hren", undef],
6306	 'green1' => [M"gr�ner Weg", M"gr�ne Wege", undef],
6307	 'green2' => [M"besonders gr�ner Weg", M"besonders gr�ne Wege", undef],
6308	 'HNR'=> [M"Hausnummer", M"Hausnummern", undef],
6309	 'NL' => [M"unbeleuchtete Stra�e", M"unbeleuchtete Stra�en", undef],
6310	 'SW' => [M"Sehensw�rdigkeit", M"Sehensw�rdigkeiten", undef],
6311	 'I'  => [M"Insel", M"Inseln", undef],
6312	 'W'  => [M"Gew�sser, nicht kategorisiert", undef, undef],
6313	 'W0' => [M"unwichtiges Gew�sser", undef, undef],
6314	 'W1' => [M"Gew�sser", undef, undef],
6315	 'W2' => [M"gr��eres Gew�sser", undef, undef],
6316	 'WR' => [M"Wasserroute", M"Wasserrouten", undef],
6317	 'radroute' => [M"Radroute", M"Radrouten", undef],
6318	);
6319    foreach my $cat (@area_cats) {
6320	my $cat_above = $cat . 'above';
6321	if (exists $category_attrib{$cat} && !exists $category_attrib{$cat_above}) {
6322	    $category_attrib{$cat_above} = $category_attrib{$cat};
6323	}
6324    }
6325    foreach (@Radwege::category_order) {
6326	if (defined $Radwege::category_code{$_}) {
6327	    $category_attrib{$Radwege::category_code{$_}} =
6328		[$Radwege::category_name{$_}, $Radwege::category_plural{$_}, undef];
6329	}
6330    }
6331
6332    %obst_file =
6333	('apfel'   => 'apfel',
6334	 'kirsche' => 'kirsche',
6335	 'birne'   => 'birne',
6336	 'pflaume' => 'pflaume',
6337	);
6338
6339    # f�r Orte und Sonstiges
6340    $xadd_anchor_type->{'o'} = {'w' => 4, 'n' => 0, 'e' => -4, 's' => 0,
6341				'nw' => 2, 'sw' => 2};
6342    $yadd_anchor_type->{'o'} = {'w' => 0, 'n' => 1, 'e' => 0,  's' => -1,
6343				'nw' => 1, 'sw' => -1};
6344    $label_spaceadd{'o'} = " ";
6345
6346    # f�r Routen
6347    $xadd_anchor_type->{'route'} = {'w' => 10, 'n' => 0, 'e' => -10, 's' => 0,
6348				    'nw' => 5, 'sw' => 5};
6349    $yadd_anchor_type->{'route'} = {'w' => 0, 'n' => 10, 'e' => 0,  's' => -10,
6350				    'nw' => 5, 'sw' => -5};
6351    # $label_spaceadd not needed here
6352
6353    # U-Bahnsymbole (auch S-Bahn, R-Bahn etc.)
6354    # XXX This should be variable depending on the drawn icon (normal, klein, mini)
6355    $xadd_anchor_type->{'u'} = {'w' => 9, 'n' => 0, 'e' => -9, 's' => 0,
6356				'nw' => 5, 'sw' => 5};
6357    $yadd_anchor_type->{'u'} = {'w' => 0, 'n' => 9, 'e' => 0,  's' => -9,
6358				'nw' => 5, 'sw' => -5};
6359    $label_spaceadd{'u'} = "  ";
6360
6361    # Sehensw�rdigkeiten (star)
6362    $xadd_anchor_type->{'v'} = {'w' => 8, 'n' => 0, 'e' => -8, 's' => 0,
6363				'nw' => 5, 'sw' => 5};
6364    $yadd_anchor_type->{'v'} = {'w' => 0, 'n' => 8, 'e' => 0,  's' => -8,
6365				'nw' => 5, 'sw' => -5};
6366    $label_spaceadd{'v'} = "  ";
6367
6368    %tag_group =		# group related tags (for stacking)
6369	('str_s' => ['s-out', 'gr', 'rw',
6370		     's-NN', 's-N', 's-NH', 's-H', 's-HH', 's-B', 's-BAB', 'sBAB-BAB', 'sBAB-fg',
6371		     'comm', (map { "comm-$_" } @comments_types),
6372		     'nl', 'qs', 'hs', 'mount',
6373		     's-label-bg', 'sBAB-label-bg', 's-label', 'sBAB-label',
6374		     'hoehe', 'vf-bg',
6375		     'sperre', 'temp_sperre_s', 'temp_sperre',
6376		     'delnet', 'pl-fg', 'lsa-bg', 'vf-fg', 'lsa-fg'],
6377	 'str_l' => ['l-out', 'l', 'comm',
6378		     (map { "comm-$_" } @comments_types),
6379		     'ql', 'hl', 'l-label-bg', 'l-label'], # XXX mount?
6380	 'p_o'   => ['o', 'O'],
6381	 'p_p'   => ['p'],
6382	 'str_u' => ['u', 'sperre_u', 'u-bg', 'u-fg', 'u_bg-img', 'u-label'],
6383	 'str_b' => ['b', 'sperre_b', 'b-bg', 'b-fg', 'b_bg-img', 'b-label'],
6384	 'str_r' => ['r', 'sperre_r', 'r-bg', 'r-fg', 'r_bg-img', 'r-label'],
6385	 'str_w' => ['w-out', 'w', 'i-out', 'i', 'w-label-bg', 'w-label', 'i-label-bg', 'i-label'],
6386	 'str_f' => ['f', 'f-label-bg', 'f-label', 'f-Pabove'],
6387	 'str_g' => ['z', 'g', 'gBO', 'gP', 'gD', 'gBO-label-bg', 'gBO-label', 'GU-img'],
6388	 'p_kn'  => ['kn', 'kn-bg', 'kn-fg', 'ki', 'ki-bg', 'ki-fg', 'rest', 'rest-bg', 'rest-fg'],
6389	 'map'   => ['map'],
6390	 'route' => ['route'],
6391	 'v'     => ['v', 'v-fg'],
6392	 'e'     => ['e', 'e-img'],
6393	);
6394
6395    # normale Reihenfolge f�r das �bereinanderlegen bei restack()
6396    #XXX labels sollten grunds�tzlich immer oben sein. Problematisch bei tag_groups
6397    # tags in the form '*...*' are special and used just as markers
6398    @normal_stack_order =
6399	(qw(map f w-out w i-out i f-Pabove *landuse* e e-img
6400	    gP gD z g gP gD gBO
6401	    s-out l-out show gr rw s-NN s-N s-NH s-H s-HH s-B s-BAB sBAB sBAB-BAB sBAB-fg l v
6402	    f-label-bg wr w-label-bg gBO-label-bg f-label w-label i-label gBO-label
6403	    u sperre_u u-bg u-fg u_bg-img r sperre_r b sperre_b
6404	    r-bg r-fg r_bg-img b-bg b-fg b_bg-img GU-img
6405	    u-label r-label b-label
6406	    hoehe vf-bg sperre temp_sperre_s temp_sperre v-fg obst
6407	    fz *route* route gps_track comm),
6408	 (map { "comm-$_" } @comments_types),
6409	 qw(comm-route-label-bg comm-route-label qs hs ql hl mount nl delnet
6410	    crosshairs
6411	    O o p pl-fg vf-fg lsas lsa-bg lsa-fg lsas-t
6412	    pp kn-bg kn-fg ki-bg ki-fg rest-bg rest-fg
6413	    fz-label s-label-bg sBAB-label-bg s-label sBAB-label l-label-bg l-label
6414	    personal-fg personal-label ovl
6415	    gpsanimrect zoomrect),
6416	);
6417    %comment_cat_labels =
6418	(ferry => M"Informationen zu F�hren",
6419	 misc => M"Sonstige Kommentare",
6420	 path => M"Wegf�hrung",
6421	 route => M"Radrouten",
6422	 tram => M"Tram auf Fahrbahn",
6423	 kfzverkehr => M"Kommentare zum Kfz-Verkehr",
6424	 scenic => M"Sch�ne Strecken",
6425	 danger => M"Gef�hrliche Stellen",
6426	);
6427}
6428
6429sub generate_plot_functions {
6430    $plotstr_draw_sub = <<'EOF';
6431        sub {
6432	    my $ret = shift;
6433	    my $strname = $ret->[Strassen::NAME];
6434	    my @kreuzungen = @{$ret->[Strassen::COORDS]};
6435            @kreuzungen = map { $conv->($_) } @kreuzungen
6436		if $conv;
6437	    my $cat_hin = $ret->[Strassen::CAT];
6438	    my $cat_rueck;
6439	    my(@addinfo_hin, @addinfo_rueck);
6440	    if ($cat_hin =~ /^(.*);(.*)$/) {
6441		($cat_hin, $cat_rueck) = ($1, $2);
6442	    }
6443	    if ($cat_hin =~ /^(.+?)::(.*)$/) { # XXX will change
6444		$cat_hin = $1;
6445		@addinfo_hin = split ':', $2;
6446	    }
6447	    if (defined $cat_rueck && $cat_rueck =~ /^(.+?)::(.*)$/) { # XXX this will change!
6448		$cat_rueck = $1;
6449		@addinfo_rueck = split ':', $2;
6450	    }
6451# XXX Problems with cat = ";anything": $cat_hin is empty and thus always
6452# restricted. Workaround: always use "anything;" with the reversed
6453# coord list. But nevertheless $ignore and $restrict won't work correctly.
6454	    return if defined $ignore and $cat_hin =~ /$ignore/;
6455	    return if defined $restrict and $cat_hin !~ /$restrict/;
6456	    if (!$edit_normal_mode) { # we want to see everything in edit mode
6457	        return if first { $_ eq "igndisp" } @addinfo_hin;
6458	    }
6459	    my $this_color_hin = $cat_hin =~ /^\#/ ? $cat_hin :
6460		($layer_category_color{$abk} && $layer_category_color{$abk}{$cat_hin})
6461		|| $category_color{$cat_hin}
6462		|| $str_color{$abk} || 'white';
6463	    my $this_color_rueck = defined $cat_rueck ?
6464		($cat_rueck =~ /^\#/ ? $cat_rueck :
6465		 ($layer_category_color{$abk} && $layer_category_color{$abk}{$cat_rueck})
6466		 || $category_color{$cat_rueck}
6467		 || $str_color{$abk} || 'white') :
6468		     'white';
6469	    my $this_width_hin = ($layer_category_line_width{$abk} && $layer_category_line_width{$abk}{$cat_hin})
6470		 || $category_width{$cat_hin} || $default_width || 1;
6471	    my $this_width_rueck = defined $cat_rueck ?
6472		(($layer_category_line_width{$abk} && $layer_category_line_width{$abk}{$cat_rueck}) || $category_width{$cat_rueck} || $default_width || 1) :
6473		    1;
6474	    my @coordlist;
6475	CROSSINGS_LOOP:
6476	    foreach (@kreuzungen) {
6477	      TRY: {
6478		    my($xx, $yy);
6479		    if (!$edit_mode && !$edit_mode_flag) {
6480			($xx, $yy) = split /,/, $_;
6481			if (!defined $yy) { # ignore invalid coords like "*"
6482			    next CROSSINGS_LOOP;
6483			}
6484                    } elsif ($edit_mode_flag) {
6485                        /^(?::.*:)?(-?[\d\.]+),(-?[\d\.]+)$/;
6486                        ($xx, $yy) = ($1, $2);
6487                        next CROSSINGS_LOOP if !defined $yy;
6488		    } elsif ($edit_mode &&
6489			     /([A-Za-z]+)?(-?[\d\.]+),(-?[\d\.]+)$/) {
6490			# XXX Verwendung von data/BASE (hier und �berall)
6491			my $this_coordsys = (defined $1 ? $1 : '');
6492			if ($this_coordsys eq $coordsys ||
6493			    (!($this_coordsys ne '' || $coordsys ne 'B'))) {
6494			    ($xx, $yy) = ($2, $3);
6495                        } else {
6496			    # the hard way: convert it
6497			    $this_coordsys = 'B' if $this_coordsys eq '';
6498			    ($xx,$yy) = $Karte::map_by_coordsys{$this_coordsys}->map2map($coord_system_obj, $2, $3);
6499#warn "($xx,$yy)";
6500			}
6501		    } else {
6502			last TRY;
6503		    }
6504		    push @coordlist, $transpose->($xx, $yy);
6505		    if ($p_draw{'pp'} && ($p_sub_draw{"pp-$abk"}||$p_sub_draw{"pp-all"})) {
6506			my($x, $y) = @coordlist[$#coordlist-1 .. $#coordlist];
6507			my $pp_cross_or_kurve_tag;
6508## This is not correct and therefore not used.
6509## The net should be plain without "gesperrt"/"oneway" bits.
6510# 			if ($net && $net->{Net}) {
6511# 			    if (scalar(keys(%{$net->{Net}{"$xx,$yy"}})) < 3) {
6512# 				$pp_cross_or_kurve_tag = "ppkvp";
6513# 			    } else {
6514# 				$pp_cross_or_kurve_tag = "ppcrs";
6515# 			    }
6516# 			}
6517			# keine Verwendung von _coord_as_string
6518			$c->createLine
6519			  ($x, $y, $x, $y,
6520			   -tags => ['pp', "$xx,$yy", undef, "pp-$abk",
6521				     ($pp_cross_or_kurve_tag ? $pp_cross_or_kurve_tag : ())],
6522			  );
6523		    }
6524		}
6525	    }
6526	    if (@coordlist > 0) {
6527		my $abk = $abk;
6528		my($mx,$my);
6529		my $image;
6530		my $anchor = "c";
6531		my $category = $cat_hin; # used for undirected things
6532		my $item; # canvas item drawn
6533
6534		my $line_shorten_hin = ($layer_category_line_shorten{$abk} && $layer_category_line_shorten{$abk}{$cat_hin}) || $layer_line_shorten{$abk} || $category_line_shorten{$cat_hin} || $line_shorten{$abk};
6535		if (defined $line_shorten_hin) { # XXX no $cat_rueck handling
6536		    line_shorten(\@coordlist);
6537		} else {
6538		    my $line_shorten_end_hin = ($layer_category_line_shorten_end{$abk} && $layer_category_line_shorten_end{$abk}{$cat_hin}) || $layer_line_shorten_end{$abk} || $category_line_shorten_end{$cat_hin} || $line_shorten_end{$abk};
6539		    if (defined $line_shorten_end_hin) { # XXX no $cat_rueck handling
6540		        line_shorten_end(\@coordlist);
6541		    }
6542		}
6543
6544	        if ($layer_category_image{$abk} && defined $layer_category_image{$abk}{$cat_hin}) {
6545		    $cat_hin = "IMG:$layer_category_image{$abk}{$cat_hin}";
6546	        } elsif (defined $category_image{$cat_hin}) {
6547		    $cat_hin = "IMG:$category_image{$cat_hin}";
6548	        }
6549
6550		my $sight_draw = sub {
6551		    # speciality for sights: draw a star
6552		    if (!defined $mx) {
6553			if (@coordlist > 2) {
6554			    ($mx,$my) = get_polygon_center(@coordlist);
6555			}
6556			if (!defined $mx) {
6557			    ($mx,$my) = @coordlist[0,1];
6558			}
6559		    }
6560		    if ($image) {
6561			if (!$photo{$image}) {
6562			    my $f;
6563			    for my $subdir ("images", "data") {
6564			        $f = maybe_expand_image_file($image, $str_file{$abk}, $subdir);
6565			        if ($f && -r $f) {
6566				    $photo{$image} = image_from_file($top, $f);
6567				    last;
6568			        }
6569			    }
6570			    if (!$photo{$image}) {
6571				warn "Can't find photo $image (1)";
6572			    }
6573			}
6574			if ($photo{$image}) {
6575			    $c->createImage($mx,$my,-image => $photo{$image},
6576					    -anchor => $anchor,
6577					    -tags => ["$abk-fg", $strname]);
6578			} else {
6579			    warn "No image for $image";
6580			}
6581		    } else {
6582			$c->createImage($mx,$my,-image => $star_photo,
6583					-tags => ["$abk-fg", $strname]);
6584		    }
6585		};
6586
6587		my $draw_strname_for_area = sub {
6588		    my($name, $add) = split(/\|/, $strname);
6589		    $name = "" if !defined $name;
6590		    ## The addition is mostly for missing geographic context; not necessary when drawing
6591		    #if ($add) {
6592		    #    $name .= " $add";
6593		    #}
6594		    $name =~ s/\cK/\n/g; # vert tab -> newline
6595		    ($mx,$my) = get_polygon_center(@coordlist);
6596		    if (!defined $mx || ! do {
6597		        my @zipped_coordlist;
6598		        for(my $i = 0; $i < $#coordlist; $i+=2) {
6599		    	push @zipped_coordlist, [$coordlist[$i], $coordlist[$i+1]];
6600		        }
6601		        point_in_polygon([$mx,$my], \@zipped_coordlist);
6602		    }) {
6603		        my $middle = int $#coordlist/2;
6604		        if ($middle%2 != 0) {
6605		    	$middle--;
6606		        }
6607		        ($mx,$my) = @coordlist[$middle,$middle+1];
6608		    }
6609
6610		    my $abk_fg = $abk;
6611		    if ($abk eq 'v') {
6612		        $abk_fg = 'v-fg';
6613		    } elsif ($abk =~ /^(?:[fw]|gBO)$/) {
6614		        $abk_fg = $abk."-label";
6615		    }
6616		    my $tags = [$abk_fg, $strname];
6617		    my %args = (-text => $name,
6618		    	    -tags => $tags,
6619		    	    -outlinewidth => 2,
6620		    	    (exists $category_font_color{$category} ? (-fill => $category_font_color{$category}) : ()),
6621		    	    (exists $category_font{$category} ? (-font => $category_font{$category}) : ()),
6622		    	   );
6623		    if (exists $category_font{$category} &&
6624		        $category_font{$category} =~ /%d/) {
6625		        my $bbox_area = get_bbox_area($item);
6626		        # XXX bessere Abstufungen
6627		        if ($bbox_area < 1500) {
6628		    	$args{-font} = sprintf $category_font{$category}, 7;
6629		        } elsif ($bbox_area > 5000) {
6630		    	$args{-font} = sprintf $category_font{$category}, 12;
6631		        } else {
6632		    	$args{-font} = sprintf $category_font{$category}, 10;
6633		        }
6634		    }
6635
6636		    if (!$no_overlap_label{$abk} ||
6637		        !draw_text_intelligent
6638		            ($c, $mx, $my,
6639		    	 %args,
6640		    	 -abk  => $abk_fg,
6641		    	 -xadd => $xadd_anchor,
6642		    	 -yadd => $yadd_anchor,
6643		    	 -outline => 1,
6644		    	)) {
6645		        my($mx,$my) = ($mx,$my);
6646		        if (defined $label_spaceadd) {
6647		            $args{-text} = $label_spaceadd . $args{-text};
6648		    	$args{-anchor} = "w";
6649		        } elsif (# shift to right for points,
6650		    	     # center for polygons
6651		    	     @coordlist == 2 || $abk eq 'v') {
6652		            $mx += $xadd_anchor->{'w'};
6653		            $my += $yadd_anchor->{'w'};
6654		    	$args{-anchor} = "w";
6655		        }
6656		        outline_text($c, $mx, $my, %args);
6657		    }
6658		};
6659
6660		my $draw_street_photo = sub {
6661			my($street_photo, $anchor, $delta, %opts) = @_;
6662			    my $addtag = delete $opts{-addtag};
6663			    my($mx,$my) = get_polyline_center(@coordlist);
6664
6665			    if ($delta) {
6666				# atan2(y2-y1, x2-x1)
6667				my $ii = 2; # second point
6668				my $alpha = atan2($coordlist[$ii+1]-$coordlist[$ii-1], $coordlist[$ii]-$coordlist[$ii-2]);
6669				my $beta  = $alpha - pi()/2;
6670				my($dx, $dy) = (-$delta*cos($beta), -$delta*sin($beta));
6671				$mx += $dx;
6672				$my += $dy;
6673			    }
6674
6675			    $c->createImage($mx,$my,
6676					    -anchor => $anchor,
6677					    -image => $street_photo,
6678					    # $abk-img or $abk-fg ?
6679					    -tags => [$abk,$strname,"$abk-img",
6680						      "$abk-" . $i,
6681						      ($addtag ? $addtag : ()),
6682						     ]);
6683			    if ($street_photo eq $steigung_photo) {
6684				if ($strname =~ /([\d\.]+)\s*%/) {
6685				    outline_text
6686					($c,
6687					 $mx, $my,
6688					 -anchor => "n",
6689					 -text => "$1%",
6690					 -font => $font{'small'},
6691					 -tags => [$abk,$strname,"$abk-fg",
6692						   "$abk-" . $i,
6693						   ($addtag ? $addtag : ()),
6694						  ],
6695					 -outlinewidth => 2,
6696					);
6697				}
6698			    }
6699		};
6700
6701		if ($cat_hin =~ /^F:(.*)$/) { # Fl�che, no $cat_rueck handling here
6702		    $category = $1;
6703		    my($color, $rest) = split(/\|/, $category, 2);
6704		    my $stipple = ($layer_category_stipple{$abk} && $layer_category_stipple{$abk}{$category}) || $layer_stipple{$abk} || $category_stipple{$category};
6705		    if (defined $rest && $rest ne "") {
6706			if ($rest =~ $complex_IMG_qr) {
6707			    $image = $1;
6708			    $anchor = $2 if $2;
6709			} elsif (!defined $stipple) {
6710			    $stipple = $rest;
6711			}
6712		    }
6713		    if ($color eq 'I') { $abk = 'i' } # Inseln
6714		    $color = ($layer_category_color{$abk} && $layer_category_color{$abk}{$color}) || $category_color{$color} || $color;
6715		    $stipple = load_stipple($stipple) if $stipple;
6716		    if ($str_outline{$abk} && @coordlist > 2) {
6717			$item = $c->createPolygon
6718			  (@coordlist,
6719			   -fill    => $outline_color{$abk},
6720			   -outline => $outline_color{$abk},
6721			   -width   => 2,
6722			   -tags    => ["$abk-out", "$abk-$category-out"],
6723			  );
6724		    }
6725		    if (@coordlist == 2) {
6726			# dicken Punkt zeichnen
6727			$item = $c->createLine
6728			    (@coordlist, @coordlist,
6729			     -fill => $color,
6730			     -width => 5, # XXX skalieren
6731			     -capstyle => $capstyle_round,
6732			     -tags => [$abk, $strname, $kreuzungen[0],
6733				       $abk."-".$i
6734				      ],
6735			    );
6736		    } else {
6737			$item = $c->createPolygon
6738			    (@coordlist,
6739			     -fill    => $color,
6740			     ($stipple ? (-stipple => $stipple) : ()),
6741			     -tags    => [$abk, $strname,
6742					  "$abk-$category",$abk."-".$i],
6743			    );
6744		    }
6745
6746		    if ($str_name_draw{$abk}) {
6747			$draw_strname_for_area->();
6748		    }
6749
6750		    if (($abk eq 'v' && $star_photo) || $image) {
6751			$sight_draw->();
6752		    }
6753
6754		} elsif ($cat_hin =~ $complex_IMG_qr) {
6755		    my $img_spec = $1;
6756		    my $anchor = ($2 ? $2 : "c");
6757		    my $p;
6758		    my $img = maybe_expand_image_file($img_spec, $str_file{$abk}, "data");
6759		    if (!$img) {
6760			# XXX get_image_for_str is actually more powerful, and should maybe replace maybe_expand_image_file+image_from_file?
6761			$p = get_image_for_str($img_spec, $img_spec, $abk);
6762		    } else {
6763			$p = image_from_file($top, $img);
6764		    }
6765		    # XXX this is leaking (photo never deleted...)
6766		    # XXX $abk-XXX => $abk-fg or $abk-img ?
6767		    # XXX use $abk-fg for now (scaling works!)
6768		    if ($p) {
6769			$item = $c->createImage(@coordlist[0..1],
6770					-image => $p,
6771					-anchor => $anchor,
6772					-tags => [$abk, $strname,
6773						  "$abk-fg", "$abk-" . $i],
6774				       );
6775		    } else {
6776			warn "Can't find photo $img (2)";
6777		    }
6778		} elsif ($use_stippleline == 1) { # old stipple code
6779		    # XXX no $cat_rueck handling here (this code branch is anyway obsolete)
6780		    # min. 4 Koordinaten erzwingen
6781		    @coordlist == 2 && push(@coordlist, @coordlist);
6782
6783		    Tk::StippleLine::create
6784		      ($c, @coordlist,
6785		       -fill => $this_color_hin,
6786		       -width => $this_width_hin,
6787		       -joinstyle => 'bevel',
6788		       -tags => [$abk, $strname,
6789				 "$abk-$cat_hin", "$abk-" . $i],
6790		      );
6791
6792		} else { # points or lines
6793		    if (@coordlist == 2) { # point
6794			# Points do not have $cat_rueck
6795			if ($abk eq 'v') {
6796			TRY_IMAGE: {
6797				if ($cat_hin =~ /\|IMG:([^|]+)/) {
6798				    $image = $1;
6799				} elsif ($star_photo) {
6800				    $image = undef; # default to $star_photo
6801				} else {
6802				    last TRY_IMAGE;
6803				}
6804				$sight_draw->();
6805				return; # next loop
6806			    }
6807			} elsif ($achtung_photo && grep { $_ eq 'danger' } @addinfo_hin) {
6808			    $draw_street_photo->($achtung_photo, "c");
6809			} elsif ($abk eq 'w' && $cat_hin eq 'I' && $strname ne '') {
6810			    # only draw label
6811			    # XXX quick hack, really only needed for osm islands
6812			    my %args = (-text => $strname,
6813					-tags => ["i-label", $strname],
6814					(exists $category_font_color{$category} ? (-fill => $category_font_color{$category}) : ()),
6815				    	(exists $category_font{$category} ? (-font => $category_font{$category}) : ()),
6816					-font => sprintf($category_font{$cat_hin}, 10),
6817					-outlinewidth => 2);
6818			    outline_text($c, @coordlist, %args);
6819			    return;
6820			} elsif ($cat_hin =~ $roundabout_qr) {
6821			    $draw_street_photo->($kreisverkehr_photo, "c");
6822			} elsif ($cat_hin =~ $viewangle_qr) {
6823			    my($start,$extent) = ($1,$2);
6824			    my $radius = get_line_width("$abk-View");
6825			    my @coords = ((map { $_-$radius } @coordlist),
6826			    		  (map { $_+$radius } @coordlist));
6827			    my @common_args = (-outline => undef,
6828					       -fill => "red",
6829			    		       -tags => [$abk, $strname,
6830#XXX fix category from View:...:... to View?
6831						        "$abk-View", "$abk-" . $i,
6832						        @extra_tags],
6833					      );
6834			    if (abs($extent) < 30) {
6835			        $c->createArc(@coords, @common_args,
6836	       		    		      -start => $start,
6837			    		      -extent => $extent,
6838				             );
6839			    } else {
6840				my $delta = $extent > 0 ? 30 : -30;
6841				my $end   = $start + $extent;
6842				for(my $_start = $start; $extent > 0 ? $_start < $end : $_start > $end; $_start+=$delta) {
6843				    $c->createArc(@coords, @common_args,
6844						  -start => $_start,
6845						  -extent => $delta/2,
6846						 );
6847				}
6848			    }
6849			    return; # next loop
6850			}
6851
6852			# dicken Punkt zeichnen
6853			my $width = $category_point_size{$cat_hin} || 5; # XXX skalieren
6854			$item = $c->createLine(@coordlist, @coordlist,
6855				       -fill => $this_color_hin,
6856				       -width => $width,
6857				       -capstyle => $capstyle_round,
6858				       -tags => [$abk, $strname,
6859						 $abk."-".$cat_hin, $abk."-".$i,
6860						 @extra_tags],
6861				      );
6862		    } else { # lines
6863			my @std_tags_hin = ($abk, $strname,$abk."-".$cat_hin,$abk."-".$i);
6864			my @std_tags_rueck;
6865			my $line_dash_hin = (exists $layer_category_line_dash{$abk} && $layer_category_line_dash{$abk}{$cat_hin}) || $layer_line_dash{$abk} || $category_dash{$cat_hin} || $line_dash{$abk};
6866			my $line_dash_rueck;
6867			my $line_capstyle_hin = (exists $layer_category_capstyle{$abk} && $layer_category_capstyle{$abk}{$cat_hin}) || $layer_line_capstyle{$abk} || $category_capstyle{$cat_hin} || $line_capstyle{$abk};
6868			my $line_capstyle_rueck;
6869			if ($cat_rueck) {
6870			    @std_tags_rueck = @std_tags_hin;
6871			    $std_tags_rueck[2] = "$abk-$cat_rueck";
6872			    $line_dash_rueck = (exists $layer_category_line_dash{$abk} && $layer_category_line_dash{$abk}{$cat_rueck}) || $layer_line_dash{$abk} || $category_dash{$cat_rueck} || $line_dash{$abk};
6873			    $line_capstyle_rueck = (exists $layer_category_capstyle{$abk} && $layer_category_capstyle{$abk}{$cat_rueck}) || $layer_line_capstyle{$abk} || $category_capstyle{$cat_rueck} || $line_capstyle{$abk};
6874			}
6875			my $stipple = ($layer_category_stipple{$abk} && $layer_category_stipple{$abk}{$category}) || $layer_stipple{$abk} || $category_stipple{$category};
6876			$stipple = load_stipple($stipple) if $stipple;
6877		        if (@addinfo_hin) { # ignore @addinfo_rueck for now
6878			    for my $addinfo_hin (@addinfo_hin) {
6879			        if ($addinfo_hin =~ $tunnel_qr) {
6880				    $line_dash_hin = $line_dash{"Tu"};
6881				    $line_dash_rueck = $line_dash_hin if defined $line_dash_hin;
6882				    draw_tunnel_entrance(\@coordlist, -width => $this_width_hin+4, -tags => \@std_tags_hin, -mounds => $addinfo_hin);
6883				} elsif ($addinfo_hin eq 'Br') {
6884				    draw_bridge(\@coordlist, -width => $this_width_hin+4, -tags => \@std_tags_hin);
6885				}
6886			    }
6887			}
6888			if (!$use_stippleline) {
6889			    undef $line_dash_hin;
6890			}
6891			if ($str_outline{$abk}) {
6892			    # XXX no $cat_rueck support yet for outlines
6893			    $c->createLine
6894			      (@coordlist,
6895			       -fill      => $outline_color{$abk},
6896			       -width     => $this_width_hin+2,
6897			       -joinstyle => 'bevel',
6898			       -tags      => ["$abk-out",
6899					      "$abk-$cat_hin-out"],
6900                               ($line_dash_hin ? (-dash => $line_dash_hin) : ()),
6901			       ($line_capstyle_hin ? (-capstyle => $line_capstyle_hin) : ()),
6902			       ($stipple ? (-stipple => $stipple) : ()),
6903			       (exists $layer_category_line_arrow{$abk} && exists $layer_category_line_arrow{$abk}{$cat_hin} ? (-arrow => $layer_category_line_arrow{$abk}{$cat_hin})
6904				: exists $layer_line_arrow{$abk} ? (-arrow => $layer_line_arrow{$abk})
6905				: exists $category_line_arrow{$cat_hin} ? (-arrow => $category_line_arrow{$cat_hin}) : ()),
6906			      );
6907			}
6908			if (defined $cat_rueck) {
6909			    my $delta = $this_width_hin/2; # XXX need a better rule for this
6910			    my($cl_hin,$cl_rueck) = offset_line(\@coordlist, $delta, $cat_hin ne '', $cat_rueck ne '');
6911			    for my $dir (1, -1) {
6912				my($cl, $this_color, $this_width, $cat, $std_tags, $line_dash, $line_capstyle);
6913				if ($dir == 1 && $cat_hin ne '') {
6914				    $this_color = $this_color_hin;
6915				    $this_width = $this_width_hin/2;
6916				    $cat        = $cat_hin;
6917				    $cl         = $cl_hin; # XXX del: [@coordlist];
6918				    $std_tags   = \@std_tags_hin;
6919				    $line_dash  = $line_dash_hin;
6920				    $line_capstyle = $line_capstyle_hin;
6921				} elsif ($dir == -1 && $cat_rueck ne '') {
6922				    $this_color = $this_color_rueck;
6923				    $this_width = $this_width_rueck/2;
6924				    $cat        = $cat_rueck;
6925				    $cl         = [];
6926				    for(my $cl_i = $#$cl_rueck-1; $cl_i >= 0; $cl_i-=2) {
6927					push @$cl, @{$cl_rueck}[$cl_i, $cl_i+1];
6928				    }
6929				    $std_tags   = \@std_tags_rueck;
6930				    $line_dash  = $line_dash_rueck;
6931				    $line_capstyle = $line_capstyle_rueck;
6932				} else {
6933				    next;
6934				}
6935#				my $delta = -$this_width;
6936#
6937#				for(my $ii = 2; $ii < $#$cl; $ii+=2) {
6938#				    # atan2(y2-y1, x2-x1)
6939#				    my $alpha = atan2($cl->[$ii+1]-$cl->[$ii-1], $cl->[$ii]-$cl->[$ii-2]);
6940#				    my $beta  = $alpha - pi()/2;
6941#				    my($dx, $dy) = ($delta*cos($beta), $delta*sin($beta));
6942#				    $cl->[$ii] += $dx;
6943#				    $cl->[$ii+1] += $dy;
6944#				    if ($ii == 2) {
6945#					$cl->[0] += $dx;
6946#					$cl->[1] += $dy;
6947#				    }
6948#				}
6949				$c->createLine
6950				    (@$cl,
6951				     -fill  => $this_color,
6952				     -width => $this_width,
6953				     -joinstyle => 'bevel',
6954				     -tags  => [@$std_tags,
6955						@extra_tags],
6956				     ($line_dash ? (-dash => $line_dash) : ()),
6957				     ($line_capstyle ? (-capstyle => $line_capstyle) : ()),
6958				     ($stipple ? (-stipple => $stipple) : ()),
6959				     #(exists $category_line_arrow{$cat} ? (-arrow => $category_line_arrow{$cat}) : ()),
6960				     # XXX Tk problem? bad rendering with capstyle=>"round" and arrow=>something
6961				     -arrow => ($line_capstyle && $line_capstyle eq 'round' ? "none" : "last"),
6962				    );
6963
6964				# Draw an extra point indicating the point of action for CP/CP2 items
6965				if ($cat_hin =~ m{^( CP | CP2 | PI )$}x) {
6966				    my @center = $cat_hin eq 'CP' ? @{$cl}[2,3] : @{$cl}[0,1];
6967				    $c->createOval((map { $_-5 } @center), (map { $_+5 } @center), # XXX skalieren
6968					-outline => $this_color_hin,
6969					-width => 2,
6970					-tags => [@$std_tags, @extra_tags],
6971				    );
6972				}
6973
6974			    }
6975			} elsif ($cat_hin eq 'Br') {
6976			    draw_bridge(\@coordlist, -width => $this_width_hin+4, -tags => \@std_tags_hin);
6977			} elsif ($cat_hin =~ $roundabout_qr) {
6978			    if ($edit_normal_mode) {
6979				$c->createLine(@coordlist,
6980					       -fill  => 'blue',
6981					       -width => 2,
6982					       -tags  => [@std_tags_hin, @extra_tags],
6983					       -dash  => [1,4],
6984					      );
6985				$draw_street_photo->($kreisverkehr_photo, "c");
6986			    } else {
6987				# ignore lined roundabouts in renderer
6988				return;
6989			    }
6990			} elsif ($cat_hin =~ $tunnel_qr) {
6991			    draw_tunnel_entrance(\@coordlist, -width => $this_width_hin+4, -tags => \@std_tags_hin, -mounds => $cat_hin);
6992			} else {
6993			    $item = $c->createLine
6994				(@coordlist,
6995				 -fill      => $this_color_hin,
6996				 -width     => $this_width_hin,
6997				 -joinstyle => 'bevel',
6998				 -tags      => [@std_tags_hin,
6999						@extra_tags],
7000				 ($stipple ? (-stipple => $stipple) : ()),
7001				 ($line_dash_hin ? (-dash => $line_dash_hin) : ()),
7002				 ($line_capstyle_hin ? (-capstyle => $line_capstyle_hin) : ()),
7003			         (exists $layer_category_line_arrow{$abk} && exists $layer_category_line_arrow{$abk}{$cat_hin} ? (-arrow => $layer_category_line_arrow{$abk}{$cat_hin})
7004				  : exists $layer_line_arrow{$abk} ? (-arrow => $layer_line_arrow{$abk})
7005				  : exists $category_line_arrow{$cat_hin} ? (-arrow => $category_line_arrow{$cat_hin}) : ()),
7006				);
7007			}
7008
7009			if ($abk eq 'sBAB') { # thin grey line for "two track" effect
7010			    $c->createLine
7011				(@coordlist,
7012				 -fill      => 'lightgrey',
7013				 -width     => 1,
7014				 -joinstyle => 'bevel',
7015				 -tags      => [$abk, $strname, $abk."-fg",$abk."-".$i],
7016                                 ($line_dash_hin ? (-dash => $line_dash_hin) : ()),
7017			         # XXX??? ($line_dash_rueck ? (-capstyle => $line_dash_rueck) : ()),
7018				 -state	    => ($this_width_hin >= $sBAB_two_track_width ? 'normal' : 'hidden'),
7019				);
7020			}
7021
7022		        if ($str_name_draw{$abk} && $category eq 'Z' && $item) {
7023			    $draw_strname_for_area->();
7024			}
7025
7026			# no $cat_rueck support for names
7027			if ($str_name_draw{$abk}
7028			    && (($abk =~ /^[ls]/ &&
7029				 $abk ne 'sBAB' &&
7030				 ($cat_hin =~ /^[BH]/ ||
7031				  ($lazy_str{$abk} && $scale >= 10)
7032				 )) || 0) # nur Hauptstra�en zeichnen (wg. Performance
7033                                          # und �bersichtlichkeit), oder auch Nebenstra�en,
7034                                          # falls lazy_plot und kleiner Ma�stab
7035			   ) {
7036			    my $strname = Strassen::strip_bezirk($strname);
7037			    Tk::RotFont::canvas
7038			      ($c, $abk, \@coordlist,
7039			       $category_rot_font{$cat_hin} || $rot_font_sub,
7040			       $category_size{$cat_hin} || 10,
7041			       $strname,
7042			       (defined $category_font_color{$cat_hin} ? (-fill => $category_font_color{$cat_hin}) : ()),
7043			      );
7044			}
7045			if ($str_nr_draw{$abk}) {
7046			    draw_street_numbers($c,$strname,$abk,\@coordlist);
7047			}
7048
7049			my $street_photo;
7050			my $street_anchor = "nw";
7051			my $street_delta;
7052			my $street_addtag;
7053			if ($abk eq 'e') {
7054			    my $p = get_symbol_scale($abk);
7055			    $street_photo = $p if $p;
7056			} elsif ($cat_hin eq 'St') {
7057			    $street_photo = $steigung_photo if $steigung_photo;
7058			    $street_anchor = "s";
7059			    $street_delta = $street_photo->width/2+2;
7060			} elsif ($abk eq 'comm-tram' || $abk eq 'nl') {
7061			    $street_photo = get_symbol_scale($abk);
7062			    $street_delta = 0; # XXX
7063			} elsif (@addinfo_hin # ignore @addinfo_rueck for now
7064				) {
7065			    for my $addinfo_hin (@addinfo_hin) {
7066			        if ($addinfo_hin eq 'inwork') {
7067				    $street_photo = get_symbol_scale('attrib-inwork');
7068				    $street_addtag = "attrib-inwork";
7069				} elsif ($addinfo_hin eq 'danger' && $achtung_photo) {
7070				    $street_photo = $achtung_photo;
7071				    $street_anchor = "c";
7072				}
7073			    }
7074			}
7075			if ($street_photo) {
7076			    $draw_street_photo->($street_photo, $street_anchor, $street_delta, -addtag => $street_addtag);
7077			}
7078		    }
7079		}
7080	    }
7081	};
7082EOF
7083
7084    # XXX maybe combine this code with parsing coords code in $plotstr_draw_sub
7085    my $parse_coords_code = <<'EOF';
7086	      TRY: {
7087#XXX		    my($xx, $yy);
7088		    if (!$edit_mode) {
7089			($xx, $yy) = split /,/, $_;
7090		    } elsif ($edit_mode &&
7091			     /([A-Za-z]+)?(-?[\d\.]+),(-?[\d\.]+)$/) {
7092			# XXX Verwendung von data/BASE (hier und �berall)
7093			my $this_coordsys = (defined $1 ? $1 : '');
7094			if ($this_coordsys eq $coordsys ||
7095			    (!($this_coordsys ne '' || $coordsys ne 'B'))) {
7096			    ($xx, $yy) = ($2, $3);
7097                        } else {
7098			    # the hard way: convert it
7099			    $this_coordsys = 'B' if $this_coordsys eq '';
7100			    ($xx,$yy) = $Karte::map_by_coordsys{$this_coordsys}->map2map($coord_system_obj, $2, $3);
7101#warn "($xx,$yy)";
7102			}
7103		    } else {
7104			last TRY;
7105		    }
7106		}
7107EOF
7108
7109    $plotpoint_draw_sub = <<'EOF'
7110	sub {
7111	    my $ret = shift;
7112	    my $category = $ret->[Strassen::CAT];
7113	    return if defined $restrict and $category !~ /$restrict/;
7114	    if (!$edit_normal_mode) { # we want to see everything in edit mode
7115	        return if index($category, "::igndisp") >= 0;
7116	    }
7117	    my $pointname = $ret->[Strassen::NAME];
7118	    my $koord = $ret->[Strassen::COORDS][0]; # erste Koordinate
7119            $koord = $conv->($koord) if $conv;
7120	    my($xx,$yy);
7121	    $_ = $koord;
7122EOF
7123    . $parse_coords_code . <<'EOF';
7124	    my($x, $y) = transpose($xx, $yy);
7125
7126	    if ($layer_category_image{$abk} && defined $layer_category_image{$abk}{$category}) {
7127		$category = "IMG:$layer_category_image{$abk}{$category}";
7128	    } elsif (defined $category_image{$category}) {
7129		$category = "IMG:$category_image{$category}";
7130	    }
7131	DRAW_ITEM: {
7132	        if ($category =~ $complex_IMG_qr) {
7133		    my $photo = $1;
7134		    my $anchor = ($2 ? $2 : "c");
7135		    my($base) = ($photo =~ m|/| ? $photo =~ /([^\/]+)$/ : $photo);
7136		    $base = "p_$base";
7137		    my $p = get_image_for_p($base, $photo, $abk);
7138		    if ($p) {
7139		        $c->createImage($x, $y, -image => $p,
7140				        -anchor => $anchor,
7141					# $abk-img or $abk-fg? set both!
7142				        -tags => ["$abk-img", "$xx,$yy", $pointname, ($abk =~ /^L\d+$/ ? ("$abk-fg", "L-fg") : ())],
7143				       );
7144		        last; # we're done, only label drawing missing
7145		    }
7146		    warn "Can't find image $photo (3)";
7147	        }
7148
7149	        if ($XXX_use_old_R_symbol && $abk eq 'r') {
7150		    my $length = $category =~ m{^(RP)$} ? $rbahn_length/2 : $rbahn_length;
7151		    $c->createLine($x-$length, $y, $x+$length, $y,
7152			           -tags => ["$abk-bg", "$xx,$yy", $pointname, "$abk-" . $category . "-bg"]);
7153		    if ($category !~ m{^(RP)$}) {
7154		        $c->createText($x, $y,
7155			               -tags => ["$abk-fg", "$xx,$yy", $pointname, "$abk-" . $category . "-fg"]);
7156		    }
7157	        } elsif ($abk =~ /^[ubr]$/) {
7158		    $c->createImage($x, $y, -tags => ["$abk-fg", "$xx,$yy", $pointname, "$abk-" . $category . "-fg"]);
7159	        } elsif ($abk eq 'lsa') {
7160		    my($rawcategory, @attribs) = split /::/, $category;
7161		    my @tags = ("$abk-fg", "$xx,$yy", $pointname,
7162			        "$abk-" . $rawcategory . "-fg",
7163			        $abk."-".$i);
7164		    # keine Verwendung von _coord_as_string
7165		    $c->createImage
7166		      ($x, $y,
7167		       -image => ($rawcategory eq 'B'
7168			          ? $andreaskr_photo
7169				  : $rawcategory eq 'B0'
7170				    ? $andreaskr_grey_photo
7171			            : $rawcategory eq 'Zbr'
7172				      ? $zugbruecke_photo
7173				      : $rawcategory eq 'F'
7174				        ? $ampelf_photo
7175			                : $ampel_photo
7176			         ),
7177		       -tags => \@tags,
7178		      );
7179		    if (@attribs) {
7180			for my $attrib (@attribs) {
7181			    if ($attrib eq 'inwork') {
7182				my $use_inwork_photo = get_symbol_scale('attrib-inwork');
7183				if ($use_inwork_photo) {
7184				    $c->createImage($x, $y,
7185					    	-anchor => 'nw',
7186					    	-image => $use_inwork_photo,
7187					    	-tags => [@tags,'attrib-inwork']);
7188				}
7189			    }
7190			}
7191		    }
7192		    $ampeln{"$xx,$yy"} = $rawcategory;
7193	        } elsif ($abk eq 'pl') {
7194		    $c->createLine($x, $y, $x, $y,
7195			           -tags => ["$abk-fg", "$xx,$yy", $pointname],
7196			          );
7197	        } elsif ($abk eq 'vf') {
7198		    my($rawcategory, $attribs) = split /::/, $category;
7199		    my @tags = ("$abk-fg", "$xx,$yy", "$abk-$rawcategory-fg", "$abk-$i");
7200		    if ($rawcategory eq 'Vf') {
7201			my($x1,$y1,$x2,$y2,$x3,$y3) =
7202			  (transpose(@{Strassen::to_koord1($ret->[Strassen::COORDS][0])}),
7203			   transpose(@{Strassen::to_koord1($ret->[Strassen::COORDS][1])}),
7204			   transpose(@{Strassen::to_koord1($ret->[Strassen::COORDS][2])}));
7205		        $c->createImage($x2,$y2, -tags => \@tags);
7206			my $len1 = Strassen::Util::strecke([$x1,$y1], [$x2,$y2]);
7207			my $whole_len1 = $len1 > 20 ? 20 : $len1;
7208			my $len2 = Strassen::Util::strecke([$x2,$y2], [$x3,$y3]);
7209			my $whole_len2 = $len2 > 20 ? 20 : $len2;
7210			my($cx1,$cy1,$cx2,$cy2,$cx3,$cy3)
7211			  = (($x1-$x2)/$len1*$whole_len1+$x2,
7212			     ($y1-$y2)/$len1*$whole_len1+$y2,
7213			     $x2,$y2,
7214			     ($x3-$x2)/$len2*$whole_len2+$x2,
7215			     ($y3-$y2)/$len2*$whole_len2+$y2,
7216			    );
7217			$c->createLine($cx1,$cy1,$cx2,$cy2,$cx3,$cy3,
7218				           -tags => "$abk-bg");
7219		    } else {
7220		        $c->createImage($x,$y, -tags => \@tags);
7221		    }
7222	        } elsif ($abk =~ /^L(\d+)/) {
7223		    my $color = $category =~ /^\#/ ? $category : exists $layer_category_color{$abk} && exists $layer_category_color{$abk}{$category} ? $layer_category_color{$abk}{$category} : exists $category_color{$category} ? $category_color{$category} : undef;
7224		    my $width = ($layer_category_line_width{$abk} && $layer_category_line_width{$abk}{$category}) || $category_width{$category} || $p_width{$abk} || $default_width || 6;
7225		    $c->createLine($x, $y, $x, $y,
7226			           (defined $color ? (-fill => $color) : ()),
7227			           -width => $width,
7228			           -tags => ["$abk-fg", "$xx,$yy", $pointname, "p-" . $i, "L-fg"]);
7229	        } elsif ($abk =~ /^(kn|ki|rest)$/) {
7230		    $c->createImage($x, $y,
7231				    -tags => ["$abk-fg", "$xx,$yy", $pointname, "$abk-" . $i]);
7232	        } elsif ($abk =~ /^label/) {
7233		    # $category should contain font, anchor etc.
7234		    $c->createText($x, $y, -text => $pointname,
7235			           -font => $font{'large'}, # XXX
7236			           -anchor => "w", # XXX
7237			           -tags => ["$abk-fg", "$xx,$yy", $pointname, "$abk-" . $i]);
7238	        } else {
7239		    # Else draw a generic point (broad, color from cat)
7240		    my $color = $category_color{$category} || ($category =~ /^\#/ ? $category : 'red');
7241		    my $width = $category_width{$category} || $p_width{$abk} || $default_width || 6;
7242		    $c->createLine($x, $y, $x, $y,
7243			           -fill => $color, -capstyle => $capstyle_round,
7244			           -width => $width,
7245			           -tags => ["$abk-fg", "$xx,$yy", $pointname, "$abk-" . $i]);
7246	        }
7247	    } # DRAW_ITEM
7248	    if ($name_draw) {
7249		my %args = ((exists $category_font_color{$category} ? (-fill => $category_font_color{$category}) : ()),
7250			    (exists $category_font{$category} ? (-font => $category_font{$category}) : ()),
7251			    -outlinewidth => 2,
7252			    -text => $pointname,
7253			    -tags => $name_draw_tag,
7254			   );
7255		if ($orientation eq 'portrait' && $Tk::VERSION >= 800) {
7256		    require Tk::RotFont;
7257		    # XXX geht nicht...
7258		    Tk::RotFont::createRotText
7259			    ($c, $x, $y,
7260			     -text => $pointname,
7261			     -rot => 3.141592653/2,
7262			     #-font => get_orte_label_font($cat),
7263			     -font => $rot_font_sub->(100), # no $cat...
7264			     -tags => $name_draw_tag,
7265			    );
7266		} elsif (!$no_overlap_label ||
7267			 !draw_text_intelligent
7268			 ($c, $x, $y,
7269			  -abk  => $name_draw_other,
7270			  -xadd => $xadd_anchor,
7271			  -yadd => $yadd_anchor,
7272			  -outline => 1,
7273			  %args,
7274			 )) {
7275		    my($x,$y) = ($x,$y);
7276		    if (defined $label_spaceadd) {
7277			$args{-text} = $label_spaceadd . $args{-text};
7278		    } else {
7279			$x += $xadd_anchor->{'w'};
7280			$y += $yadd_anchor->{'w'};
7281		    }
7282		    outline_text($c, $x, $y, -anchor => 'w', %args);
7283		}
7284	    }
7285	};
7286EOF
7287
7288    $plotorte_draw_sub = <<'EOF'
7289	sub {
7290	    my $ret = shift;
7291	    my $cat = $ret->[Strassen::CAT];
7292	    my($name, $add) = split(/\|/, $ret->[Strassen::NAME]);
7293	    my($xx,$yy);
7294	    $_ = $ret->[Strassen::COORDS][0];
7295            $_ = $conv->($_) if $conv;
7296EOF
7297    . $parse_coords_code . <<'EOF';
7298#	    if ($ret->[Strassen::COORDS][0] =~ /(-?\d+),(-?\d+)/) {
7299	    if (defined $xx) {
7300#		my($x, $y) = ($1, $2);
7301#		my($tx, $ty) = $transpose->($x, $y);
7302		my($tx, $ty) = $transpose->($xx, $yy);
7303		my $fullname = ($add ? $name . " " . $add : $name);
7304		return if ($place_category && $place_category ne "auto" && $cat < $place_category);
7305		my $point_item;
7306                if (!$municipality) {
7307                    $point_item = $c->createLine
7308			($tx, $ty, $tx, $ty,
7309			 -tags => [$type, "$xx,$yy", $fullname, $label_tag."P$cat", $type."-".($i-1)],
7310			);
7311                }
7312		if ($name_o) {
7313		    my $text = ($args{Shortname}
7314				? $name
7315				: $fullname);
7316		    my(@tags) = ($label_tag, "$label_tag$cat", $label_tag."-".($i-1));
7317		    if ($orientation eq 'portrait' && $Tk::VERSION >= 800) {
7318			require Tk::RotFont;
7319			# XXX geht nicht...
7320			Tk::RotFont::createRotText
7321				($c, $tx, $ty-4,
7322				 -text => $text,
7323				 -rot => 3.141592653/2,
7324				 #-font => get_orte_label_font($cat),
7325				 -font => $rot_font_sub->(100+$cat*12),
7326				 -tags => \@tags,
7327				);
7328		    } elsif ($no_overlap_label && !$municipality) {
7329			push(@orte_coords_labeling,
7330			     [$text, $tx, $ty, $cat, $point_item]);
7331		    } else {
7332			if ($do_outline_text) {
7333			    outline_text
7334				($c,
7335				 $tx+4,
7336				 $ty,
7337				 -text => $text,
7338				 -tags => \@tags,
7339				 -anchor => 'w',
7340				 -justify => 'left',
7341				 -fill => '#000080',
7342				 -font => get_orte_label_font($cat),
7343				);
7344			} else {
7345			    $c->createText($tx, $ty,
7346					   -text => $label_spaceadd{'o'} . $text,
7347					   -tags => \@tags,
7348					  );
7349			}
7350		    }
7351		}
7352	    }
7353	};
7354EOF
7355}
7356
7357sub maybe_expand_image_file {
7358    my($imgfile, $datafile, $subdir) = @_;
7359    if (file_name_is_absolute($imgfile)) {
7360	return try_image_suffix($imgfile);
7361    }
7362    my $abs_img = try_image_suffix("$FindBin::RealBin/$subdir/$imgfile");
7363    if (defined $abs_img && -r $abs_img) {
7364	return $abs_img;
7365    }
7366    # relative to this file
7367    return try_image_suffix(dirname($datafile) . "/" . $imgfile);
7368}
7369
7370# For an absolute image path without suffix try to find an existing
7371# image which is supported by the current configuration. Returns undef
7372# if nothing suitable could be found.
7373sub try_image_suffix {
7374    my($imgfile_without_suffix) = @_;
7375    return $imgfile_without_suffix if $imgfile_without_suffix =~ m{\.(png|jpg|xpm|gif|svg)$};
7376    for my $suffix (@image_type_order) {
7377	my $try_imgfile = $imgfile_without_suffix.".".$suffix;
7378	if (can_handle_image_suffix($suffix) && -r $try_imgfile) {
7379	    return $try_imgfile;
7380	}
7381    }
7382    undef;
7383}
7384
7385# Return true if the supplied image suffix ("jpg", "gif" etc.) can be
7386# handled. The result is cached in the global %can_handle_image.
7387sub can_handle_image_suffix {
7388    my $suffix = shift;
7389    if (!defined $can_handle_image{$suffix}) {
7390	if ($suffix eq 'png') {
7391	    if (eval {
7392		die "Probably corrupt in my SiePerl distribution" if $os eq 'win' && $Tk::VERSION < 804;
7393		require Tk::PNG;
7394		1;
7395	    }) {
7396		$can_handle_image{$suffix} = 1;
7397	    } else {
7398		$can_handle_image{$suffix} = 0;
7399	    }
7400	} elsif ($suffix eq 'jpg') {
7401	    if (eval {
7402		die "Probably corrupt in my SiePerl distribution" if $os eq 'win' && $Tk::VERSION < 804;
7403		require Tk::JPEG;
7404		1;
7405	    }) {
7406		$can_handle_image{$suffix} = 1;
7407	    } else {
7408		$can_handle_image{$suffix} = 0;
7409	    }
7410	} elsif ($suffix eq 'svg') {
7411	    # Assume that a postprocessor will be run to create the
7412	    # real image
7413	    if (can_handle_image_suffix('png') && eval {
7414		require File::Temp;
7415		is_in_path('convert');
7416	    }) {
7417		$can_handle_image{$suffix} = 1;
7418	    } else {
7419		$can_handle_image{$suffix} = 0;
7420	    }
7421	} elsif ($suffix =~ m{^(gif|xpm)$}) { # Tk builtins
7422	    $can_handle_image{$suffix} = 1;
7423	} else {
7424	    die "Unhandled image suffix '$suffix'";
7425	}
7426    }
7427    $can_handle_image{$suffix};
7428}
7429
7430sub set_bindings {
7431    foreach (qw(p pp o
7432		u-bg u-fg u_bg-img b-bg b-fg b_bg-img r-bg r-fg r_bg-img
7433		sperre sperre_u sperre_b sperre_r
7434		lsa-fg lsa-bg show pl-fg
7435		L-img L-fg kn-fg ki-fg rest-fg)) {
7436	std_p_binding($_);
7437    }
7438
7439    foreach (qw(s sBAB S l L u b r f v v-fg w W i e comm mount),
7440	     (map { "comm-$_" } @comments_types),
7441	     qw(gr qs hs ql hl fz nl ovl temp_sperre temp_sperre_s rw wr)) {
7442	std_str_binding($_);
7443    }
7444
7445    # XXX Some bindings are here and in std_p_binding, which cause
7446    # problems as both function set the <Leave> binding
7447    # XXX route: no!
7448    # XXX more missing, typically everything with a label is transparent
7449    foreach (qw(lsa-bg lsa-fg vf-bg vf-fg
7450		s-label-bg s-label sBAB-label-bg sBAB-label
7451		w-label-bg w-label f-label-bg f-label gBO-label-bg gBO-label
7452		l-label-bg l-label
7453		u-label b-label r-label fz-label show O),
7454	     (map { ("comm-$_-label", "comm-$_-label-bg") } @comments_types),
7455	    ) {
7456	std_transparent_binding($_);
7457    }
7458    # spezielle Bindings f�r Routen
7459    $c->bind('route', '<Any-Enter>'  => sub { enterroute($_[0]) });
7460    $c->bind('route', '<Any-Motion>' => sub { enterroute($_[0]) });
7461    $c->bind('route', '<Any-Leave>'  => \&leaveroute);
7462
7463    # Cursor bei delnet-Kreuzen:
7464    $c->bind("delnet", "<Any-Enter>" => sub {
7465		 if ($map_mode eq MM_USEREDIT) {
7466		     save_cursor();
7467		     set_cursor("addnet", "tcross");
7468		 }
7469	     });
7470    $c->bind("delnet", "<Any-Leave>" => \&restore_cursor);
7471
7472    foreach (qw(all)) {
7473	# XXX TODO should be ButtonRelease-1 some day, if using
7474	# B1-Motion for rubberbanding a zoom region
7475	if ($MM_DRAG_IS_OBSOLETE) {
7476	    $c->bind($_, "<ButtonRelease-1>" => \&set_route_point);
7477	} else {
7478	    $c->bind($_, "<ButtonPress-1>" => \&set_route_point);
7479	}
7480    }
7481
7482    # Stack in tkstadtware f�r dragging angucken! XXX
7483    $c->CanvasBind("<1>" => sub {
7484		       if ($MM_DRAG_IS_OBSOLETE) {
7485			   my $e = $c->XEvent;
7486			   ($canvas_drag_x, $canvas_drag_y) = ($e->x, $e->y);
7487			   $c->scan('mark', $canvas_drag_x, $canvas_drag_y);
7488			   $maybe_canvas_drag = 1;
7489		       }
7490
7491		       if ($map_mode =~ /^BBBike/) {
7492			   my $button_callback = $map_mode . '::button';
7493			   if (defined &$button_callback) {
7494			       my $e = $c->XEvent;
7495			       my $ret = eval $button_callback.'($_[0], $e)';
7496			       die $@ if $@;
7497			       return if $ret; # otherwise fallthrough to MM_DRAG
7498			   }
7499		       } elsif ($map_mode eq MM_CUSTOMCHOOSE) {
7500			   set_route_point($c);
7501		       } elsif ($map_mode eq MM_SCRIBBLE) {
7502			   # XXX not Tk::Babybike!
7503			   Tk::Babybike::handle_button1_scribble($c,$c->XEvent);
7504		       } elsif ($map_mode eq MM_URL_SELECT) {
7505			   my($url) = grep { $_ } map {
7506			       my($url) = $_ =~ m{((?:file|https?)://\S+)};
7507			       defined $url ? $url : undef;
7508			   } $c->gettags("current");
7509			   if ($url) {
7510			       require WWWBrowser;
7511			       main::status_message("URL: $url", "info");
7512			       WWWBrowser::start_browser($url);
7513			   } else {
7514			       warn "Cannot get URL from " . join(", ", $c->gettags("current"));
7515			   }
7516		       }
7517
7518		       # XXX duplicated code, see above
7519		       if ($map_mode eq MM_DRAG) {
7520			   my $e = $c->XEvent;
7521			   ($canvas_drag_x, $canvas_drag_y) = ($e->x, $e->y);
7522			   $c->scan('mark', $canvas_drag_x, $canvas_drag_y);
7523			   $maybe_canvas_drag = 1;
7524		       }
7525		   });
7526    $c->CanvasBind('<B1-Motion>' => sub {
7527		       if ($map_mode eq MM_SCRIBBLE) {
7528			   # XXX not Tk::Babybike!
7529			   return Tk::Babybike::handle_button1_motion_scribble($c,$c->XEvent);
7530		       }
7531		       return if $map_mode ne MM_DRAG && !$MM_DRAG_IS_OBSOLETE;
7532		       my $e = $c->XEvent;
7533		       my($e_x, $e_y) = ($e->x, $e->y);
7534		       # Start drag only if the user has moved a certain
7535		       # distance (3 pixels here). This is because clicking
7536		       # the mouse may involve a small motion movement.
7537		       return if ($maybe_canvas_drag &&
7538				  Strassen::Util::strecke([$canvas_drag_x, $canvas_drag_y],[$e_x, $e_y]) < 3);
7539		       $maybe_canvas_drag = 0;
7540		       $c->scan('dragto', $e_x, $e_y, 1);
7541		       if (!$c->{SavedCursor}) {
7542			   save_cursor();
7543			   set_cursor('movehand','fleur');
7544		       }
7545		       $in_canvas_drag = 1;
7546		   });
7547    $c->CanvasBind('<ButtonRelease-1>' => sub {
7548		       restore_cursor();
7549		       $in_canvas_drag = 0;
7550		   });
7551
7552    set_b2();
7553
7554    # Canvas menu
7555    my $popup_menu;
7556    if ($right_is_popup) {
7557	$popup_menu = $c->Menu(-title => M"Kartenmen�",
7558			       -tearoff => $Tk::platform eq 'unix');
7559	$popup_menu->command(-label => M"Gesamte Route l�schen",
7560			     -command => sub { delete_route() },
7561			    );
7562	$popup_menu->command(-label => M"Suche wiederholen",
7563			     -command => \&re_search_gui,
7564			    );
7565	$popup_menu->command(-label => M"R�ckweg",
7566			     -command => \&way_back,
7567			    );
7568    }
7569    if ($c->can("menu") and $c->can("PostPopupMenu") and $Tk::VERSION >= 800) {
7570	$c->menu($popup_menu);
7571	$c->Tk::bind('<3>' => sub {
7572			 if ($right_is_popup) {
7573			     my $e = $_[0]->XEvent;
7574			     $_[0]->PostPopupMenu($e->X, $e->Y);
7575			 } else {
7576			     delete_route();
7577			 }
7578		     });
7579    } else {
7580	# legacy code
7581	$frame->bind($c, "<ButtonPress-3>" => sub {
7582			 if ($right_is_popup) {
7583			     my $e = $_[0]->XEvent;
7584			     $popup_menu->Post($e->X, $e->Y);
7585			 } else {
7586			     delete_route();
7587			 }
7588		     });
7589    }
7590    $top->Advertise(PopupMenu => $popup_menu)
7591	if $popup_menu;
7592
7593    my $alt_mouse1 = sub {
7594## DEBUG_BEGIN
7595#benchbegin("Alt Mouse1");
7596## DEBUG_END
7597
7598	if ($map_mode eq MM_DRAG || $MM_DRAG_IS_OBSOLETE) {
7599	    my $e = $c->XEvent;
7600	    ($canvas_drag_x, $canvas_drag_y) = ($e->x, $e->y);
7601	    $c->scan('mark', $canvas_drag_x, $canvas_drag_y);
7602	    $maybe_canvas_drag = 1;
7603	}
7604
7605	if ($alt_set_route_point{$map_mode}) {
7606	    return $alt_set_route_point{$map_mode}->(@_);
7607	}
7608	my($rx,$ry);
7609	if ($map_mode eq MM_BUTTONPOINT) {
7610	   ($rx,$ry) = freerec_sub(@_);
7611	}
7612	freedraw_sub($_[0],$rx,$ry);
7613## DEBUG_BEGIN
7614#benchend();
7615## DEBUG_END
7616    };
7617
7618    foreach (qw(Alt Shift Lock)) {
7619	$frame->bind($c, "<$_-ButtonPress-1>"   => $alt_mouse1);
7620    }
7621
7622    if ($followmouse) {
7623	start_followmouse();
7624    }
7625
7626    # Zoom
7627    for my $kp ('plus', 'KP_Add') {
7628	$top->bind("<$kp>" => sub { scalecanvas_from_canvas_event($c, 2) });
7629    }
7630    for my $kp ('minus', 'KP_Subtract') {
7631	$top->bind("<$kp>" => sub { scalecanvas_from_canvas_event($c, 0.5) });
7632    }
7633
7634    $top->protocol('WM_DELETE_WINDOW', \&exit_app_noninteractive);
7635    my($old_width, $old_height);
7636    my $in_configure_event;
7637    $top->bind('<Configure>' => sub {
7638		   my $e = $top->XEvent;
7639		   return if !$e || $in_configure_event;
7640		   $in_configure_event++;
7641		   eval {
7642		       if (!defined $old_width || $old_width != $e->w ||
7643			   !defined $old_height || $old_height != $e->h) {
7644			   arrange_symframe();
7645			   arrange_topframe();
7646			   $old_width = $e->w;
7647			   $old_height = $e->h;
7648		       }
7649		   };
7650		   my $err = $@;
7651		   $in_configure_event--;
7652		   die $err if $err;
7653	       });
7654
7655    $top->bind("<<CloseMainWin>>" => \&exit_app);
7656    for my $mod (qw(Alt Control)) {
7657	$top->bind("<$mod-r>" => sub { reload_all() });
7658    }
7659
7660    $top->bind('<Control-o>' => sub { load_save_route(0) });
7661    $top->bind('<Control-s>' => sub { load_save_route(1) });
7662    $top->bind('<Control-underscore>' => \&get_undo_route);
7663    $top->bind('<Control-z>' => \&get_undo_route);
7664    $top->bind($_ => sub {
7665		   require BBBikeAdvanced;
7666		   search_anything();
7667	       })
7668	for ('<Control-Key-f>', '<Key-slash>');
7669    $top->bind('<Control-g>' => sub { send_route_to_gps() });
7670    $top->bind('<Key-colon>' => sub {  my $e = $c->XEvent;
7671				       my(%args);
7672				       if ($e) {
7673					   my ($x, $y) = ($c->canvasx($e->x),
7674							  $c->canvasy($e->y));
7675					   $args{-preserveposition} = [$x,$y];
7676				       }
7677				       enter_scale(%args);
7678				   });
7679
7680    $top->bind("<Escape>" => sub { $escape = 1 });
7681    $top->bind('Busy', '<Escape>' => sub { $escape = 1; });
7682    $top->bind('Busy', '<KeyRelease-Escape>' => sub { });
7683    bind_nomod($top, '<asterisk>' => \&show_register);
7684    for my $i (0 .. 9) {
7685	my $ii = $i;
7686	$top->bind("<Key-$ii>" => sub { get_route_from_register($ii) });
7687    }
7688
7689    bind_nomod($top, "<P>" => sub {
7690		   require BBBikeAdvanced;
7691		   start_ptksh();
7692	       });
7693    ## XXX Duplicate binding!
7694    #$top->bind("<Control-R>" => sub {
7695    #		   require BBBikeAdvanced;
7696    #		   reload_new_modules();
7697    #	       });
7698    bind_nomod($top, "<S>" => sub {
7699		   set_map_mode(MM_SEARCH);
7700	       });
7701    bind_nomod($top, "<U>" => sub {
7702		   $map_mode = MM_USEREDIT;
7703		   set_cursor('delnet', 'X_cursor');
7704	       });
7705    if ($Tk::platform ne 'MSWin32') { # XXX aber auf der Win98-Maschine von Monika laeuft es gut?!
7706	bind_nomod($top, "<X>" => \&layer_editor);
7707    }
7708    bind_nomod($top, "<i>" => sub { show_info() });
7709
7710    if (!$no_map) {
7711	bind_nomod($top, '<Key-M>' => sub { $map_draw = 1; getmap() });
7712	$top->bind('<Control-Key-M>' => sub { delete_map() });
7713    }
7714
7715    $top->bind("<BackSpace>" => \&mouse_dellast);
7716    $top->bind("<Shift-BackSpace>" => \&delete_route);
7717    $top->bind("<Control-Key-x>" => \&delete_route);
7718    $top->bind("<Delete>" => \&deltovia);
7719
7720    if ($advanced) {
7721	advanced_bindings();
7722    }
7723
7724    for my $kp ('', 'KP_') {
7725	eval { # perl/Tk+win definiert keine KP_-Keysyms
7726	$top->bind("<${kp}Down>"  => sub { $c->yview(scroll =>  1, 'units') });
7727	$top->bind("<${kp}Up>"    => sub { $c->yview(scroll => -1, 'units') });
7728	$top->bind("<${kp}Left>"  => sub { $c->xview(scroll => -1, 'units') });
7729	$top->bind("<${kp}Right>" => sub { $c->xview(scroll =>  1, 'units') });
7730
7731	$top->bind("<${kp}Begin>" => sub { center_best() });
7732        };
7733    }
7734
7735    $top->bind("<Next>"  => sub { $c->yview(scroll =>  5, 'units') });
7736    $top->bind("<Prior>" => sub { $c->yview(scroll => -5, 'units') });
7737    $top->bind("<Home>"  => sub { $c->xview(scroll => -5, 'units') });
7738    $top->bind("<End>"   => sub { $c->xview(scroll =>  5, 'units') });
7739    eval {
7740    $top->bind("<KP_Next>"  => sub { $c->xview(scroll =>  1, 'units');
7741				     $c->yview(scroll =>  1, 'units') });
7742    $top->bind("<KP_Prior>" => sub { $c->xview(scroll =>  1, 'units');
7743				     $c->yview(scroll => -1, 'units') });
7744    $top->bind("<KP_Home>"  => sub { $c->xview(scroll => -1, 'units');
7745				     $c->yview(scroll => -1, 'units') });
7746    $top->bind("<KP_End>"   => sub { $c->xview(scroll => -1, 'units');
7747				     $c->yview(scroll =>  1, 'units') });
7748    };
7749
7750    $top->bind("<Shift-KP_2>" => sub { $c->yview(scroll =>  5, 'units') });
7751    $top->bind("<Shift-KP_8>" => sub { $c->yview(scroll => -5, 'units') });
7752    $top->bind("<Shift-KP_4>" => sub { $c->xview(scroll => -5, 'units') });
7753    $top->bind("<Shift-KP_6>" => sub { $c->xview(scroll =>  5, 'units') });
7754
7755    $top->bind("<Shift-KP_3>" => sub { $c->xview(scroll =>  5, 'units');
7756				       $c->yview(scroll =>  5, 'units') });
7757    $top->bind("<Shift-KP_9>" => sub { $c->xview(scroll =>  5, 'units');
7758				       $c->yview(scroll => -5, 'units') });
7759    $top->bind("<Shift-KP_7>" => sub { $c->xview(scroll => -5, 'units');
7760				       $c->yview(scroll => -5, 'units') });
7761    $top->bind("<Shift-KP_1>" => sub { $c->xview(scroll => -5, 'units');
7762				       $c->yview(scroll =>  5, 'units') });
7763
7764    # Cycling through toplevels
7765    $top->bind("all", "<Control-Tab>" => sub { focus_next_toplevel(); Tk->break });
7766    $top->bind(".", "<Control-Tab>" => sub { });
7767    $top->bind("all", "<Control-Shift-Tab>" => sub { focus_prev_toplevel(); Tk->break });
7768    $top->bind(".", "<Control-Shift-Tab>" => sub { });
7769
7770}
7771
7772sub focus_next_toplevel { _focus_nextprev_toplevel(+1) }
7773sub focus_prev_toplevel { _focus_nextprev_toplevel(-1) }
7774
7775sub _focus_nextprev_toplevel {
7776    my($dir) = @_;
7777    my @all_toplevels = grep { Tk::Exists($_) && $_->state eq "normal" } ($top, values(%toplevel));
7778    my $current_toplevel = $top->focusCurrent->toplevel;
7779    my $new_i;
7780    for(my $i=0; $i<=$#all_toplevels;$i++) {
7781	if ($all_toplevels[$i] == $current_toplevel) {
7782	    $new_i = $i + $dir;
7783	    last;
7784	}
7785    }
7786    if (!defined $new_i) {
7787	$new_i = 0;
7788	warn "cannot find current toplevel <$current_toplevel> in list <@all_toplevels>, fallback to main window <$top>";
7789    } else {
7790	if ($new_i < 0) {
7791	    $new_i = $#all_toplevels;
7792	} elsif ($new_i > $#all_toplevels) {
7793	    $new_i = 0;
7794	}
7795    }
7796    $all_toplevels[$new_i]->raise;
7797    # ->focus between toplevels does not seem to work under cygwin/x
7798    $all_toplevels[$new_i]->focus;
7799}
7800
7801sub set_map_mode {
7802    if (@_) {
7803	$map_mode = $_[0];
7804    }
7805    execute_and_set_map_mode_deactivate(undef);
7806    if ($map_mode eq MM_SEARCH) {
7807	if (defined $search_route_flag && $search_route_flag =~ /^ziel/) {
7808	    set_cursor('ziel');
7809	} else {
7810	    set_cursor('start');
7811	}
7812    } elsif ($map_mode eq MM_BUTTONPOINT) {
7813	set_cursor('xy','crosshair');
7814    } elsif ($map_mode eq MM_INFO) {
7815#XXX	$map_mode_deactivate->() if $map_mode_deactivate;
7816	set_cursor('info','circle');
7817#XXX	undef $map_mode_deactivate;
7818    } elsif ($map_mode eq MM_DRAG) {
7819	set_cursor('movehand','fleur');
7820    } elsif (exists $map_mode_callback{$map_mode} &&
7821	     ref $map_mode_callback{$map_mode} eq 'CODE') {
7822	$map_mode_callback{$map_mode}->();
7823    } elsif ($map_mode eq MM_URL_SELECT) {
7824	set_cursor('www');
7825    }
7826}
7827
7828sub execute_and_set_map_mode_deactivate {
7829    my($new_sub) = @_;
7830    if ($map_mode_deactivate) {
7831	$map_mode_deactivate->();
7832	undef $map_mode_deactivate;
7833    }
7834    if ($new_sub) {
7835	$map_mode_deactivate = $new_sub;
7836    }
7837}
7838
7839# Bindings
7840# ... unter Mauszeiger anzeigen
7841# Punkte
7842sub std_p_binding {
7843    my $tag = $_[0];
7844    $c->bind($tag, '<Any-Enter>' => sub {
7845		 $layer_pre_enter_command{$tag}->()
7846		     if exists $layer_pre_enter_command{$tag};
7847		 enterpoint($_[0]);
7848		 $layer_post_enter_command{$tag}->()
7849		     if exists $layer_post_enter_command{$tag};
7850	     });
7851    unless (/^lsa-/) { # lsa-fg/bg: leavepoint wird unten gesetzt
7852	$c->bind($tag, '<Any-Leave>' => sub {
7853		     $layer_pre_leave_command{$tag}->()
7854			 if exists $layer_pre_leave_command{$tag};
7855		     leavepoint(@_);
7856		     $layer_post_leave_command{$tag}->()
7857			 if exists $layer_post_leave_command{$tag};
7858		 });
7859    }
7860}
7861# Strecken, Fl�chen
7862sub std_str_binding {
7863    my $tag = $_[0];
7864    $c->bind($tag, '<Any-Enter>' => sub {
7865		 $layer_pre_enter_command{$tag}->()
7866		     if exists $layer_pre_enter_command{$tag};
7867		 enterstr($_[0]);
7868		 $layer_post_enter_command{$tag}->()
7869		     if exists $layer_post_enter_command{$tag};
7870	     });
7871    $c->bind($tag, '<Any-Leave>' => sub {
7872		 $layer_pre_leave_command{$tag}->()
7873		     if exists $layer_pre_leave_command{$tag};
7874		 leavestr($_[0]);
7875		 $layer_post_leave_command{$tag}->()
7876		     if exists $layer_post_leave_command{$tag};
7877	     });
7878    if (defined $c_balloon) {
7879	# Need to check if *all* items under the cursor are the same, as we
7880	# create the balloon text from *all* canvas items. This uses some
7881	# logic found in balloon_info_from_all_tags
7882	use vars qw($old_current_str_items);
7883	$old_current_str_items = "" if !defined $old_current_str_items;
7884	my $closeenough = $balloon_info_from_all_tags_closeenough;
7885	$c->bind($tag, '<Any-Motion>' => sub {
7886		     my($c) = @_;
7887		     my $e = $c->XEvent;
7888		     my($xx, $yy) = ($c->canvasx($e->x), $c->canvasy($e->y));
7889		     my(@items) = $c->find(overlapping =>
7890					   $xx-$closeenough, $yy-$closeenough,
7891					   $xx+$closeenough, $yy+$closeenough);
7892		     my $new_current_str_items = join(" ", @items);
7893		     if ($new_current_str_items ne $old_current_str_items) {
7894			 enterstr($c);
7895			 $old_current_str_items = $new_current_str_items;
7896		     } else {
7897			 $c_balloon->Track;
7898		     }
7899		 });
7900    }
7901}
7902
7903# unter den Tags nachgucken, ob es eine Stra�e zum Anzeigen gibt
7904# ("durchsichtige" Tags)
7905sub std_transparent_binding {
7906    # Motion statt Enter, da sich die Stra�e unter einer Route
7907    # �ndern kann.
7908    $c->bind($_[0], '<Any-Motion>' => sub {
7909		 my $str = show_below_route_str($_[0]);
7910		 if (defined $str && $str ne ''
7911		     && defined $c_balloon
7912		     && $use_c_balloon >= 2) {
7913		     # XXX before each $c_ballon->Popup should be this line (maybe move into sub?):
7914		     if ($leave_after) { $leave_after->cancel; undef $leave_after }
7915		     if (1) { $str = balloon_info_from_all_tags($c) }
7916		     if (defined $str) { $c_balloon->Popup($str); } # XXX if defined
7917		 }
7918	     });
7919    if ($_[0] =~ /^(show$|lsa-)/) { # XXX this special handling should go away
7920	$c->bind($_[0], '<Any-Leave>'  => sub { &leavepoint;
7921						&leavestr; } );
7922    } else {
7923	$c->bind($_[0], '<Any-Leave>'  => \&leavestr);
7924    }
7925}
7926
7927# Aufzeichnen eines Punktes
7928sub freerec_sub {
7929    my $e = $_[0]->XEvent;
7930    my($xx, $yy) = ($c->canvasx($e->x), $c->canvasy($e->y));
7931    require BBBikeAdvanced;
7932    buttonpoint(anti_transpose($xx, $yy));
7933}
7934
7935# freies Zeichnen von Punkten
7936sub freedraw_sub {
7937    my($w, $ax, $ay) = @_;
7938    my($xx, $yy);
7939    if (defined $ax && defined $ay) {
7940	($xx, $yy) = transpose($ax, $ay);
7941    } else {
7942	my($e) = $w->XEvent;
7943	($xx, $yy) = ($c->canvasx($e->x), $c->canvasy($e->y));
7944	($ax, $ay) = anti_transpose($xx, $yy);
7945    }
7946    return if !defined(addpoint_xy($ax, $ay, $xx, $yy));
7947    push @search_route_points, [join(",",@{ $realcoords[-1] }), POINT_MANUELL];
7948    if ($net && $map_mode ne MM_BUTTONPOINT) {
7949	push @act_search_route,
7950	    $net->route_to_name([$realcoords[-2], $realcoords[-1]],
7951				 -startindex => $#realcoords+1);
7952	add_new_point($net, join(",",@{ $realcoords[-1] }), -quiet => 1);
7953    }
7954    if ($map_mode ne MM_BUTTONPOINT) {
7955	set_flag('via');
7956	set_flag('ziel');
7957	set_cursor('ziel');
7958	$search_route_flag = 'ziel_cont';
7959    }
7960    updatekm();
7961    if (!$edit_mode && !$edit_normal_mode) {
7962	update_route_strname();
7963    }
7964}
7965
7966# Letzten Punkt l�schen
7967sub mouse_dellast {
7968    if ($special_edit ne '') {
7969	eval $special_edit . '_edit_mouse3(@_)';
7970	die $@ if $@;
7971    } else {
7972	dellast()
7973    }
7974}
7975
7976# delete_route light. Allerdings nicht ganz klar, wo das hier warum
7977# verwendet wird.
7978sub reset_button_command {
7979    reset_undo_route();
7980    undef $search_route_flag;
7981    if ($map_mode eq MM_SEARCH) {
7982	search_route_mouse(1);
7983    }
7984}
7985
7986sub change_net_type {
7987    undef $handicap_s_net;
7988    if ($net_type eq "r") {
7989	*set_coords = \&set_coords_rbahn;
7990    } elsif ($net_type eq "us") {
7991	*set_coords = \&set_coords_usbahn;
7992    } elsif ($net_type eq "rus") {
7993	*set_coords = \&set_coords_bahn;
7994    } elsif ($net_type eq 'wr') {
7995	*set_coords = \&set_coords_wasserrouten;
7996	if (!$str_draw{wr}) {
7997	    plot("str", "wr", -draw => 1);
7998	}
7999    } elsif ($net_type eq 'custom') {
8000	if (!keys %custom_net_str) {
8001	    require BBBikeAdvanced;
8002	    select_layers_for_net_dialog();
8003	}
8004	*set_coords = \&set_coords_custom;
8005    } else {
8006	*set_coords = \&set_coords_str;
8007    }
8008    if (defined $net) {
8009	make_net();
8010    }
8011}
8012
8013# Routenpunkt festlegen
8014sub set_route_point {
8015    return if $in_canvas_drag;
8016    my $e = $_[0]->XEvent;
8017    # auf Alt, Shift und CapsLock checken
8018    # bei Win95/NT ist 8 nicht CapsLock, sondern NumLock
8019    if ($Tk::VERSION < 800) {
8020	return if $e->s & (1+2+($os eq 'win' ? 0 : 8));
8021    } else {
8022	return if $e->s =~ /\b(Shift|Alt|Lock)-/;
8023    }
8024    if ($MM_DRAG_IS_OBSOLETE) {
8025	$c->scan('mark', $e->x, $e->y);
8026    }
8027    if ($map_mode eq MM_EDITPOINT) {
8028	my(@tags) = $c->gettags('current');
8029	if ($tags[0] eq 'pp' || $tags[0] =~ /^vf/ || $tags[0] =~ /^lsa/) {
8030	    $point_editor->set($tags[1]);
8031	}
8032    } elsif ($map_mode eq MM_INSERTPOINT) {
8033	insert_point_from_canvas($c);
8034    } elsif ($map_mode eq MM_CREATERELATION) {
8035	create_relation_from_canvas($c);
8036    } elsif ($map_mode eq MM_DRAG) {
8037	$c->scan('mark', $e->x, $e->y);
8038    } elsif ($special_edit ne '') {
8039	eval $special_edit . '_edit_mouse1(@_)';
8040	die $@ if $@;
8041    } elsif ($map_mode eq MM_CUSTOMCHOOSE_TAG || $map_mode eq MM_CUSTOMCHOOSE) {
8042	$customchoosecmd->($c, $e);
8043    } elsif ($map_mode eq MM_SEARCH) { # XXX doppelt
8044	#XXX defined $search_route_flag && ????
8045	if (defined $search_route_flag && $search_route_flag eq 'ziel_cont') {
8046	    search_route_mouse_cont();
8047	} elsif ($search_route_flag) {
8048	    search_route_mouse();
8049	} else {
8050	    warn "XXX activating....";
8051	    $search_route_flag = "start";
8052	    search_route_mouse();
8053	}
8054	Tk->break; # XXX insert more Tk->break in this subroutine?
8055    } elsif ($map_mode eq MM_BUTTONPOINT) {
8056	my $item = 'current';
8057	my(@tags) = $c->gettags($item);
8058	if ($tags[0] !~ /^(pp|o)$/) {
8059	    ($item) = find_below($c, "pp", "o");
8060	    if (!defined $item) {
8061		warn "Not over a <pp> or <o> point, got @tags";
8062		return;
8063	    }
8064	}
8065	require BBBikeAdvanced;
8066	my($rx,$ry) = buttonpoint(undef,undef,$item);
8067	freedraw_sub($_[0],$rx,$ry);
8068    } elsif ($map_mode eq MM_INFO) {
8069	show_info();
8070    } elsif ($map_mode =~ /^BBBike/) {
8071	my $itembutton_callback = $map_mode . '::itembutton';
8072	if (defined &$itembutton_callback) {
8073	    eval $itembutton_callback.'($c,$e)';
8074	    die $@ if $@;
8075	}
8076    } elsif ($map_mode eq MM_USEREDIT) {
8077	user_edit_street();
8078	Tk->break; # XXX insert more Tk->break in this subroutine?
8079    } elsif ($set_route_point{$map_mode}) {
8080	$set_route_point{$map_mode}->($e);
8081    } elsif ($map_mode ne MM_SEARCH) {
8082	addpoint_inter();
8083    }
8084}
8085
8086sub draw_street_numbers {
8087    # the coloring is german specific
8088    my($c,$strname,$abk,$coordlist_ref) = @_;
8089    use constant SMALLER_TABLES => 0.7;
8090    my $do_round = 0;
8091    my($type, $image, $nr);
8092    if ($city_obj->can("parse_street_type_nr")) {
8093	($type, $nr, $do_round, $image) = $city_obj->parse_street_type_nr($strname);
8094    }
8095    if (!defined $type) {
8096	# XXX handling of multiple street numbers? e.g. "F1, R1" or "B2/B5"?
8097	($type,$nr) = Strasse::parse_street_type_nr($strname);
8098    }
8099    if (defined $type) {
8100	my $dist = 0;
8101	my $drawn = 0;
8102	my $draw_sub = sub {
8103	    my $coord_i = shift;
8104	    my($midx,$midy) = Strassen::Util::middle(@{$coordlist_ref}[$coord_i..$coord_i+3]);
8105	    # XXX make public if
8106	    # XXX * I find a way of resizing for larger scales
8107	    # XXX * I should check the legal status of all these logos
8108	    if ($devel_host && defined $image && (my $p = get_image("strnr_$type", $datadir."/comments_route_img/$image"))) {
8109		$c->createImage
8110		    ($midx,$midy,-image => $p,
8111		     -tags => "$abk-label"); # no strnr (only for images) and strnr-$item here
8112	    } else {
8113		my($item, $r_item);
8114		# XXX It seems that at least the BAB number is off by maybe half a pixel,
8115		# but it's not possible in Tk to set subpixel positions.
8116		$item = $c->createText
8117		    ($midx,$midy,
8118		     -text => ($type =~ /^(B|BAB|DK|DW)$/ ? "" : $type) . (defined $nr ? $nr : ""),
8119		     -font => $scale < SMALLER_TABLES ? $font{'tiny'} : $font{'normal'},
8120		     -fill => ($do_round             ? 'white'  :
8121			       $type =~ /^(BAB|DK)$/ ? 'white'  :
8122			       $type =~ /^(F|R)$/    ? 'green4' :
8123			       'black'),
8124		     -tags => "$abk-label"); # no strnr (only for images) and strnr-$item here
8125		my(@bbox) = $c->bbox($item);
8126		if ($do_round) {
8127		    $r_item = $c->createOval
8128			($bbox[0]-2,$bbox[1]-2,$bbox[2]+2,$bbox[3]+2,
8129			 -fill => '#90d090',
8130			 -outline => 'black',
8131			 -width => 1,
8132			 -tags => ["$abk-label-bg", "strnr", "strnr-$item"],
8133			);
8134		} elsif ($type =~ m{^( B | BAB | DK | DW)$}x) {
8135		    my $size = $scale < SMALLER_TABLES ? 16 : 32;
8136		    my $p;
8137		    # prefer png because of alpha
8138		    if ($type eq 'B' || $type eq 'DW') {
8139			$p = get_image("strnr_B$size", "bundesstrasse_table_$size.png");
8140		    } elsif ($type eq 'BAB') {
8141			$p = get_image("strnr_BAB$size", "bab_table_$size.png");
8142		    } elsif ($type eq 'DK') {
8143			$p = get_image("strnr_DK$size", "droga_krajowa_table_$size.png");
8144		    }
8145		    if ($p) {
8146			# Manually corrected (-1/-1) to look with my standard font
8147			# (lucida sans ...)
8148			$r_item = $c->createImage(int(($bbox[2]+$bbox[0])/2) - ($size > 16 ? 1 : 0),
8149						  int(($bbox[3]+$bbox[1])/2) - ($size > 16 ? 1 : 0),
8150						  -image => $p,
8151						  -tags => ["$abk-label-bg", "strnr", "strnr-$item"],
8152						 );
8153		    } else {
8154			# XXX fallback to createRectangle below
8155			warn "Cannot get image for strnr_" . $type . $size;
8156		    }
8157		} else {
8158		    $r_item = $c->createRectangle
8159			($bbox[0]-2,$bbox[1]-2,$bbox[2]+2,$bbox[3]+2,
8160			 -fill => ($type eq 'B' ? 'yellow' :
8161				   ($type eq 'BAB' ? 'blue' :
8162				    'white')),
8163			 -outline => ($type eq 'BAB' ? 'white' :
8164				      ($type =~ /^(F|R)$/ ? 'green4' : 'black')),
8165			 -width => 2,
8166			 -tags => ["$abk-label-bg", "strnr", "strnr-$item"],
8167			);
8168		}
8169		$c->raise($item,$r_item);
8170	    }
8171	    $dist = 0;
8172	    $drawn++;
8173	};
8174
8175	for(my $ci=2; $ci<$#$coordlist_ref; $ci+=2) {
8176	    $dist += Strassen::Util::strecke([@{$coordlist_ref}[$ci-2,$ci-1]], [@{$coordlist_ref}[$ci,$ci+1]]);
8177	    if ($dist >= 400) { # should be in the magnitude of canvas height
8178		$draw_sub->($ci-2);
8179	    }
8180	}
8181	if (!$drawn) {
8182	    $draw_sub->(int($#$coordlist_ref/4)*2); # XXX ueberdenken
8183	}
8184    }
8185}
8186
8187# middle mouse button bindings
8188sub set_b2 {
8189    # first delete all canvas b2 bindings
8190    foreach my $bind (qw(ButtonPress-2 2 B2-Motion)) {
8191	$c->CanvasBind("<$bind>" => '');
8192    }
8193    if ($b2_mode == B2M_DELLAST) {
8194	$c->CanvasBind("<ButtonPress-2>" => \&mouse_dellast);
8195    } elsif ($b2_mode == B2M_AUTOSCROLL) {
8196	require Tk::Autoscroll;
8197	my %extra_args;
8198	$extra_args{'-speed'}  = $autoscroll_speed if ($autoscroll_speed);
8199	$extra_args{'-middle'} = !!$autoscroll_middle;
8200	Tk::Autoscroll::Init($c, %extra_args);
8201    } elsif ($b2_mode == B2M_SCAN || $b2_mode == B2M_FASTSCAN) {
8202	my $gain = $b2_mode == B2M_SCAN ? 1 : 10;
8203	$c->CanvasBind('<2>',
8204		       [sub {
8205			    my($w,$x,$y) = @_;
8206			    $w->scan('mark',$x,$y);
8207			},Tk::Ev('x'),Tk::Ev('y')]);
8208	$c->CanvasBind('<B2-Motion>',
8209		       [sub {
8210			    my($w,$x,$y) = @_;
8211			    $w->scan('dragto',$x,$y,$gain);
8212			},Tk::Ev('x'),Tk::Ev('y')]);
8213    } elsif ($b2_mode == B2M_CUSTOM && $b2m_customcmd) {
8214	$c->CanvasBind('<2>', [$b2m_customcmd, $c]);
8215	$c->CanvasBind('<B2-Motion>', '');
8216    } else {
8217	# no bindings
8218    }
8219    set_mouse_desc();
8220}
8221
8222# Setzen der Hilfstexte f�r die Maustastenbelegung
8223sub enter_leave_bind_for_help {
8224    my($w, $textref) = @_;
8225    my(@save_mouse_text);
8226    $w->bind
8227      ('<Enter>' => sub {
8228	   for my $i (1..3) {
8229	       if (defined $textref->[$i-1]) {
8230		   $save_mouse_text[$i] = $mouse_text[$i] || '';
8231		   $mouse_text[$i] = $textref->[$i-1];
8232	       }
8233	   }
8234       });
8235    $w->bind
8236      ('<Leave>' => sub {
8237	   for my $i (1..3) {
8238	       if (defined $save_mouse_text[$i]) {
8239		   $mouse_text[$i] = $save_mouse_text[$i];
8240		   undef $save_mouse_text[$i];
8241	       }
8242	   }
8243       });
8244}
8245
8246sub set_datadir {
8247    my($newdir, %args) = @_;
8248    if ($args{-clearold}) {
8249	@Strassen::datadirs = ();
8250    }
8251    if (defined $newdir && -d $newdir) {
8252	unshift @Strassen::datadirs, $newdir;
8253	$datadir = $newdir;
8254    } else {
8255	$datadir = $Strassen::datadirs[0];
8256    }
8257    if ($verbose) {
8258	print STDERR Mfmt("Aktuelles Datenverzeichnis ist %s\n", $datadir);
8259    }
8260
8261    my $metafile = "$newdir/meta.dd";
8262    if (-r $metafile) {
8263	require Geography::FromMeta;
8264	$city_obj = Geography::FromMeta->load_meta($metafile);
8265    }
8266
8267    # XXX The polar_coord_hack for osm2bbd
8268    if (-e "$datadir/Karte/Polar.pm") {
8269	lib->import($datadir);
8270    }
8271}
8272
8273# Beendet die Anwendung. Bei Bedarf werden Konfigurationsdateien gesichert.
8274# Tempor�re Dateien werden gel�scht.
8275sub exit_app {
8276
8277    if (Tk::Exists($top) && $ask_quit && $Tk::VERSION >= 800) {
8278	# deiconify seems to be required on Solaris CDE
8279	$top->deiconify;
8280	# XXX and raise makes the thing slow on KDE :-(
8281	$top->raise;
8282	return if ($top->messageBox
8283		   (-icon => "question",
8284		    -title => M"BBBike beenden",
8285		    -message => M"Soll BBBike beendet werden?",
8286		    -type => "YesNo") =~ /no/i); # XXX Sprache?
8287    }
8288
8289    exit_app_noninteractive();
8290}
8291
8292sub exit_app_noninteractive {
8293    save_last_loaded($last_loaded_obj);
8294    save_last_loaded($last_loaded_layers_obj) if $last_loaded_layers_obj;
8295
8296    if ($autosave_opts && defined $opt) {
8297	# get actual geometry
8298	$geometry = fix_geometry();
8299	# get actual font parameters
8300	if ($top->can("fontActual")) {
8301	    my %f_attr = $top->fontActual($font{'normal'});
8302	    $font_family = $f_attr{-family};
8303	    $font_size   = $f_attr{-size};
8304	    $font_weight = $f_attr{-weight};
8305	}
8306	# Reference power/speed
8307	my $speed_or_power = ($active_speed_power{Type} eq 'speed'
8308			      ? \@speed
8309			      : \@power
8310			     );
8311	$speed_power_reference_string = $active_speed_power{Type} . ":" . $speed_or_power->[$active_speed_power{Index}];
8312	# save options
8313	eval {
8314	    $opt->save_options;
8315	};
8316	if ($@) {
8317	    status_message($@, "warn");
8318	}
8319    }
8320
8321    if (defined &BBBikeServer::server_cleanup) {
8322	BBBikeServer::server_cleanup();
8323    }
8324
8325    my @todel;
8326    if (keys %tmpfiles) {
8327	push @todel, keys %tmpfiles;
8328	if ($INC{'GfxConvert.pm'}) {
8329	    push @todel, keys %GfxConvert::tmpfiles;
8330	}
8331    }
8332    unlink @todel if (@todel);
8333    $top->destroy if Tk::Exists($top);
8334    exit;
8335}
8336
8337######################################################################
8338
8339# Ver�ndern der aktuellen Default-Geschwindigkeit oder Default-Leistung.
8340# $type ist entweder "speed" oder "power"
8341# $index ist der zu �nderne Eintrag
8342sub change_active_speed_power {
8343    my($type, $index) = @_;
8344    my $has_old = 0;
8345    if (keys %active_speed_power) {
8346	# delete old
8347	my $frame = ($active_speed_power{Type} eq 'speed'
8348		     ? \@speed_frame
8349		     : \@power_frame
8350		    );
8351	my $inx = $active_speed_power{Index};
8352	if (defined $frame->[$inx]) {
8353	    $frame->[$inx]->configure(-relief => "raised",
8354				      -borderwidth => 1);
8355	}
8356	$has_old = 1;
8357    }
8358
8359    %active_speed_power = (Type  => $type,
8360			   Index => $index);
8361
8362    # set new
8363    my $frame = ($active_speed_power{Type} eq 'speed'
8364		 ? \@speed_frame
8365		 : \@power_frame
8366		);
8367    my $inx = $active_speed_power{Index};
8368    if (defined $frame->[$inx]) {
8369	$frame->[$inx]->configure(-relief => "raised",
8370				  -borderwidth => 2);
8371    }
8372
8373    calc_ampel_optimierung() if $ampel_optimierung;
8374
8375    redraw_path() if $has_old;
8376}
8377
8378sub change_ampel_count {
8379    my($type, $index) = @_;
8380    $ampel_count->{$type}[$index] = !$ampel_count->{$type}[$index];
8381    if ($ampel_count->{$type}[$index]) {
8382	$ampel_count_button->{$type}[$index]->configure
8383	  (-image => $ampel_klein_photo);
8384	updatekm();
8385    } else {
8386	$ampel_count_button->{$type}[$index]->configure
8387	  (-image => $ampel_klein_grey_photo);
8388	updatekm();
8389    }
8390}
8391
8392sub change_kopfstein_count {
8393    my($type, $index) = @_;
8394    $kopfstein_count->{$type}[$index] = !$kopfstein_count->{$type}[$index];
8395    if ($kopfstein_count->{$type}[$index]) {
8396	$kopfstein_count_button->{$type}[$index]->configure
8397	  (-image => $kopfstein_klein_photo);
8398	updatekm();
8399    } else {
8400	$kopfstein_count_button->{$type}[$index]->configure
8401	  (-image => $kopfstein_klein_grey_photo);
8402	updatekm();
8403    }
8404}
8405
8406# Erzeugt den String f�r den Label der Leistung
8407sub mk_power_txt {
8408    my($i) = @_;
8409    if (defined $i) {
8410	$power_txt[$i] = "$power[$i] W";
8411    } else {
8412	for($i = 0; $i <= $#power; $i++) {
8413	    $power_txt[$i] = "$power[$i] W";
8414	}
8415    }
8416}
8417
8418# Dialog zum Eingeben der Leistung
8419### AutoLoad Sub
8420sub enter_power {
8421    my($i) = @_;
8422    my $t = redisplay_top($top, "power-$i", -title => M"Leistung");
8423    return if !defined $t;
8424    my $var = $power[$i];
8425    my $scale_var = $var;
8426    my $row = 0;
8427    $t->Label(-text => M('Leistung (in W)').':'
8428	     )->grid(-row => $row, -column => 0);
8429    my $e = $t->Entry(-textvariable => \$var,
8430		      -width => 4)->grid(-row => $row, -column => 1);
8431    $e->tabFocus;
8432    $row++;
8433    $t->Scale(-from => 10,
8434	      -to => 500,
8435	      -bigincrement => 50,
8436	      -resolution => 5,
8437	      -orient => 'horiz',
8438	      -showvalue => 0,
8439	      -variable => \$scale_var,
8440	      -command => sub { $var = $scale_var },
8441	     )->grid(-row => $row, -column => 1, -sticky => 'we');
8442    $row++;
8443    my $ref_row = $row;
8444    my $create_reference_label = sub {
8445	$t->Label(-text => M"Referenzleistung",
8446		 )->grid(-row => $ref_row, -column => 0, -columnspan => 2);
8447    };
8448    my $is_reference = ($active_speed_power{Type} eq 'power' &&
8449			$active_speed_power{Index} eq $i);
8450    if (!$is_reference) {
8451	my $rb;
8452	$rb = $t->Button
8453	    (-text => M"Als Referenzleistung verwenden",
8454	     -command => sub {
8455		 change_active_speed_power("power", $i);
8456		 $create_reference_label->();
8457		 $rb->gridForget;
8458	     },
8459	    )->grid(-row => $row, -column => 0, -columnspan => 2);
8460	$row++;
8461    } else {
8462	$create_reference_label->();
8463	$row++;
8464    }
8465    my $close_window = sub { $t->destroy; };
8466    my $apply_window = sub { IncBusy($t);
8467			     eval {
8468				 $power[$i] = $var;
8469				 after_changed_power($i);
8470			     };
8471			     DecBusy($t);
8472			 };
8473    my $ok_window    = sub { &$close_window;
8474			     &$apply_window };
8475    my $bf = $t->Frame->grid(-row => $row, -column => 0,
8476			     -columnspan => 2);
8477    my $okb = $bf->Button
8478      (Name => 'ok',
8479       -command => $ok_window)->grid(-row => 0, -column => 0,
8480				     -sticky => 'ew');
8481    $bf->Button(Name => 'apply',
8482		-command => $apply_window)->grid(-row => 0, -column => 1,
8483						 -sticky => 'ew');
8484    my $cb = $bf->Button
8485      (Name => 'close',
8486       -command => $close_window)->grid(-row => 0, -column => 2,
8487					-sticky => 'ew');
8488
8489    $t->bind('<Return>' => sub { $okb->invoke });
8490    $t->bind('<<CloseWin>>' => sub { $cb->invoke });
8491
8492    my_popup($t);
8493}
8494
8495sub after_changed_power {
8496    my($i) = @_; # index
8497    my $is_reference = ($active_speed_power{Type} eq 'power' &&
8498			$active_speed_power{Index} eq $i);
8499    mk_power_txt($i);
8500    calc_ampel_optimierung()
8501	if $ampel_optimierung && $is_reference;
8502    recalc_bikepwr();
8503    updatekm();
8504}
8505
8506sub get_reference_journey_time {
8507    my $key = $active_speed_power{Type} eq 'power' ? 'PowerTimeSeconds' : 'TimeSeconds';
8508    $act_value{$key}->[$active_speed_power{Index}];
8509}
8510
8511# Erzeugt den String f�r den Label der Geschwindigkeit
8512sub mk_speed_txt {
8513    my($i) = @_;
8514    if (defined $i) {
8515	$speed_txt[$i] = "$speed[$i] km/h";
8516    } else {
8517	for($i = 0; $i <= $#speed; $i++) {
8518	    $speed_txt[$i] = "$speed[$i] km/h";
8519	}
8520    }
8521}
8522
8523# Dialog zum Eingeben der Geschwindigkeit
8524### AutoLoad Sub
8525sub enter_speed {
8526    my($i) = @_;
8527    my $t = redisplay_top($top, "speed-$i", -title => M"Geschwindigkeit");
8528    return if !defined $t;
8529    my $var = $speed[$i];
8530    my $scale_var = $var;
8531    my $row = 0;
8532    $t->Label(-text => M('Geschwindigkeit (in km/h)').':'
8533	     )->grid(-row => $row, -column => 0);
8534    my $e = $t->Entry(-textvariable => \$var,
8535		      -width => 3)->grid(-row => $row, -column => 1);
8536    $e->tabFocus;
8537    $row++;
8538    $t->Scale(-from => 5,
8539	      -to => 60,
8540	      -bigincrement => 5,
8541	      -resolution => 1,
8542	      -orient => 'horiz',
8543	      -showvalue => 0,
8544	      -variable => \$scale_var,
8545	      -command => sub { $var = $scale_var },
8546	     )->grid(-row => $row, -column => 1, -sticky => 'we');
8547    $row++;
8548    my $ref_row = $row;
8549    my $create_reference_label = sub {
8550	$t->Label(-text => M"Referenzgeschwindigkeit",
8551		 )->grid(-row => $ref_row, -column => 0, -columnspan => 2);
8552    };
8553    my $is_reference = ($active_speed_power{Type} eq 'speed' &&
8554			$active_speed_power{Index} eq $i);
8555    if (!$is_reference) {
8556	my $rb;
8557	$rb = $t->Button
8558	    (-text => M"Als Referenzgeschwindigkeit verwenden",
8559	     -command => sub {
8560		 change_active_speed_power("speed", $i);
8561		 $create_reference_label->();
8562		 $rb->gridForget;
8563	     },
8564	    )->grid(-row => $row, -column => 0, -columnspan => 2);
8565	$row++;
8566    } else {
8567	$create_reference_label->();
8568	$row++;
8569    }
8570    my $close_window = sub { $t->destroy; };
8571    my $apply_window = sub { IncBusy($t);
8572			     eval {
8573				 $speed[$i] = $var;
8574				 mk_speed_txt($i);
8575				 calc_ampel_optimierung()
8576				     if $ampel_optimierung && $is_reference;
8577				 updatekm();
8578			     };
8579			     DecBusy($t);
8580			 };
8581    my $ok_window    = sub { &$close_window;
8582			     &$apply_window };
8583    my $bf = $t->Frame->grid(-row => $row, -column => 0,
8584			     -columnspan => 2);
8585    my $okb = $bf->Button
8586      (Name => 'ok',
8587       -command => $ok_window)->grid(-row => 0, -column => 0,
8588				     -sticky => 'ew');
8589    $bf->Button(Name => 'apply',
8590		-command => $apply_window)->grid(-row => 0, -column => 1,
8591						 -sticky => 'ew');
8592    my $cb = $bf->Button
8593      (Name => 'close',
8594       -command => $close_window)->grid(-row => 0, -column => 2,
8595					-sticky => 'ew');
8596    $t->bind('<Return>' => sub { $okb->invoke });
8597    $t->bind('<<CloseWin>>' => sub { $cb->invoke });
8598
8599    my_popup($t);
8600}
8601
8602# Dialog zum Eingeben der Windgeschwindigkeit und -richtung
8603### AutoLoad Sub
8604sub enter_wind {
8605    require Tk::Optionmenu;
8606    require Met::Wind;
8607    import Met::Wind;
8608    my $t = redisplay_top($top, "wind", -title => M"Wind");
8609    return if !defined $t;
8610    my @var = ($winddir, $wind_v_max, $wind_v);
8611    my @scale_var = @var;
8612    my(@e, @om, @sc);
8613    my %wind_range =
8614      ('Beaufort' => [0, 16],
8615       'm/s' => [0, 56],
8616       'km/h' => [0, 200],
8617       'mi/h' => [0, 125],
8618       'kn' => [0, 100]);
8619    my @wind_unit = (undef, 'm/s', 'm/s');
8620    my @last_wind_unit = @wind_unit;
8621    $t->Label(-text => M("Windrichtung").":")->grid(-row => 0, -column => 0);
8622    $t->Label(-text => M("max. Windgeschwindigkeit").":"
8623	     )->grid(-row => 1, -column => 0);
8624    $t->Label(-text => M("mitt. Windgeschwindigkeit").":"
8625	     )->grid(-row => 2, -column => 0);
8626
8627    my $rbf = $t->Frame->grid(-row => 0, -column => 1, -columnspan => 10);
8628    foreach my $spec ([qw(sw 0 2)],
8629		      [qw(w  0 1)],
8630		      [qw(nw 0 0)],
8631		      [qw(n  1 0)],
8632		      [qw(ne 2 0)],
8633		      [qw(e  2 1)],
8634		      [qw(se 2 2)],
8635		      [qw(s  1 2)]) {
8636	my($windri, $col, $row) = @$spec;
8637	$col*=2;
8638	$rbf->Label(-text => uc($windri))->grid(-row => $row,
8639						-column => $col);
8640	$rbf->Radiobutton(-variable => \$var[0], -value => $windri,
8641			 )->grid(-row => $row, -column => $col+1);
8642    }
8643    if (defined $windrose2_photo) {
8644	$rbf->Label(-image => $windrose2_photo)->grid(-row => 1,
8645						      -column => 1*2,
8646						      -columnspan => 2);
8647    }
8648
8649    for(my $i = 1; $i <= $#var; $i++) {
8650	$e[$i] = $t->Entry(-textvariable => \$var[$i],
8651			   -width => 5)->grid(-row => $i, -column => 1);
8652    }
8653
8654    for(my $i = 1; $i <= $#var; $i++) {
8655	my $ii = $i;
8656	$om[$i] = $t->Optionmenu
8657	  (-takefocus => 1,
8658	   -highlightthickness => 2,
8659	   -variable => \$wind_unit[$i],
8660	   -command => sub {
8661	       if ($last_wind_unit[$ii] ne $wind_unit[$ii]) {
8662		   my $old_var = $var[$ii];
8663		   $sc[$ii]->configure
8664		     (-from => $wind_range{$wind_unit[$ii]}->[0],
8665		      -to   => $wind_range{$wind_unit[$ii]}->[1],
8666		     );
8667		   $var[$ii] = wind_velocity([$old_var,
8668					      $last_wind_unit[$ii]],
8669					     $wind_unit[$ii]);
8670		   $last_wind_unit[$ii] = $wind_unit[$ii];
8671	       }
8672	   })->grid(-row => $i, -column => 2);
8673	$om[$i]->addOptions('m/s', 'km/h', 'Beaufort', 'mi/h', 'kn');
8674	$sc[$i] = $t->Scale(-from => $wind_range{$wind_unit[$i]}->[0],
8675			    -to   => $wind_range{$wind_unit[$i]}->[1],
8676			    -orient => 'horiz',
8677			    -showvalue => 0,
8678			    -variable => \$scale_var[$i],
8679			    -command => sub { $var[$ii] = $scale_var[$ii] },
8680			   )->grid(-row => $i, -column => 3, -sticky => 'we');
8681    }
8682
8683    $rbf->focus;
8684    for(my $i = 1; $i < $#var; $i++) {
8685	my $ii = $i;
8686	$e[$i]->bind('<Return>' => sub { $e[$ii+1]->tabFocus });
8687    }
8688
8689    my $apply_window = sub {
8690	for(my $i = 1; $i <= $#var; $i++) {
8691	    if ($wind_unit[$i] ne 'm/s') {
8692		$om[$i]->setOption('m/s');
8693		# Der Rest wird automatisch im -command vom Optionmenu
8694		# erledigt.
8695	    }
8696	}
8697	if (defined $var[0] and $var[0] =~ /^([ns][ew]?|[ew])$/i) {
8698	    analyze_wind(undef, undef, @var);
8699	    $wind = 1; # XXX ?
8700	    if ($coloring eq 'wind') {
8701		redraw_path();
8702		updatekm();
8703	    }
8704	} else {
8705	    status_message(Mfmt("Unerlaubte Windrichtung: <%s>", $var[0]),
8706			   'warn');
8707	}
8708    };
8709    my $close_window = sub { $t->destroy };
8710    my $ok_window = sub { &$close_window;
8711			  &$apply_window; };
8712
8713    my $bf = $t->Frame->grid(-row => 3, -column => 0,
8714			     -columnspan => 10, -sticky => 'we');
8715    my $okb = $bf->Button(Name => 'ok',
8716			  -command => $ok_window,
8717			 )->pack(-side => 'left', -fill => 'x', -expand => 1);
8718    $bf->Button(Name => 'apply',
8719		-command => $apply_window,
8720	       )->pack(-side => 'left', -fill => 'x', -expand => 1);
8721    my $cb = $bf->Button(Name => 'close',
8722			 -command => $close_window,
8723			)->pack(-side => 'left', -fill => 'x', -expand => 1);
8724    $bf->Label->pack(-side => 'left', -fill => 'x', -expand => 1);
8725    $bf->Button(-text => M"Beaufort-Tabelle",
8726		-command => sub {
8727		    Met::Wind::beaufort_table
8728		      ($t,
8729		       -command => sub {
8730			   my($num, $unit, $toplevel) = @_;
8731			   $var[2] = Met::Wind::wind_velocity([$num, $unit],
8732							      $wind_unit[2]);
8733			   $toplevel->destroy;
8734		       },
8735		      )
8736		  },
8737	       )->pack(-side => 'left', -fill => 'x', -expand => 1);
8738
8739    $e[-1]->bind('<Return>' => sub { $okb->invoke });
8740    $t->bind('<<CloseWin>>' => sub { $cb->invoke });
8741    my_popup($t);
8742}
8743
8744# Dialog zum Eingeben des Mapscales
8745### AutoLoad Sub
8746sub enter_scale {
8747    my(%args) = @_;
8748    my($x,$y) = @{ $args{-preserveposition} || [] };
8749    return unless $mapscale =~ /:\s*(\d+)/;
8750    my($old_mapscale, $new_mapscale, $new_mapscale_scale);
8751    $old_mapscale = $new_mapscale = $new_mapscale_scale = $1;
8752
8753    my $t = redisplay_top($top, "scale", -title => M"Ma�stab");
8754    return if !defined $t;
8755    $t->Label(-text => M"Ma�stab 1:"
8756	     )->grid(-row => 0, -column => 0, -sticky => 'e');
8757    my $e = $t->Entry(-textvariable => \$new_mapscale,
8758		      -width => 8)->grid(-row => 0, -column => 1,
8759					 -sticky => 'ew');
8760    $e->tabFocus;
8761    my $sc;
8762    if (defined $default_mapscale && $default_mapscale != 0) {
8763	$t->Button(Name => 'default',
8764		   -command => sub {
8765		       $new_mapscale = $new_mapscale_scale = $default_mapscale;
8766		   },
8767		  )->grid(-row => 0, -column => 2);
8768    }
8769    my $Scale = 'Scale';
8770    my %scaleargs = (-bigincrement => 5000,
8771		     -resolution => 1000,
8772		     -showvalue  => 0,
8773		    );
8774    eval {
8775	require Tk::LogScale;
8776	require Tie::Watch;
8777	$Scale = 'LogScale';
8778	%scaleargs = (-resolution => 0.01,
8779		      -showvalue => 0);
8780    };
8781    my $scale = $t->$Scale
8782      (-from => 1000,
8783       -to => 3_000_000,
8784       %scaleargs,
8785       -orient => 'horiz',
8786       -variable => \$new_mapscale_scale,
8787       -command => sub { $new_mapscale = int($new_mapscale_scale); },
8788      )->grid(-row => 1, -column => 1,
8789	      -columnspan => 2,
8790	      -sticky => 'we');
8791    my $close_window = sub { $t->destroy; };
8792    my $apply_window = sub {
8793	IncBusy($t);
8794	eval {
8795	    if ($old_mapscale != $new_mapscale and $new_mapscale != 0) {
8796		scalecanvas($c, $old_mapscale/$new_mapscale, $x, $y);
8797		if ($mapscale =~ /:\s*(\d+)/) {
8798		    $old_mapscale = $new_mapscale = $1;
8799		    if (Tk::Exists($scale)) {
8800			# Die Abfrage ist ein Workaround, ansonsten
8801			# gibt es einen Perl-Panic, wenn Tk::LogScale
8802			# verwendet wird. M�glicher Grund: es wird auf
8803			# eine Tie-Variable zugegriffen, die
8804			# anscheinend schon zerst�rt ist (?), bzw.
8805			# deren Tie-Objekt zerst�rt ist.
8806			$new_mapscale_scale = $1;
8807		    }
8808		} else {
8809		    die Mfmt("Fehler beim Parsen des Massstabs: %s",
8810			     $mapscale);
8811		}
8812	    }
8813	};
8814	DecBusy($t);
8815    };
8816    my $ok_window    = sub { &$close_window;
8817			     &$apply_window };
8818    my $bf = $t->Frame->grid(-row => 2, -column => 0,
8819			     -columnspan => 2);
8820    my $okb = $bf->Button
8821      (Name => 'ok',
8822       -command => $ok_window)->grid(-row => 0, -column => 0,
8823				     -sticky => 'ew');
8824    $bf->Button(Name => 'apply',
8825		-command => $apply_window)->grid(-row => 0, -column => 1,
8826						 -sticky => 'ew');
8827    my $cb = $bf->Button
8828      (Name => 'close',
8829       -command => $close_window)->grid(-row => 0, -column => 2,
8830					-sticky => 'ew');
8831
8832    $t->bind('<Return>' => sub { $okb->invoke });
8833    $t->bind('<<CloseWin>>' => sub { $cb->invoke });
8834
8835    my_popup($t);
8836}
8837
8838# �ndert den -state einer gesamten Widgethierarchie unter $frame
8839# $enable gibt an, ob die Widgets de/aktiviert werden sollen
8840# $exceptions ist ein Hash, wobei die Keys die Ausnahmen unter den Widgets
8841# angeben
8842### AutoLoad Sub
8843sub change_state_all {
8844    my($frame, $enable, $exceptions) = @_;
8845    foreach ($frame->children) {
8846	next if exists $exceptions->{$_};
8847	if ($enable) {
8848	    eval { $_->configure(-state => 'normal') };
8849	} else {
8850	    eval { $_->configure(-state => 'disabled') };
8851	}
8852	if ($_->can('children')) {
8853	    change_state_all($_, $enable, $exceptions);
8854	}
8855    }
8856}
8857
8858sub toggle_enter_opt_preferences {
8859    if ($show_enter_opt_preferences) {
8860	enter_opt_preferences();
8861    } else {
8862	$toplevel{"optparam"}->withdraw
8863	    if Tk::Exists($toplevel{"optparam"});
8864    }
8865}
8866
8867# Dialog zum Einstellen der Optimierungseinstellungen
8868### AutoLoad Sub
8869sub enter_opt_preferences {
8870    my($i) = @_;
8871    $show_enter_opt_preferences = 1;
8872    my $t = redisplay_top($top, "optparam", -title => M"Optimierungsparameter");
8873    return if !defined $t;
8874    my $withdraw = sub { $show_enter_opt_preferences = 0;
8875			 $t->withdraw;
8876		     };
8877    $t->protocol('WM_DELETE_WINDOW', $withdraw);
8878    require Tk::NoteBook;
8879    my $nb = $t->NoteBook->grid(-row => 0, -column => 0,
8880				-columnspan => 3);
8881    my %var = %qualitaet_s_speed;
8882    my %var4 = %handicap_s_speed;
8883    my %var2 = %strcat_speed;
8884    my %var3 = %radwege_speed;
8885    my $Entry = 'Entry';
8886    my @EntryArgs = ();
8887    eval {
8888	require Tk::NumEntry;
8889	$Entry = 'NumEntry';
8890	@EntryArgs = (-minvalue => 1);
8891    };
8892    my @act_page;
8893    $act_page[0] = $nb->add("q", -label => M"Stra�enqualit�t");
8894    my $gridy = 0;
8895    $act_page[0]->Label(-text => M"Stra�enqualit�t",
8896		     -font => $font{'bold'})->grid(-row => $gridy,
8897						   -column => 0);
8898    $act_page[0]->Label(-text => M"max. Geschwindigkeit",
8899		     -font => $font{'bold'})->grid(-row => $gridy,
8900						   -column => 1,
8901						   -columnspan => 2,
8902						  );
8903    $gridy++;
8904#XXX geht nicht...warum ???
8905#     $t->bind('<Return>' => sub {
8906# warn $t->focusCurrent;
8907# 		   if ($t->focusCurrent->isa('Tk::Entry')) {
8908# 		       $t->focusNext->tabFocus;
8909# 		   }
8910# 	       });
8911
8912    my @e;
8913    for (0 .. 3) {
8914	my $i = $_;
8915	$act_page[0]->Label(-text => "Q$i: " .
8916			          $category_attrib{"Q$i"}->[ATTRIB_LONG],
8917			)->grid(-row => $gridy, -column => 0, -sticky => 'w');
8918	my $w;
8919	$w = $e[$i] = $act_page[0]->$Entry(-textvariable => \$var{"Q$i"},
8920					   -width => 3,
8921					   @EntryArgs,
8922					  );
8923	$w->grid(-row => $gridy, -column => 1, -sticky => 'e');
8924	$act_page[0]->Label(-text => 'km/h')->grid(-row => $gridy, -column => 2,
8925						-sticky => 'w');
8926	$gridy++;
8927    }
8928    $e[0]->tabFocus;
8929
8930    {
8931	require Tk::Optionmenu;
8932	my $name2inx =
8933	    {M"egal" => 0,
8934	     M"Kopfsteinpflaster und schlechte Fahrbahnen vermeiden" => 1,
8935	     M"nur sehr gute Bel�ge bevorzugen (rennradtauglich)" => 2,
8936	     M"freie Eingabe" => 3,
8937	    };
8938	my $default = M"freie Eingabe";
8939	my $o = $act_page[0]->Optionmenu
8940	    (-options => [sort { $name2inx->{$a} <=> $name2inx->{$b} } keys %$name2inx],
8941	     -variable => \$default,
8942	     -command => sub {
8943		 my $inx = $name2inx->{$default};
8944		 my $speed = get_active_speed();
8945		 if ($inx == 0) {
8946		     while(my($k,$v) = each %var) {
8947			 $var{$k} = $speed;
8948		     }
8949		 } elsif ($inx == 3) {
8950		     # no op
8951		 } else {
8952		     my $velocity_kmh = $speed;
8953		     # Taken from bbbike.cgi
8954		     my %penalty;
8955		     my %max_limit;
8956		     if ($inx == 2) { # rennradtauglich
8957			 %penalty = ( "Q0" => 1,
8958				      "Q1" => 1.2,
8959				      "Q2" => 1.6,
8960				      "Q3" => 2 );
8961			 %max_limit = ( Q1 => $velocity_kmh / 25,
8962					Q2 => $velocity_kmh / 16,
8963					Q3 => $velocity_kmh / 10 );
8964		     } else {
8965			 %penalty = ( "Q0" => 1,
8966				      "Q1" => 1,
8967				      "Q2" => 1.5,
8968				      "Q3" => 1.8 );
8969			 %max_limit = ( Q1 => $velocity_kmh / 25,
8970					Q2 => $velocity_kmh / 18,
8971					Q3 => $velocity_kmh / 13 );
8972		     }
8973		     my $min_limit = $velocity_kmh / 5;
8974		     for my $q (keys %max_limit) {
8975			 if ($penalty{$q} < $max_limit{$q}) {
8976			     $penalty{$q} = $max_limit{$q};
8977			 }
8978		     }
8979		     if ($velocity_kmh > 5) {
8980			 for my $q (keys %penalty) {
8981			     if ($penalty{$q} > $min_limit) {
8982				 $penalty{$q} = $min_limit;
8983			     }
8984			 }
8985		     }
8986
8987		     while(my($k,$v) = each %penalty) {
8988			 $var{$k} = int($speed/$v);
8989		     }
8990		 }
8991	     }
8992	    )->grid(-row => $gridy,
8993		    -column => 0,
8994		    -sticky => 'w');
8995    }
8996
8997    my $cb1;
8998    $cb1 = $act_page[0]->Checkbutton
8999      (-text => M"Verwenden",
9000       -variable => \$qualitaet_s_optimierung,
9001       -command => sub { change_state_all($act_page[0], $qualitaet_s_optimierung,
9002					  {$cb1=>1}); },
9003      )->grid(-row => $gridy++,
9004	      -column => 2,
9005	      -sticky => 'e');
9006    change_state_all($act_page[0], $qualitaet_s_optimierung, {$cb1=>1});
9007
9008    #######
9009    $act_page[1] = $nb->add("cat", -label => M"Stra�enkategorien",
9010-createcmd => sub {
9011    $gridy = 0;
9012    $act_page[1]->Label(-text => M"Stra�enkategorien",
9013		     -font => $font{'bold'})->grid(-row => $gridy,
9014						   -column => 0);
9015    $act_page[1]->Label(-text => M"max. Geschwindigkeit",
9016		     -font => $font{'bold'})->grid(-row => $gridy,
9017						   -column => 1,
9018						   -columnspan => 2,
9019						  );
9020    $gridy++;
9021    # XXX no BAB here!
9022    for (qw(HH H NH N NN)) {
9023	my $i = $_;
9024	next if $_ eq 'NH' && !$city_obj->is_osm_source && !$devel_host; # XXX maybe only restrict in edit mode???
9025	$act_page[1]->Label(-text => $category_attrib{$i}->[ATTRIB_PLURAL] . ": "
9026			)->grid(-row => $gridy, -column => 0,
9027				-sticky => 'w');
9028	my $w = $act_page[1]->$Entry(-textvariable => \$var2{$i},
9029				     -width => 3,
9030				     @EntryArgs,
9031				    );
9032	# bind return XXX
9033	$w->grid(-row => $gridy, -column => 1, -sticky => 'e');
9034	$act_page[1]->Label(-text => 'km/h')->grid(-row => $gridy, -column => 2,
9035						-sticky => 'w');
9036	$gridy++;
9037    }
9038
9039    {
9040	require Tk::Optionmenu;
9041	# Die Verwendung von $name2inx ist nur ein Workaround...
9042	# Eigentlich w�rde ich die [Name => Wert]-Notation von Optionmenu
9043	# verwenden wollen, aber das geht nicht :-(
9044	my $name2inx =
9045	    {M"Nur Hauptstra�en" => 0,
9046	     M"Hauptstra�en bevorzugen" => 1,
9047	     M"Alle Stra�en ber�cksichtigen" => 2,
9048	     M"Nebenstra�en bevorzugen" => 3,
9049	     M"Nur Nebenstra�en" => 4,
9050	    };
9051	my $default = M"Alle Stra�en ber�cksichtigen";
9052	my $o = $act_page[1]->Optionmenu
9053	  (-options => [sort { $name2inx->{$a} <=> $name2inx->{$b} } keys %$name2inx],
9054	   -variable => \$default,
9055	   -command => sub {
9056	       my $i = 0;
9057	       # XXX BAB
9058	       # XXX should be relative to current speed, like in cgi!
9059	       # XXX rethink penalty for NH, maybe like in cgi!
9060	       for (qw(HH H NH N NN)) {
9061		   $var2{$_} = [[100,100,100,1,1],
9062				[100,100,100,12,12],
9063				[100,100,100,100,100],
9064				[12,12,100,100,100],
9065				[1,1,100,100,100],
9066			       ]->[$name2inx->{$default}][$i];
9067		   $i++;
9068	       }
9069	   })->grid(-row => $gridy,
9070		    -column => 0,
9071		    -sticky => 'w');
9072    }
9073
9074    my $cb2;
9075    $cb2 = $act_page[1]->Checkbutton
9076      (-text => M"Verwenden",
9077       -variable => \$strcat_optimierung,
9078       -command => sub { change_state_all($act_page[1], $strcat_optimierung,
9079					  {$cb2=>2}); },
9080      )->grid(-row => $gridy++,
9081	      -column => 2,
9082	      -sticky => 'e');
9083    change_state_all($act_page[1], $strcat_optimierung, {$cb2=>2});
9084});
9085    #######
9086    $act_page[2] = $nb->add("rw", -label => M"Radwege",
9087-createcmd => sub {
9088    $gridy = 0;
9089    $act_page[2]->Label(-text => M"Radwege",
9090		     -font => $font{'bold'})->grid(-row => $gridy,
9091						   -column => 0);
9092    $act_page[2]->Label(-text => M"max. Geschwindigkeit",
9093		     -font => $font{'bold'})->grid(-row => $gridy,
9094						   -column => 1,
9095						   -columnspan => 2,
9096						  );
9097    $gridy++;
9098    require Radwege;
9099    for (@Radwege::bbbike_category_order) {
9100	my $i = $_;
9101	$act_page[2]->Label(-text => $Radwege::bez{$i} .": "
9102			)->grid(-row => $gridy, -column => 0,
9103				-sticky => 'w');
9104	my $w = $act_page[2]->$Entry(-textvariable => \$var3{$i},
9105				     -width => 3,
9106				     @EntryArgs,
9107				    );
9108	# bind return XXX
9109	$w->grid(-row => $gridy, -column => 1, -sticky => 'e');
9110	$act_page[2]->Label(-text => 'km/h')->grid(-row => $gridy, -column => 2,
9111						-sticky => 'w');
9112	$gridy++;
9113    }
9114
9115    my $N_RW_cb;
9116    my $N_RW1_cb;
9117    my $cb3;
9118    $cb3 = $act_page[2]->Checkbutton
9119      (-text => M"Verwenden",
9120       -variable => \$radwege_optimierung,
9121       -command => sub { change_state_all($act_page[2], $radwege_optimierung,
9122					  {$cb3=>1,$N_RW_cb=>1,$N_RW1_cb=>1}); },
9123      )->grid(-row => $gridy++,
9124	      -column => 2,
9125	      -sticky => 'e');
9126    change_state_all($act_page[2], $radwege_optimierung, {$cb3=>3});
9127
9128    $N_RW_cb = $act_page[2]->Checkbutton
9129	(-text => M"Hauptstra�en ohne Radwege/Busspuren meiden",
9130	 -variable => \$N_RW_optimization,
9131	 -command => sub {
9132	     if ($N_RW_optimization) {
9133		 $radwege_optimierung = 0;
9134		 $strcat_optimierung = 0;
9135		 $N_RW1_optimization = 0;
9136		 change_state_all($act_page[2], $radwege_optimierung,
9137				  {$cb3=>1,$N_RW_cb=>1,$N_RW1_cb=>1});
9138	     }
9139	 },
9140	)->grid(-row => $gridy++,
9141		-column => 0,
9142		-sticky => "w");
9143    $N_RW1_cb = $act_page[2]->Checkbutton
9144	(-text => M"Hauptstra�en ohne Radwege meiden",
9145	 -variable => \$N_RW1_optimization,
9146	 -command => sub {
9147	     if ($N_RW1_optimization) {
9148		 $radwege_optimierung = 0;
9149		 $strcat_optimierung = 0;
9150		 $N_RW_optimization = 0;
9151		 change_state_all($act_page[2], $radwege_optimierung,
9152				  {$cb3=>1,$N_RW_cb=>1,$N_RW1_cb=>1});
9153	     }
9154	 },
9155	)->grid(-row => $gridy++,
9156		-column => 0,
9157		-sticky => "w");
9158});
9159
9160    #######
9161    $act_page[3] = $nb->add("lsa", -label => M"Ampel-Optimierung",
9162-createcmd => sub {
9163    $gridy = 0;
9164    $act_page[3]->Label(-text => M"Ampel-Optimierung",
9165		     -font => $font{'bold'})->grid(-row => $gridy,
9166						   -column => 0);
9167#      $act_page[3]->Label(-text => M"max. Geschwindigkeit",
9168#  		     -font => $font{'bold'})->grid(-row => $gridy,
9169#  						   -column => 1,
9170#  						   -columnspan => 2,
9171#  						  );
9172    $gridy++;
9173
9174    my $dgf = $act_page[3]->Frame->grid(-row => $gridy++, -column => 0,
9175				     -sticky => 'w', -columnspan => 3);
9176    my $gridyy = 0;
9177    $dgf->Label(-text => M("Durchschnittsgeschwindigkeit (km/h)").":"
9178	       )->grid(-row => $gridyy, -column => 0,
9179		       -sticky => 'w');
9180    my $gridxx = 1;
9181    for (qw(10 15 20 25 30)) {
9182	$dgf->Radiobutton(-text => $_,
9183			  -variable => \$average_v,
9184			  -value => $_,
9185			  -command => \&calc_ampel_optimierung,
9186			 )->grid(-row => $gridyy, -column => $gridxx++,
9187				 -sticky => 'w');
9188    }
9189    $gridyy++;
9190    my $am_frame = $dgf->Frame->grid(-row => $gridyy,
9191				     -column => 1,
9192				     -columnspan => 5,
9193				     -sticky => "nw");
9194    $am_frame->Radiobutton(-text => M"Automatisch",
9195			   -variable => \$average_v,
9196			   -value => 0,
9197			   -command => \&calc_ampel_optimierung,
9198			   )->pack(-side => 'left');
9199    $am_frame->Radiobutton(-text => M"Manuell �ber Strecke",
9200			   -variable => \$average_v,
9201			   -value => -1,
9202			   -command => \&calc_ampel_optimierung,
9203			   )->pack(-side => 'left');
9204
9205    $dgf->Label(-text => M("Beschleunigung (m/s^2)").":"
9206	       )->grid(-row => ++$gridyy, -column => 0,
9207		       -sticky => 'w');
9208    $gridxx = 1;
9209    my $found_beschleunigung;
9210    for (qw(0.5 1 1.5 2)) {
9211	$dgf->Radiobutton(-text => $_,
9212			  -variable => \$beschleunigung,
9213			  -value => $_,
9214			  -command => \&calc_ampel_optimierung,
9215			 )->grid(-row => $gridyy, -column => $gridxx++,
9216				 -sticky => 'w');
9217	if ($beschleunigung == $_) {
9218	    $found_beschleunigung++;
9219	}
9220    }
9221
9222    if (!$beschleunigung) { $beschleunigung = 1 }
9223    if (!$found_beschleunigung) {
9224	if ($beschleunigung > 2) { $beschleunigung = 2 }
9225	elsif ($beschleunigung < 0.5) { $beschleunigung = 0.5 }
9226	$beschleunigung = int($beschleunigung*2)/2;
9227    }
9228    $gridyy++;
9229
9230    $dgf->Label(-text => M("Verlorene Strecke (m)").":"
9231		)->grid(-row => $gridyy, -column => 0, -sticky => "w");
9232    $dgf->Entry(-textvariable => \$lost_strecke_per_ampel,
9233		-width => 5
9234		)->grid(-row => $gridyy, -column => 1,
9235			-columnspan => 5, -sticky => "w");
9236
9237    my $cb4;
9238    $cb4 = $act_page[3]->Checkbutton
9239      (-text => M"Verwenden",
9240       -variable => \$ampel_optimierung,
9241       -command => sub { change_state_all($act_page[3], $ampel_optimierung,
9242					  {$cb4=>4}); },
9243      )->grid(-row => $gridy++,
9244	      -column => 2,
9245	      -sticky => 'e');
9246    change_state_all($act_page[3], $ampel_optimierung, {$cb4=>4});
9247});
9248
9249    ####
9250    $act_page[4] = $nb->add("h", -label => M"Sonst. Beeintr�chtigungen");
9251    $gridy = 0;
9252    $act_page[4]->Label(-text => M"Sonst. Beeintr�chtigungen",
9253		     -font => $font{'bold'})->grid(-row => $gridy,
9254						   -column => 0);
9255    $act_page[4]->Label(-text => M"max. Geschwindigkeit",
9256		     -font => $font{'bold'})->grid(-row => $gridy,
9257						   -column => 1,
9258						   -columnspan => 2,
9259						  );
9260    $gridy++;
9261#XXX geht nicht...warum ???
9262#     $t->bind('<Return>' => sub {
9263# warn $t->focusCurrent;
9264# 		   if ($t->focusCurrent->isa('Tk::Entry')) {
9265# 		       $t->focusNext->tabFocus;
9266# 		   }
9267# 	       });
9268
9269    @e = ();
9270    for (0 .. 4) {
9271	my $i = $_;
9272	$act_page[4]->Label(-text => "q$i: " .
9273			          $category_attrib{"q$i"}->[ATTRIB_LONG],
9274			)->grid(-row => $gridy, -column => 0, -sticky => 'w');
9275	my $w;
9276	$w = $e[$i] = $act_page[4]->$Entry(-textvariable => \$var4{"q$i"},
9277					   -width => 3,
9278					   @EntryArgs,
9279					  );
9280	$w->grid(-row => $gridy, -column => 1, -sticky => 'e');
9281	$act_page[4]->Label(-text => 'km/h')->grid(-row => $gridy, -column => 2,
9282						-sticky => 'w');
9283	$gridy++;
9284    }
9285    $e[0]->tabFocus;
9286
9287    my $cb5;
9288    $cb5 = $act_page[4]->Checkbutton
9289      (-text => M"Verwenden",
9290       -variable => \$handicap_s_optimierung,
9291       -command => sub { change_state_all($act_page[4], $handicap_s_optimierung,
9292					  {$cb5=>5}); },
9293      )->grid(-row => $gridy++,
9294	      -column => 2,
9295	      -sticky => 'e');
9296    change_state_all($act_page[4], $handicap_s_optimierung, {$cb5=>5});
9297
9298    #######
9299    $gridy = 1;
9300#XXX    my $close_window = sub { $t->destroy; };
9301    my $close_window = $withdraw;
9302    my $apply_window = sub { eval {
9303				 while(my($k,$v) = each %var) {
9304				     if ($qualitaet_s_speed{$k} != $v) {
9305					 undef $qualitaet_s_net;
9306				     }
9307				     $qualitaet_s_speed{$k} = $v;
9308				 }
9309				 while(my($k,$v) = each %var2) {
9310				     if ($strcat_speed{$k} != $v) {
9311					 undef $strcat_net;
9312				     }
9313				     $strcat_speed{$k} = $v;
9314				 }
9315				 # special: B == HH
9316				 $strcat_speed{"B"} = $strcat_speed{"HH"};
9317				 while(my($k,$v) = each %var3) {
9318				     if ($radwege_speed{$k} != $v) {
9319					 undef $radwege_net;
9320				     }
9321				     $radwege_speed{$k} = $v;
9322				 }
9323				 while(my($k,$v) = each %var4) {
9324				     if ($handicap_s_speed{$k} != $v) {
9325					 undef $handicap_s_net;
9326				     }
9327				     $handicap_s_speed{$k} = $v;
9328				 }
9329			     };
9330			 };
9331    my $ok_window    = sub { &$close_window;
9332			     &$apply_window };
9333    my $bf = $t->Frame->grid(-row => $gridy++, -column => 0,
9334			     -columnspan => 3);
9335    my $okb = $bf->Button
9336      (Name => 'ok',
9337       -command => $ok_window)->grid(-row => 0, -column => 0,
9338				     -sticky => 'ew');
9339    $bf->Button(Name => 'apply',
9340		-command => $apply_window)->grid(-row => 0, -column => 1,
9341						 -sticky => 'ew');
9342    my $clb = $bf->Button
9343      (Name => 'close',
9344       -command => $close_window)->grid(-row => 0, -column => 2,
9345					-sticky => 'ew');
9346
9347    $t->bind('<Return>' => sub { $okb->invoke });
9348    $t->bind('<<CloseWin>>' => sub { $clb->invoke });
9349
9350    $t->Popup(@popup_style);
9351}
9352
9353# Macht aus den negativen Werten positive und aus den positiven reziproke
9354# Werte f�r die Penalty-Berechnung.
9355### AutoLoad Sub
9356sub optprefs2penalty {
9357    my $val = shift;
9358    if ($val < 0 ) {
9359	$val = -$val;
9360    } elsif ($val > 0) {
9361	$val = 1/$val;
9362    }
9363}
9364
9365# Alternativer Dialog zum Einstellen der Optimierung. Noch nicht
9366# fertig.
9367### AutoLoad Sub
9368sub enter_opt_preferences2 {
9369    my $t = redisplay_top($top, "optprefs", -title => M"Optimierungsvorlieben");
9370#XXX handicap XXX
9371    return if !defined $t;
9372    my @l = ([M"Ampeln", M"Ampeln vermeiden", M"Ampeln bevorzugen"],
9373	     [M"Abbiegen", M"Abbiegen vermeiden", M"Abbiegen bevorzugen"],
9374	     [M"Qualit�t", M"schlechte Qualit�t vermeiden", M"schlechte Qualit�t bevorzugen"],
9375	     [M"Kategorie", M"Hauptstra�en vermeiden", M"Nebenstra�en vermeiden"],
9376	     [M"Radwege", M"Radwege vermeiden", M"Radwege bevorzugen"],
9377	     [M"Steigung", M"Steigungen vermeiden", M"Steigungen bevorzugen"]);
9378
9379# Kategorie: B/HH: 3, H: 2, NH/N: 1, NN: 0
9380
9381# Kat     Scale	Res
9382
9383# 0	-5	-5
9384# 1	-5	-2
9385# 2	-5	+2
9386# 3	-5	+5
9387
9388# 0	-3	-3
9389# 1	-3	-1
9390# 2	-3	+1
9391# 3	-3	+3
9392
9393# 0	0	0
9394# 1	0	0
9395# 2	0	0
9396# 3	0	0
9397
9398# 0	+3	+3
9399# 1	+3	+1
9400# 2	+3	-1
9401# 3	+3	-3
9402
9403# 0	+5	+5
9404# 1	+5	+2
9405# 2	+5	-2
9406# 3	+5	-5
9407
9408    my @scale;
9409    my $y = 0;
9410    for my $l_def (@l) {
9411	my($l, $minus, $plus) = @$l_def;
9412	$optprefs{$l} = 0 unless defined $optprefs{$l};
9413	$t->Label(-text => $minus)->grid(-row => $y, -column => 0,
9414					 -sticky => 'e',
9415					);
9416	$scale[$y] = $t->Scale(-showvalue => 0,
9417			       -from => -5,
9418			       -to   => 5,
9419			       -variable => \$optprefs{$l},
9420			       -orient => 'h')->grid(-row => $y, -column => 1);
9421	$t->Label(-text => $plus)->grid(-row => $y, -column => 2,
9422					-sticky => 'w',
9423				       );
9424	$y++;
9425    }
9426
9427    my $close_window = sub { $t->destroy; };
9428    # XXX �berhaupt mit apply und so arbeiten? Wie war das gedacht gewesen?
9429    my $apply_window = sub {
9430	eval {
9431	    # Ampeloptimierung
9432	    #XXX $lost_time_per_ampel    = -$optprefs{"Ampeln"}*?;
9433	    # XXX what about F ...?
9434	    $lost_strecke_per_ampel = -$optprefs{"Ampeln"}*40;
9435	    $ampel_optimierung      = ($optprefs{Ampeln} != 0);
9436
9437	    # Abbiegeoptimierung
9438	    $abbiege_penalty     = -$optprefs{"Abbiegen"}*30;
9439	    $abbiege_optimierung = ($optprefs{Abbiegen} != 0);
9440
9441	    # Qualit�tsoptimierung
9442#  	    foreach (0 .. 3) {
9443#  	    $qualitaet_s_speed{"Q
9444#  	    $qualitaet_s_optimierung = ($optprefs{Qualit�t} != 0);
9445	};
9446    };
9447    my $ok_window    = sub { &$close_window;
9448			     &$apply_window };
9449    my $bf = $t->Frame->grid(-row => $y++, -column => 0,
9450			     -columnspan => 3,
9451			     -sticky => "ew");
9452    my $gridx = 0;
9453    my $okb = $bf->Button
9454      (Name => 'ok',
9455       -command => $ok_window)->grid(-row => 0, -column => $gridx++,
9456				     -sticky => 'ew');
9457    $bf->Button(-text => M"Zur�cksetzen",
9458		-command => sub {
9459		    for my $l_def (@l) {
9460			$optprefs{$l_def->[0]} = 0;
9461		    }
9462		})->grid(-row => 0,
9463			 -column => $gridx++,
9464			 -sticky => 'ew');
9465    $bf->Button(Name => 'apply',
9466		-command => $apply_window)->grid(-row => 0,
9467						 -column => $gridx++,
9468						 -sticky => 'ew');
9469    my $clb = $bf->Button
9470      (Name => 'close',
9471       -command => $close_window)->grid(-row => 0, -column => $gridx++,
9472					-sticky => 'ew');
9473
9474    $t->bind('<Return>' => sub { $okb->invoke });
9475    $t->bind('<<CloseWin>>' => sub { $clb->invoke });
9476
9477    $t->idletasks;
9478    my $bar = $t->Frame(-bg => 'red'
9479		       )->place('-y' => $scale[0]->y,
9480				'-x' => $scale[0]->x + $scale[0]->width/2-1,
9481				-width => 2,
9482				-height => ($scale[-1]->y-$scale[0]->y+
9483					    $scale[-1]->height),
9484			       );
9485
9486    # fast ein Hack: Events im senkrechten Strich werden auf die
9487    # daruterliegenden Scales weitergeleitet
9488    if ($bar->can('eventGenerate')) {
9489	foreach my $evt (qw(Motion
9490			    B1-Motion 1 ButtonRelease-1
9491			    B2-Motion 2 ButtonRelease-2
9492			   )) {
9493	    my $evt2 = $evt;
9494	    $bar->bind("<$evt2>" => sub {
9495			   my $e = shift->XEvent;
9496			   my($X,$Y) = ($e->X, $e->Y);
9497			   # feststellen, welches Scale-Widget sich
9498			   # darunter befindet
9499			   my $wid = $bar->containing($X+5,$Y);
9500			   if (defined $wid && $wid->isa('Tk::Scale')) {
9501			       $wid->eventGenerate("<$evt2>",
9502						   '-x' => $X-$wid->rootx,
9503						   '-y' => $Y-$wid->rooty,
9504						  );
9505			   }
9506		       });
9507	}
9508    }
9509
9510    my_popup($t);
9511
9512}
9513
9514# Berechnet f�r die Watt-Zahl die entsprechende Geschwindigkeit
9515### AutoLoad Sub
9516sub power2speed {
9517    my($power, %args) = @_;
9518    return if !$bp_obj;
9519    my $new_bp_obj = clone BikePower $bp_obj;
9520    $new_bp_obj->given('P');
9521    $new_bp_obj->headwind(0);
9522    my $grade = $args{-grade} || 0;
9523    $new_bp_obj->grade($grade);
9524    $new_bp_obj->power($power);
9525    $new_bp_obj->calc;
9526    $new_bp_obj->velocity*3.6;
9527}
9528
9529# Berechnet f�r die angegebene Geschwindigkeit die Watt-Zahl
9530### AutoLoad Sub
9531sub speed2power {
9532    my($speed, %args) = @_;
9533    return if !$bp_obj;
9534    my $new_bp_obj = clone BikePower $bp_obj;
9535    $new_bp_obj->given('v');
9536    $new_bp_obj->headwind(0);
9537    my $grade = $args{-grade} || 0;
9538    $new_bp_obj->grade($grade);
9539    $new_bp_obj->velocity($speed/3.6);
9540    $new_bp_obj->calc;
9541    $new_bp_obj->power;
9542}
9543
9544# Berechnet den Faktor f�r die max. Geschwindigkeit, die auf der
9545# jeweiligen Stra�e (wegen Belag, Kategorie ...) gefahren werden kann.
9546### AutoLoad Sub
9547sub max_speed {
9548    my($speed_belag) = @_;
9549    my $speed_radler = get_active_speed();
9550    if ($speed_belag <= 0) {
9551	require Carp;
9552	Carp::cluck("Division by zero protection");
9553	return $speed_radler;
9554    }
9555    ($speed_belag >= $speed_radler
9556     ? 1
9557     : $speed_radler/$speed_belag);
9558}
9559
9560# Return active speed in km/h.
9561### AutoLoad Sub
9562sub get_active_speed {
9563    my $speed;
9564    if ($active_speed_power{Type} eq 'power') {
9565	$speed = power2speed($power[$active_speed_power{Index}]);
9566    } else {
9567	$speed = $speed[$active_speed_power{Index}];
9568    }
9569    if (!$speed) {
9570	$speed = 20; # f�r alle F�lle
9571    }
9572    $speed;
9573}
9574
9575sub toggle_mouse_help {
9576    if (defined $toplevel{"help"} and
9577	Tk::Exists($toplevel{"help"})) {
9578	$toplevel{"help"}->destroy;
9579    } else {
9580	mouse_help();
9581    }
9582}
9583
9584# Gibt ein Hilfsfenster mit der derzeitigen Maustastenbelegung aus
9585### AutoLoad Sub
9586sub mouse_help {
9587    my $bgcolor = 'grey80';
9588    my $help_t = redisplay_top($top, 'help',
9589			       -title => M"Maushilfe",
9590			       @popup_style,
9591			       -bg => $bgcolor);
9592    return if !defined $help_t;
9593    $help_t->protocol('WM_DELETE_WINDOW' => sub {
9594			  $show_mouse_help = 0;
9595			  $help_t->destroy;
9596		      });
9597    my $row = 0;
9598    $help_t->gridColumnconfigure($_, -minsize => "1.6i") for (0..2);
9599    $help_t->gridRowconfigure($row, -minsize => "0.7i");
9600    $help_t->Message(-textvariable => \$mouse_text[1],
9601		     -width => "1.5i",
9602		     -bg => $bgcolor,
9603		    )->grid(-row => $row+1, -column => 0, -sticky => 'ne');
9604    $help_t->Message(-textvariable => \$mouse_text[2],
9605		     -width => "1.5i",
9606		     -bg => $bgcolor,
9607		    )->grid(-row => $row, -column => 1, -sticky => 's');
9608    $help_t->Message(-textvariable => \$mouse_text[3],
9609		     -width => "1.5i",
9610		     -bg => $bgcolor,
9611		    )->grid(-row => $row+1, -column => 2, -sticky => 'nw');
9612    $row++;
9613    # Maus zeichnen
9614    my $c = $help_t->Canvas(-width => "1.13i", -height => "1.38i",
9615			    -bg => $bgcolor,
9616			    -borderwidth => 0,
9617			    -highlightthickness => 0,
9618			    -takefocus => 0,
9619			   )->grid(-row => $row, -column => 1);
9620    $c->create('rectangle',"0.070866i","0.070866i","1.062992i","1.311024i",
9621	       -fill => 'white',
9622	       -outline => undef);
9623    $c->create('line',"1.062992i","1.311024i","1.062992i","0.070866i","0.070866i","0.070866i","0.070866i","1.311024i","1.062992i","1.311024i");
9624    $c->create('line',"0.744094i","0.122047i","1.027559i","0.122047i","1.027559i","0.531496i","0.744094i","0.531496i","0.744094i","0.122047i");
9625    $c->create('line',"0.425197i","0.122047i","0.708661i","0.122047i","0.708661i","0.531496i","0.425197i","0.531496i","0.425197i","0.122047i");
9626    $c->create('line',"0.106299i","0.122047i","0.389764i","0.122047i","0.389764i","0.531496i","0.106299i","0.531496i","0.106299i","0.122047i");
9627    $c->create('line', "0.106299i", "0.318898i", "0.000000i", "0.318898i");
9628    $c->create('line', "1.133858i", "0.318898i", "1.027559i", "0.318898i");
9629    $c->create('line', "0.562992i", "0.007874i", "0.562992i", "0.114173i");
9630}
9631
9632## DEBUG_BEGIN
9633#BEGIN{mymstat("50% BEGIN");}
9634## DEBUG_END
9635
9636# L�dt bzw. speichert eine Route
9637### AutoLoad Sub
9638sub load_save_route {
9639    my($save, $file, %args) = @_;
9640    status_message("");
9641    my $path;
9642    my $ext = $bbbike_route_ext;
9643    my $interactively_selected_filename = 0;
9644    if (!defined $file) {
9645	my $method = $save ? "getSaveFile" : "getOpenFile";
9646	$file = $top->$method
9647	    (-title => ($save ? M"Route speichern" : M"Route laden"),
9648	     -initialdir => $oldpath,
9649	     ($save ?
9650	      (-defaultextension => ".$ext") :
9651	      (-filetypes => [[M"Route-Dateien", '.' . $bbbike_route_ext],
9652			      [M"GPSMan-Tracks", ['.tracks','.trk']],
9653			      [M"GPSMan-Routen", ['.rte']],
9654			      [M"G7toWin", ['.g7t', '.G7T']],
9655			      [M"MPS-Tracks", ['.mps', '.MPS']],
9656			      [M"Alle Dateien",  '*']]),
9657	     ));
9658	return if !defined $file;
9659	$oldpath = dirname $file;
9660	$interactively_selected_filename = 1;
9661    }
9662    if (!-f $file && !file_name_is_absolute($file)) { # unvollst�ndiger Dateiname
9663        $file = catfile($bbbike_routedir, "$file.$ext");
9664    }
9665    if (!$save) { # load
9666        IncBusy($top) if $top;
9667	eval {
9668
9669	    my $res = Route::load($file,
9670				  { ResetRoute => \&reset_undo_route },
9671				  -fuzzy => 0);
9672
9673	    if ($res->{IsStrFile}) {
9674		# eine Strassen-Datei
9675		plot_layer('str', $file);
9676		return;
9677	    }
9678
9679	    @realcoords          = @{ $res->{RealCoords} };
9680	    @search_route_points = @{ $res->{SearchRoutePoints} };
9681
9682	    if (!@realcoords) {
9683		die M"Leere Routendatei";
9684	    }
9685
9686	    add_last_loaded($file, $last_loaded_obj) if $interactively_selected_filename;
9687	    @coords = ();
9688	    my $i;
9689	    my($minx, $miny, $maxx, $maxy);
9690	    my $std = ($coord_system eq 'standard');
9691	    foreach (@realcoords) {
9692		my($x, $y);
9693		if ($std) {
9694		    ($x, $y) = transpose($_->[0], $_->[1]);
9695		} else {
9696		    ($x, $y) = transpose
9697		      ($coord_system_obj->standard2map($_->[0], $_->[1]));
9698		    require BBBikeAdvanced;
9699		    buttonpoint($x, $y);
9700		};
9701		push(@coords, [$x, $y]);
9702		if (!defined $minx || $x < $minx) { $minx = $x }
9703		if (!defined $maxx || $x > $maxx) { $maxx = $x }
9704		if (!defined $miny || $y < $miny) { $miny = $y }
9705		if (!defined $maxy || $y > $maxy) { $maxy = $y }
9706	    }
9707
9708	    if ($zoom_loaded_route) {
9709		zoom_view($minx, $miny, $maxx, $maxy);
9710	    } elsif ($center_loaded_route) {
9711		my $x2 =
9712		  (abs($coords[0]->[0]-$minx) > abs($coords[0]->[0]-$maxx)
9713		   ? $minx : $maxx);
9714		my $y2 =
9715		  (abs($coords[0]->[1]-$miny) > abs($coords[0]->[1]-$maxy)
9716		   ? $miny : $maxy);
9717		$c->center_view2($coords[0]->[0], $coords[0]->[1], $x2, $y2);
9718	    }
9719
9720	    restore_search_route_points();
9721
9722	    redraw_path();
9723	    updatekm();
9724	    update_route_strname();
9725
9726	    undef $search_route_flag;
9727	    search_route_mouse_cont();
9728
9729	    status_message(Mfmt("Typ der Routendatei: %s, Punkte: %s", $res->{Type}, scalar(@realcoords)), "info");
9730	};
9731
9732	if ($@) {
9733	    status_message($@, 'err');
9734	}
9735	DecBusy($top) if $top;
9736    } else { # Save
9737	my $case = ($os eq 'win' ? '(?i)' : '');
9738	if ($file !~ /$case\.$ext$/i) {
9739	    $file .= ".$ext";
9740	}
9741	make_backup($file);
9742	eval {
9743	    Route::save(-file => $file,
9744			-realcoords => \@realcoords,
9745			-searchroutepoints => \@search_route_points);
9746	};
9747	if ($@) {
9748	    status_message($@, 'err');
9749	} else {
9750	    add_last_loaded($file, $last_loaded_obj) if $interactively_selected_filename;
9751	}
9752    }
9753}
9754
9755### AutoLoad Sub
9756sub save_route_as_bbd {
9757    require Route;
9758    require Route::Heavy;
9759    my $file = $top->getSaveFile(-defaultextension => '.bbd');
9760    return unless defined $file;
9761    my $tmpfile = "$tmpdir/bbbike-$<-$$.bbr";
9762    load_save_route(1, $tmpfile);
9763    my $s = Route::as_strassen($tmpfile,
9764			       name => "Route",
9765			       cat => "X",
9766			       fuzzy => 0,
9767			      );
9768    if (!$s) {
9769	status_message("Fataler Fehler: $tmpfile l�sst sich nicht konvertieren", "die");
9770    }
9771
9772    $s->write($file);
9773
9774    unlink $tmpfile;
9775}
9776
9777### AutoLoad Sub
9778sub save_route_as_esri {
9779    my $file = $top->getSaveFile(-defaultextension => '.shp');
9780    return unless defined $file;
9781    $file =~ s/\.shp$//;
9782    my $tmpfile1 = "$tmpdir/bbbike-$<-$$.bbr";
9783    my $tmpfile2 = "$tmpdir/bbbike-$<-$$.bbd";
9784    load_save_route(1, $tmpfile1);
9785    eval {
9786	# XXX Better diagnostics. bbr2bbd and bbd2esri should be
9787	# callable as modules.
9788	system("$FindBin::RealBin/miscsrc/bbr2bbd", $tmpfile1, $tmpfile2);
9789	status_message(Mfmt("Das Ausf�hren von %s ist mit dem Code %s fehlgeschlagen", "bbr2bbd", $?), "die") if $? != 0;
9790	system("$FindBin::RealBin/miscsrc/bbd2esri", $tmpfile2, "-o", $file);
9791	status_message(Mfmt("Das Ausf�hren von %s ist mit dem Code %s fehlgeschlagen", "bbd2esri", $?), "die") if $? != 0;
9792    }; warn $@ if $@;
9793    unlink $tmpfile2;
9794    unlink $tmpfile1;
9795}
9796
9797### AutoLoad Sub
9798sub save_route_as_optimized_gpx {
9799    gps_interface('BBBikeGPS::GPXRoute', -noloading => 1);
9800}
9801
9802### AutoLoad Sub
9803sub send_route_to_gps {
9804    if (!@{ get_act_search_route() }) {
9805	status_message(M"Keine Route", "infodlg");
9806	return;
9807    }
9808    if ($os eq 'win') {
9809	# Assume that the windows distribution has gpsbabel bundled,
9810	# so always prefer this one
9811	require GPS::Gpsbabel;
9812	if (GPS::Gpsbabel->gpsbabel_available) {
9813	    gps_interface('BBBikeGPS::GpsbabelSend', -noloading => 1);
9814	} else {
9815	    require BBBikeGPS;
9816	    if (GPS::BBBikeGPS::MapSourceSend->has_mapsource) {
9817		gps_interface('BBBikeGPS::MapSourceSend', -noloading => 1);
9818	    } else {
9819		if ($gps_device ne 'USB' && eval { require GPS::DirectGarmin; 1 }) {
9820		    gps_interface('DirectGarmin');
9821		} else {
9822		    my $recommended_path = GPS::Gpsbabel->gpsbabel_recommended_path;
9823		    my $download_location = GPS::Gpsbabel->gpsbabel_download_location;
9824		    # XXX use hypertext_widget
9825		    status_message(<<EOF, "die");
9826Das Programm gpsbabel wird zur �bertragung zum GPS-Ger�t ben�tigt und muss noch installiert werden. gpsbabel gibt es hier zum Download:
9827$download_location
9828
9829Die heruntergeladene .zip-Datei sollte im Verzeichnis
9830$recommended_path
9831ausgepackt werden.
9832EOF
9833		}
9834	    }
9835	}
9836    } else {
9837	gps_interface('DirectGarmin');
9838    }
9839}
9840
9841# weiter zur Druckfunktion...
9842### AutoLoad Sub
9843sub print_function {
9844    my $print_backend = $print_backend;
9845    if (!defined $print_backend || $print_backend eq "") {
9846	if ($os eq 'win') {
9847	    my $available = print_postscript(undef, -checkavailability => 1);
9848	    if (!$available) {
9849		# a PDF viewer should be available everywhere nowadays on Win32
9850		$print_backend = "pdf";
9851	    } else {
9852		$print_backend = "ps";
9853	    }
9854	} else {
9855	    $print_backend = "ps";
9856	}
9857    }
9858
9859    if ($print_backend eq 'pdf') {
9860	require File::Temp;
9861	my($fh, $tmpfile) = File::Temp::tempfile(UNLINK => 1,
9862						 SUFFIX => ".pdf");
9863	$tmpfiles{$tmpfile}++;
9864	pdf_export(-visiblemap => 1, -file => $tmpfile);
9865	close($fh);
9866	if (-e $tmpfile && -s $tmpfile) {
9867	    view_pdf($tmpfile);
9868	}
9869	return;
9870    }
9871
9872    return if slow_postscript_generation();
9873
9874    my $tmpfile = create_postscript
9875	($c,
9876	 -legend => ($use_legend ?
9877		     ($use_legend_right ? 'right' : 'left') : 0),
9878	 -colormode => $ps_color,
9879	 -rotate    => $ps_rotate,
9880	 -scale_a4  => $ps_scale_a4,
9881	);
9882    my @print_args;
9883    if ($ps_scale_a4) {
9884	push @print_args, -media => 'A4';
9885    }
9886    print_postscript($tmpfile, @print_args);
9887}
9888
9889# Berechnet die Canvas-Koordinaten der Route aus den Standard-Koordinaten
9890### AutoLoad Sub
9891sub realcoords2coords {
9892    @coords = ();
9893    my $i;
9894    my $std = ($coord_system eq 'standard');
9895    foreach (@realcoords) {
9896	my($x, $y);
9897	if ($std) {
9898	    ($x, $y) = transpose($_->[0], $_->[1]);
9899	} else {
9900	    ($x, $y) = transpose
9901	      ($coord_system_obj->standard2map($_->[0], $_->[1]));
9902	}
9903	push @coords, [$x, $y];
9904    }
9905}
9906
9907######################################################################
9908#
9909# Funktionen zum Zeichnen der Kartenelemente (Strecken und Punkte)
9910#
9911# Allegemeine Plot-Funktion
9912sub plot {
9913    my($type, $abk, %args) = @_;
9914    Hooks::get_hooks("before_plot")->execute;
9915    if (exists $args{'-draw'}) {
9916	if ($type eq 'str') {
9917	    $str_draw{$abk} = $args{'-draw'};
9918	} else {
9919	    $p_draw{$abk} = $args{'-draw'};
9920	}
9921    }
9922    if ($type eq 'str') {
9923	plotstr($abk, %args);
9924    } elsif ($type eq 'p') {
9925	if ($abk =~ /sperre/) {
9926	    my $object_or_file = $args{-object} || $args{-filename} || $p_obj{$abk};
9927	    $args{-abk} = $abk;
9928	    plot_sperre($object_or_file, %args);
9929	} else {
9930	    plotp($abk, %args);
9931	}
9932    } else {
9933	die "Unknown type $type";
9934    }
9935###XXX H�h?
9936#    if ($BBBikeLazy::mode && defined &bbbikelazy_remove_data) {
9937#	bbbikelazy_remove_data($type, $abk);
9938#    }
9939    Hooks::get_hooks("after_plot")->execute;
9940}
9941
9942sub plot_layer {
9943    my($type, $file, %args) = @_;
9944    my $abk = next_free_layer();
9945    if (!defined $abk) {
9946	status_message("Kein freier Layer mehr vorhanden", "err");
9947	return;
9948    }
9949    fix_stack_order($abk);
9950    if ($type eq 'p') {
9951	$p_draw{$abk} = 1;
9952	if (defined $file) {
9953	    $p_file{$abk} = $file;
9954	    delete $p_obj{$abk};
9955	}
9956    } else {
9957	$str_draw{$abk} = 1;
9958	if (defined $file) {
9959	    $str_file{$abk} = $file;
9960	    delete $str_obj{$abk};
9961	}
9962    }
9963    plot($type, $abk, %args);
9964    if ($type eq 'p' && $p_draw{$abk}) {
9965	$most_recent_p_layer = $abk;
9966    } elsif ($type eq 'str' && $str_draw{$abk}) {
9967	$most_recent_str_layer = $abk;
9968    }
9969    $abk;
9970}
9971
9972# XXX
9973# h�heres Canvas-Objekt
9974# - derzeitige Transpose-Funktion
9975# - Scale
9976# - Koordinatensystem
9977#
9978# Zeichnet Strecken auf dem Canvas
9979sub plotstr {
9980    my($abk, %args) = @_;
9981    my $c = $c;
9982    return if !$c;
9983    my $std = 1;
9984    my $transpose = \&transpose;
9985    if (exists $args{Canvas}) {
9986	$c = $args{Canvas};
9987	$std = 0;
9988	$transpose = ($show_overview_mode eq 'region'
9989		      ? \&transpose_small
9990		      : \&transpose_medium);
9991    }
9992
9993    status_message("");
9994    $abk   = 's'      if !defined $abk;
9995
9996    # alte Tags l�schen
9997    if (!$std || !$args{FastUpdate} || !$str_draw{$abk}) {
9998	$c->delete($abk);		# evtl. alte Koordinaten l�schen
9999	$c->delete("pp-$abk");
10000    }
10001    $c->delete("$abk-out");
10002    $c->delete("$abk-label");
10003    $c->delete("$abk-label-bg");
10004    $c->delete("$abk-fg") if $abk eq 'v'; # XXX do not use for "b", "r" or "u"!
10005    if ($abk eq 'w') { # Wasser *und* Inseln l�schen
10006	$c->delete("i");
10007	$c->delete("i-out");
10008	$c->delete("i-label");
10009	$c->delete("i-label-bg");
10010    }
10011
10012    if ($std && !$str_draw{$abk}) {
10013	if ($lazy_str{$abk}) {
10014	    bbbikelazy_remove_data("str", $abk);
10015	}
10016	status_message(Mfmt("Layer <%s> entfernt", exists $str_attrib{$abk} ? $str_attrib{$abk}->[ATTRIB_PLURAL] : $abk), "info");
10017	return;
10018    }
10019    # A hack to get overview canvas plots deleted
10020    if (exists $args{Canvas} && exists $args{-draw} && !$args{-draw}) {
10021	return;
10022    }
10023
10024    # Get source from filename or street object
10025    my($filename, $filename_maybe, $str, $has_filename);
10026    if (!defined $args{-object}) {
10027	$filename = $args{-filename} || $args{Filename};
10028	if (defined $filename) {
10029	    $str_file{$abk} = $filename;
10030	} else {
10031	    $filename = get_strassen_file($str_file{$abk});
10032	    $filename_maybe = $str_file{$abk} if $edit_mode_flag; # as fallback if no -orig version available
10033	}
10034	$has_filename = 1;
10035	delete $pending{"replot-str-$abk"};
10036	if (!defined $filename) {
10037	    status_message(Mfmt("Dateiname f�r <%s> ist nicht definiert.", $abk),
10038			   'err');
10039	    return;
10040	}
10041    } else {
10042	$str = delete $args{-object};
10043    }
10044
10045#     # Radwege werden im Edit-Modus besser mit radweg_draw_canvas() gezeichnet
10046#     # XXX ups? stimmt das noch immer??? -> wahrscheinlich nicht! XXX
10047#     if ($abk eq 'rw' and $coord_system ne 'standard') {
10048# 	radweg_open();
10049# 	radweg_draw_canvas();
10050# 	return;
10051#     }
10052
10053    my $dont_use_cache;
10054    my $dont_set_cache = 1;
10055
10056    if (!$str) {
10057	$dont_use_cache = ($coord_system ne 'standard' ||
10058			   $args{FastUpdate});
10059	$dont_set_cache = ($coord_system ne 'standard');
10060    TRYCACHE: {
10061	    if (defined $str_obj{$abk} && !$dont_use_cache) {
10062		last TRYCACHE if ($abk eq 'l' and
10063				  (defined $str_cache_attr{'l'} and
10064				   $str_cache_attr{'l'} ne "$str_far_away{'l'}"));
10065		last TRYCACHE if ($str_regions{'l'} && @{$str_regions{'l'}});
10066		last TRYCACHE if !$str_obj{$abk}->is_current;
10067		$str = $str_obj{$abk};
10068	    }
10069	}
10070    }
10071
10072    if (!defined $str) {
10073	cache_decider_init();
10074	# XXX use get_any_strassen_obj?
10075	if ($abk eq 'w') {
10076	    $str = _get_wasser_obj($filename);
10077	} elsif ($abk eq 'l') {
10078	    $str = _get_landstr_obj();
10079	} elsif ($abk eq 'e') {
10080	    $str = _get_ferry_obj();
10081	} elsif ($abk eq 'comm') {
10082	    $str = _get_comments_obj();
10083	} elsif ($abk eq 'fz') {
10084	    $str = _get_fragezeichen_obj();
10085	} else {
10086	    eval { $str = Strassen->new($filename); };
10087	    if ($@ && $filename_maybe) {
10088		eval { $str = Strassen->new($filename_maybe); };
10089	    }
10090	    if ($@) {
10091		if ($edit_mode || $edit_normal_mode) {
10092		    status_message(Mfmt("Beim Laden der Datei %s: %s", $filename, $@), "info");
10093		    return;
10094		}
10095		# Do not "die", may be in Progress mode
10096		if (!$no_original_datadir) {
10097		    $str_draw{$abk} = 0;
10098		    status_message($@, "err");
10099		}
10100		return;
10101	    }
10102	}
10103	if ($abk ne 'w') { # XXX get_cache_identifier benutzen
10104	    if ((!$dont_set_cache && cache_decider()) ||
10105		$abk =~ /^[sl]$/ ||
10106		$edit_normal_mode # Always cache in edit mode to make "reload all" work
10107	       ) {
10108		# f�r nearest_line_points Caching erzwingen
10109		$str_obj{$abk} = $str;
10110		if ($abk eq 'l') {
10111		    $str_cache_attr{'l'} = "$str_far_away{'l'}";
10112		    # XXX str_regions?
10113		}
10114	    }
10115	}
10116    }
10117
10118    if (!defined $str) {
10119	status_message(M"Kein Objekt definiert!", "err");
10120	return;
10121    }
10122
10123    handle_global_directives($str, $abk);
10124    # XXX obsolete:
10125    if (defined $filename && -e "$filename.desc") {
10126	require BBBikeAdvanced;
10127	read_desc_file("$filename.desc", $abk);
10128    }
10129
10130    if ($str_name_draw{$abk}) {
10131	require Tk::RotFont;
10132    }
10133
10134    my $lazy = defined $args{-lazy} ? $args{-lazy} : $lazy_plot;
10135    if ($std && $lazy && $has_filename) {
10136	status_message(Mfmt("Layer <%s> gezeichnet", exists $str_attrib{$abk} ? $str_attrib{$abk}->[ATTRIB_PLURAL] : $abk), "info");
10137	return bbbikelazy_add_data("str", $abk, $str, \%args);
10138    }
10139
10140    my $complete_str = $str;
10141    my $diffed_str = 0;
10142    my $indexmap;
10143    if ($args{FastUpdate}) {
10144	my($new_str, $todelref);
10145	($new_str, $todelref, $indexmap) = $str->diff_orig(-clonefile => 1);
10146	if (!defined $new_str) {
10147	    print STDERR M("Diff-Ausgabe wird nicht verwendet"), "\n" if $verbose;
10148	    $c->delete($abk);		# evtl. alte Koordinaten l�schen
10149	    $c->delete("pp-$abk");
10150	} else {
10151	    if ($verbose) {
10152		print STDERR M("Diff-Ausgabe wird verwendet"), "\n";
10153		print STDERR Mfmt("Anzahl der neu zu zeichnenden Stra�en: %d", scalar @{$new_str->data}), "\n";
10154		print STDERR Mfmt("Anzahl der zu l�schenden Stra�en: %d", scalar @$todelref), "\n";
10155	    }
10156	    for my $id (@$todelref) {
10157		for my $strdeladd ("", "-label") {
10158		    $c->delete("$abk$strdeladd-$id");
10159		}
10160	    }
10161	    $str = $new_str;
10162	    $diffed_str = 1;
10163	}
10164    }
10165
10166    my($restrict, $restrict_list, $ignore, $ignore_list) = _set_restrict($abk);
10167
10168    my %category_color = %category_color;
10169    if ($abk =~ /^g(|[PD])$/ && !$std) {
10170	$category_color{Z} = '#9e9e9e';
10171    }
10172
10173    my $default_width = get_line_width($abk) || 4;
10174    if (defined $args{Width}) { $default_width = $args{Width} }
10175    my %category_width; # XXX the global category_width is ignored!!! should be changed
10176    {
10177	my $scale = (exists $args{Canvas}
10178		     ? ($show_overview_mode eq 'region'
10179			? $small_scale
10180			: $medium_scale)
10181		     : $scale);
10182	%category_width = _set_category_width($abk, $scale);
10183    }
10184
10185    # current category size
10186    my %category_size = map {
10187	($_, $category_size{$_}* $label_font_size/10)
10188    } keys %category_size;
10189
10190    my $no_overlap_label = (exists $args{NoOverlapLabel}
10191			    ? $args{NoOverlapLabel} : $no_overlap_label{$abk});
10192
10193    my $coordsys = $coord_system_obj->coordsys;
10194
10195    my $use_stippleline = decide_stippleline($abk);
10196
10197    destroy_delayed_restack();
10198
10199    IncBusy($top);
10200    $progress->Init(-dependents => $c,
10201		    (defined $filename ? (-label => $filename) : ()),
10202		   );
10203
10204    my %conv_args;
10205    if ($args{-map}) {
10206	$conv_args{Map} = $args{-map};
10207    }
10208    my $conv = $str->get_conversion(%conv_args);
10209
10210    eval {
10211	# XXX Experiment
10212	if ($orientation eq 'landscape' &&
10213	    !$edit_mode &&
10214#XXX?	    !$edit_normal_mode &&
10215	    !$str_name_draw{$abk} &&
10216	    !$str_nr_draw{$abk} &&
10217	    !exists $args{Canvas} &&
10218	    !$p_draw{'pp'} &&
10219	    ($abk eq 'l' || $abk eq 's') &&
10220	    !$conv &&
10221	    defined &BBBike::fast_plot_str) {
10222	    eval {
10223		die if $str->isa("Strassen::Storable");
10224		# Wenn outline nicht definiert ist, dann wird es
10225		# eigenm�chtig gesetzt. Die XS-Routine ist daf�r schnell
10226		# genug.
10227		if (!defined $str_outline{$abk}) {
10228		    $str_outline{$abk} = 1;
10229		}
10230		my(@files) = $str->file;
10231		if (grep { /\.gz$/ } @files) {
10232		    die "fast_plot_str can't handle gzipped files yet";
10233		}
10234		my(@args) = ($c, $abk,
10235			     (@files > 1 ? \@files : @files),
10236			     $progress);
10237		if (@$restrict_list) {
10238		    push @args, $restrict_list;
10239		} else {
10240		    push @args, undef;
10241		}
10242		push @args, \%category_width;
10243		if (@$ignore_list) {
10244		    push @args, $ignore_list;
10245		} else {
10246		    push @args, undef;
10247		}
10248		BBBike::fast_plot_str(@args);
10249	    };
10250	    my $err = $@;
10251	    if (!$err) {
10252		goto PLOTSTR_CONT;
10253	    } else {
10254		warn $err if $^W;
10255	    }
10256	}
10257
10258	my $xadd_anchor = $xadd_anchor_type->{$abk};
10259	my $yadd_anchor = $yadd_anchor_type->{$abk};
10260	my $label_spaceadd = $label_spaceadd{$abk};
10261
10262	my $real_i = 0;
10263	my $i;
10264	my $anzahl_eindeutig = $str->count;
10265	$str->init;
10266	$escape = 0;
10267	my @extra_tags = ($abk =~ /^L\d+/ ? ("$abk-s") : ());
10268
10269	my $draw_sub = eval $plotstr_draw_sub;
10270	string_eval_die($@, $plotstr_draw_sub) if $@;
10271
10272	my $bench = Tk::Time_So_Far();
10273	while (1) {
10274	    my $ret = $str->next;
10275	    last if !@{$ret->[Strassen::COORDS]};
10276	    if (!$diffed_str) {
10277		if ($real_i % 80 == 0) {
10278		    $progress->Update($real_i/$anzahl_eindeutig);
10279		    # XXX Probleme mit diesem $top->update, falls
10280		    # ein anderer plot-Vorgang damit gestartet wird
10281		    #if ($progress) {
10282		    #$top->update; # f�r Escape
10283		    #if ($escape) {
10284		    #	status_message("Zeichnen von <$filename> abgebrochen",
10285		    #		       "warn");
10286		    #	last;
10287		    #    }
10288		    #}
10289		}
10290	    }
10291#last if $i > 100; # for Debugging XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
10292
10293	    $i = $indexmap && exists $indexmap->{$real_i} ? $indexmap->{$real_i} : $real_i;
10294	    $draw_sub->($ret); # XXX evtl. den Code mit eval erzeugen
10295	    $real_i++;
10296	}
10297# XXXXXX can this ever happen? XXXXXXXXXXXXXXXXXXXXXXXXXXX
10298# XXX Yes: If a bbd file contains a half-valid line (with name and cat, but without coords)
10299if ($str->pos != scalar @{$str->{Data}}) { status_message("warning: " . $str->pos . " != " . scalar(@{$str->{Data}}) . "!", "dialog", "err") }
10300#XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
10301	warn sprintf "Plotting streets '$abk' took %.3fs\n", Tk::Time_So_Far()-$bench
10302	    if $verbose;
10303
10304      PLOTSTR_CONT:
10305	$c->itemconfigure('pp',
10306			  -capstyle => $capstyle_round,
10307			  -width => 5,
10308			 );
10309	pp_color();
10310	if ($layer_active_color{$abk}) {
10311	    $c->itemconfigure($abk, -activefill => $layer_active_color{$abk});
10312	}
10313	if ($abk eq 'e' && defined $linestip) {
10314	    # XXX hacky: make sure that e-img do not get configured,
10315	    # so use 'e-Q' instead of just 'e'
10316	    $c->itemconfigure('e-Q', -stipple => '@' . $linestip);
10317	}
10318
10319	if (!exists $args{Canvas} && !$no_make_net && !$edit_mode && !$edit_normal_mode) {
10320	    if (defined $net && !$net->is_source($str) && $abk =~ /^[sl]$/) {
10321		make_net();
10322	    } elsif (!defined $net && $abk =~ /^[sl]$/) {
10323		make_net();
10324	    }
10325	}
10326
10327	if (($edit_mode || $edit_normal_mode || $abk eq 'relgps' || $args{FastUpdate})) { # XXX? and !$diffed_str) {
10328	    warn "Try to copy original data" if $verbose;
10329	    my $r = $complete_str->copy_orig;
10330	    warn "Returned $r" if $verbose;
10331	}
10332
10333	if ($std) {
10334	    restack_delayed(); # XXX check!
10335	}
10336
10337	if ($abk =~ /^L\d+/) {
10338	    std_str_binding($abk);
10339	}
10340
10341    };
10342    warn "eval called before line " . __LINE__ . ": $@" if ($@);
10343    $progress->Finish;
10344    DecBusy($top);
10345}
10346
10347sub _set_restrict {
10348    my($abk) = @_;
10349    my($restrict, @restrict, $ignore, @ignore);
10350    if (exists $str_restrict{$abk} ||
10351	exists $str_ignore{$abk}) {
10352	my $all_set = 1;
10353	my($k,$v);
10354	if (exists $str_restrict{$abk}) {
10355	    while(($k,$v) = each %{$str_restrict{$abk}}) {
10356		if (!$v) {
10357		    $all_set = 0;
10358		} else {
10359		    push @restrict, $k;
10360		}
10361	    }
10362	}
10363	if (exists $str_ignore{$abk}) {
10364	    while(($k,$v) = each %{$str_ignore{$abk}}) {
10365		if ($v) {
10366		    $all_set = 0;
10367		    push @ignore, $k;
10368		}
10369	    }
10370	}
10371	if (exists $str_restrict{$abk}) {
10372	    if ($all_set || !@restrict) {
10373		undef $restrict;
10374	    } else {
10375		$restrict = '^(' . join('|', map { quotemeta $_ } @restrict) . ")\$";
10376	    }
10377	}
10378	if (exists $str_ignore{$abk}) {
10379	    $ignore = '^(' . join('|', map { quotemeta $_ } @ignore) . ")\$";
10380	}
10381	if ($] >= 5.005) {
10382	    eval q{
10383	    $restrict = qr/$restrict/
10384		if defined $restrict;
10385	    $ignore = qr/$ignore/
10386		if defined $ignore;
10387            }; die $@ if $@;
10388	}
10389    }
10390    ($restrict, \@restrict, $ignore, \@ignore);
10391}
10392
10393#XXX %category_width wird nicht skaliert...
10394sub _set_category_width {
10395    my($abk, $this_scale) = @_;
10396    $this_scale = $scale if !defined $this_scale;
10397    my %category_width;
10398    foreach (keys %line_width) {
10399	if (/^$abk-(.*)/) {
10400	    my $cat = $1;
10401	    $category_width{$cat} = get_line_width($_, $this_scale);
10402	}
10403    }
10404    %category_width;
10405}
10406
10407sub decide_stippleline {
10408    my($abk) = @_;
10409    if ($Tk::VERSION < 800.016) {
10410	if (exists $line_dash{$abk} || exists $layer_line_dash{$abk} || exists $layer_category_line_dash{$abk}) {
10411	    require Tk::StippleLine;
10412	    return 1;
10413	} else {
10414	    return 0;
10415	}
10416    }
10417    return 3; # signal that -dash exists or is needed
10418}
10419
10420# Arguments:
10421#   $c: canvas to draw onto
10422#   $x, $y: canvas coordinates
10423#   %args: options for createText, special options are:
10424#      -outlinecolor: color of the outline, by default canvas background
10425#      -outlinewidth: width of the outline, by default 1
10426### AutoLoad Sub
10427sub outline_text {
10428    my($c, $x, $y, %args) = @_;
10429    my $outline_color = delete $args{'-outlinecolor'} || $c->cget(-background);
10430    my $fg            = delete $args{'-fill'}         || "black";
10431    my $outline_width = delete $args{'-outlinewidth'} || 1;
10432    my $tags          = delete $args{'-tags'};
10433    $tags = [$tags] if ref $tags ne 'ARRAY';
10434    $outline_i++;
10435    if (defined $outline_color && defined $outline_width) {
10436        my @outlines;
10437        foreach (1 .. $outline_width) {
10438            push(@outlines, [-$_, 0], [$_, 0], [0, $_], [0, -$_]);
10439        }
10440        foreach (@outlines) {
10441            $c->createText($x + $_->[0], $y + $_->[1],
10442			   -fill => $outline_color,
10443			   -tags => [@$tags, 'outlslave-'.$outline_i,
10444				     'outldata_'.join("_",@$_)],
10445			   %args);
10446        }
10447    }
10448    $c->createText($x, $y,
10449		   -fill => $fg,
10450		   -tags => [@$tags, 'outlmaster', 'outlmaster-'.$outline_i,
10451			     "outlmaster-width-$outline_width"],
10452		   %args);
10453}
10454
10455### AutoLoad Sub
10456sub plot_mount {
10457    my $mount;
10458    if ($str_draw{'mount'}) {
10459	my $comm = Strassen->new(get_strassen_file("comments_mount"));
10460	my $comm_mount = Strassen->new_copy_restricted($comm, -grep => ["St;"]);
10461	$mount = MultiStrassen->new($str_file{"mount"},
10462				    $comm_mount);
10463    }
10464    plot('str','mount', -object => $mount);
10465}
10466
10467# Zeichnet gesperrte Stra�en und Einbahnstra�en.
10468# XXX gesperrte Wegf�hrungen werden noch nicht gezeichnet
10469### AutoLoad Sub
10470sub plot_sperre {
10471    my $file_or_object = shift;
10472    my %args = @_;
10473    my $abk = $args{-abk} || 'sperre';
10474    Hooks::get_hooks("before_plot")->execute;
10475    if (!$args{FastUpdate}) {
10476	$c->delete($abk);
10477    }
10478    if (!$p_draw{$abk}) {
10479	Hooks::get_hooks("after_plot")->execute; # XXX should not be here
10480	status_message(Mfmt("Layer <Sperrungen> entfernt"), "info");
10481	return;
10482    }
10483    IncBusy($top);
10484    eval {
10485	my $gesperrt;
10486	if (UNIVERSAL::isa($file_or_object, "Strassen")) {
10487	    $gesperrt = $file_or_object;
10488	} else {
10489	    $gesperrt = new Strassen (defined $file_or_object
10490				      ? $file_or_object
10491				      : get_strassen_file($sperre_file)
10492				     );
10493	}
10494	$p_obj{$abk} = $gesperrt;
10495	my $is_car = $gesperrt->file =~ /gesperrt_car/;
10496	my $car_photo;
10497	if ($is_car) {
10498	    $car_photo = load_photo($top, 'car');
10499	}
10500
10501	my $width0  = get_line_width('sperre0');
10502	my $width1  = get_line_width('sperre1');
10503	my $width2  = get_line_width('sperre2');
10504	my $width3  = get_line_width('sperre3');
10505	my $width3_nocross = get_line_width('sperre3nocross');
10506	my $length1 = get_line_length('sperre1');
10507	my $length2 = get_line_length('sperre2');
10508
10509	my %type2cat =
10510	    (StrassenNetz::BLOCKED_ONEWAY()        => "sperre1",
10511	     StrassenNetz::BLOCKED_ONEWAY_STRICT() => "sperre1s",
10512	     StrassenNetz::BLOCKED_COMPLETE()      => "sperre2",
10513	     StrassenNetz::BLOCKED_CARRY()         => "sperre0",
10514	    );
10515
10516	my %type2fill =
10517	    (StrassenNetz::BLOCKED_ONEWAY()        =>
10518	     ($width1 && $length1 ? $category_color{'sperre1'} : undef),
10519	     StrassenNetz::BLOCKED_ONEWAY_STRICT() =>
10520	     ($width1 && $length1 ? $category_color{'sperre1s'} : undef),
10521	    );
10522	my $fill2 = ($width2 && $length2 ? $category_color{'sperre2'} : undef);
10523
10524	# korrigieren, damit beim Vergr��ern etwas erscheint
10525	$length1 = ($length1 ? $length1 : 1);
10526	$length2 = ($length2 ? $length2 : 1);
10527
10528	# XXX don't duplicate code from plotstr!
10529	my $diffed_str = 0;
10530	my $str = $gesperrt;
10531	my $complete_str = $str;
10532	my $indexmap;
10533	if ($args{FastUpdate}) {
10534	    my($new_str, $todelref);
10535	    ($new_str, $todelref, $indexmap) = $str->diff_orig(-clonefile => 1);
10536	    if (!defined $new_str) {
10537		print STDERR M("Diff-Ausgabe wird nicht verwendet") if $verbose;
10538		$c->delete($abk);		# evtl. alte Koordinaten l�schen
10539		$c->delete("pp-$abk");
10540	    } else {
10541		if ($verbose) {
10542		    print STDERR M("Diff-Ausgabe wird verwendet"), "\n";
10543		    print STDERR Mfmt("Anzahl der neu zu zeichnenden Objekte: %d", scalar @{$new_str->data}), "\n";
10544		    print STDERR Mfmt("Anzahl der zu l�schenden Objekte: %d", scalar @$todelref), "\n";
10545		}
10546		foreach (@$todelref) {
10547		    $c->delete("$abk-$_");
10548		}
10549		$str = $new_str;
10550		$diffed_str = 1;
10551		$gesperrt = $str;
10552	    }
10553	}
10554
10555	my %conv_args;
10556	if ($args{-map}) {
10557	    $conv_args{Map} = $args{-map};
10558	}
10559	my $conv = $gesperrt->get_conversion(%conv_args);
10560
10561	my $use_inwork_photo = get_symbol_scale('attrib-inwork');
10562
10563	$gesperrt->init;
10564	my $real_pos = -1;
10565	while (1) {
10566	    $real_pos++;
10567	    my $pos = $indexmap && exists $indexmap->{$real_pos} ? $indexmap->{$real_pos} : $real_pos;
10568	    my $ret = $gesperrt->next;
10569	    my @kreuzungen = @{$ret->[Strassen::COORDS]};
10570	    last if !@kreuzungen;
10571            @kreuzungen = map { $conv->($_) } @kreuzungen
10572		if $conv;
10573
10574	    my($icon_x, $icon_y, $icon_anchor);
10575	    my $sub_cat;
10576	    my($cat,$addinfo) = $ret->[Strassen::CAT] =~ m{^(.*?)(?:::?(.*))?$};
10577	    my @addinfo = $addinfo ? split(':', $addinfo): ();
10578	    my %addinfo = map {($_,1)} @addinfo;
10579	    if (!$edit_normal_mode) { # we want to see everything in edit mode
10580	        next if $addinfo{'igndisp'};
10581	    }
10582	    if ($cat eq StrassenNetz::BLOCKED_CARRY) {
10583		if ($width0) { # gr��er 0
10584		    $sub_cat = 'sperre0';
10585		    my($x,$y) =
10586		      transpose(@{Strassen::to_koord1($kreuzungen[0])});
10587
10588		    my $rad = deg2rad($addinfo[1] || 0); # addinfo[1] is angle
10589		    my $cos4 = cos($rad)*4;
10590		    my $sin4 = sin($rad)*4;
10591		    for my $add ([-$cos4,$sin4], [0,0], [$cos4,-$sin4]) {
10592			my($yadd,$xadd) = @$add;
10593			$c->createLine
10594			    ($x-$cos4+$xadd, ($y+$yadd)-$sin4, $x+$cos4+$xadd, ($y+$yadd)+$sin4,
10595			     -width => $width0, # XXX $width0 verwenden und in get_line_width anpassen
10596			     -tags => [$abk, $sub_cat,
10597				       $ret->[Strassen::NAME], $abk.'-'.$pos],
10598			    );
10599		    }
10600		    ($icon_x, $icon_y, $icon_anchor) = ($x, $y, 'n');
10601		}
10602	    } elsif ($cat eq StrassenNetz::BLOCKED_NARROWPASSAGE) {
10603#XXX works, but write nicer...
10604		# if ($widthBNP) XXX
10605		$sub_cat = 'sperreBNP';
10606		my($x,$y) =
10607		    transpose(@{Strassen::to_koord1($kreuzungen[0])});
10608
10609		my $rad = deg2rad($addinfo[1] || 0); # addinfo[1] is angle
10610		my $cos1 = cos($rad);
10611		my $sin1 = sin($rad);
10612		my $cos4 = cos($rad)*4;
10613		my $sin4 = sin($rad)*4;
10614		my $tags = [$abk, $sub_cat, $ret->[Strassen::NAME], $abk.'-'.$pos];
10615		for my $add ([-$cos1,$sin1]) {
10616		    my($yadd,$xadd) = @$add;
10617		    $c->createLine
10618			($x-$cos1+$xadd, ($y+$yadd)-$sin1, $x+$cos4+$xadd, ($y+$yadd)+$sin4,
10619			 -width => $width0, # XXX $widthBNP verwenden und in get_line_width anpassen
10620			 -tags => $tags,
10621			);
10622		}
10623		for my $add ([$cos1,-$sin1]) {
10624		    my($yadd,$xadd) =  @$add;
10625		    $c->createLine
10626			($x-$cos4+$xadd, ($y+$yadd)-$sin4, $x+$cos1+$xadd, ($y+$yadd)+$sin1,
10627			 -width => $width0, # XXX $widthBNP verwenden und in get_line_width anpassen
10628			 -tags => $tags,
10629			);
10630		}
10631		if ($addinfo{'trailer=no'} && $notrailer_photo) {
10632		    my($xm,$ym) = ($x+$cos1-$sin1, $y+$cos1+$sin1);
10633		    $c->createImage($xm,$ym,
10634				    -anchor => 'nw',
10635				    -image => $notrailer_photo,
10636				    -tags => $tags);
10637		}
10638		($icon_x, $icon_y, $icon_anchor) = ($x, $y, 'n');
10639	    } elsif ($cat =~ /^@{[ StrassenNetz::BLOCKED_ROUTE ]}(nocross)?/) {
10640		my $is_nocross = defined $1;
10641		$sub_cat = 'sperre3';
10642		my @c;
10643		for(my $i = 0; $i <= $#kreuzungen; $i++) {
10644		    push @c, map { transpose(@$_) } Strassen::to_koord1($kreuzungen[$i]);
10645		}
10646
10647		line_shorten(\@c);
10648
10649		if (!$is_nocross) {
10650		    # move to the right
10651		    my $delta = -3;
10652		    for(my $i = 2; $i < $#c; $i+=2) {
10653			# atan2(y2-y1, x2-x1)
10654			my $alpha = atan2($c[$i+1]-$c[$i-1], $c[$i]-$c[$i-2]);
10655			my $beta  = $alpha - pi()/2;
10656			my($dx, $dy) = ($delta*cos($beta), $delta*sin($beta));
10657			$c[$i] += $dx;
10658			$c[$i+1] += $dy;
10659			if ($i == 2) {
10660			    $c[0] += $dx;
10661			    $c[1] += $dy;
10662			}
10663		    }
10664		}
10665
10666		$c->createLine
10667		    (@c,
10668		     -width => (!$is_nocross ? $width3 : $width3_nocross),
10669		     (!$is_nocross ? (-arrow => 'last',
10670				      -arrowshape => [4,6,3],
10671				      -smooth => 1,
10672				      -fill => 'red',
10673				     )
10674		                   : (-fill => '#ff4500',
10675				     )
10676		     ),
10677		     ($Tk::VERSION >= 800.016 ? (-dash => $line_dash{sperre3}) : ()),
10678		     -tags => [$abk, $sub_cat,
10679			       $ret->[Strassen::NAME], $abk.'-'.$pos],
10680		    );
10681		($icon_x, $icon_y, $icon_anchor) = ($c[0], $c[1], 'n');
10682	    } else {
10683		$sub_cat = $type2cat{$cat};
10684		if ($cat eq StrassenNetz::BLOCKED_COMPLETE && $#kreuzungen == 0) {
10685		    # ein bisschen schummeln ...
10686		    push @kreuzungen, $kreuzungen[0];
10687		}
10688		my $tags = [$abk, $sub_cat,
10689			    $ret->[Strassen::NAME], $abk.'-'.$pos];
10690
10691		my $plot_one = sub {
10692		    my($p_ref, $inx) = @_;
10693		    my($x1,$y1) =
10694			transpose(@{Strassen::to_koord1($p_ref->[$inx])});
10695		    my($x2,$y2) =
10696			transpose(@{Strassen::to_koord1($p_ref->[$inx+1])});
10697		    my($xm,$ym) = (int(($x2-$x1)/2+$x1), int(($y2-$y1)/2+$y1));
10698
10699		    if ($cat eq StrassenNetz::BLOCKED_ONEWAY ||
10700			$cat eq StrassenNetz::BLOCKED_ONEWAY_STRICT) {
10701			my $alpha = atan2($y2-$y1, $x2-$x1);
10702			my($xd,$yd) = ($length1*cos($alpha),
10703				       $length1*sin($alpha));
10704			$c->createLine($xm+$xd, $ym+$yd, $xm-$xd, $ym-$yd,
10705				       -fill => $type2fill{$cat},
10706				       -width => $width1,
10707				       -arrow => 'last',
10708				       -arrowshape => [4,6,3],
10709				       -tags => $tags,
10710				      );
10711		    } elsif ($cat eq StrassenNetz::BLOCKED_COMPLETE) {
10712			# $c->createImage($xm,$ym,
10713			# 	    -image => $blocked_photo,
10714			# 	    -tags => $tags);
10715			$c->createLine($xm-$length2, $ym-$length2,
10716				       $xm+$length2, $ym+$length2,
10717				       -fill => $fill2,
10718				       -width => $width2,
10719				       -tags => $tags);
10720			$c->createLine($xm-$length2, $ym+$length2,
10721				       $xm+$length2, $ym-$length2,
10722				       -fill => $fill2,
10723				       -width => $width2,
10724				       -tags => $tags);
10725		    }
10726
10727		    my @anchors = qw(nw sw ne se);
10728
10729		    # Add an additional icon
10730		    for my $check (['inwork', $use_inwork_photo],
10731				   ['night',  $night_photo],
10732				   ['clock',  $clock_photo],
10733				   ['tempmaybe', $cal_questionmark_photo],
10734				   ['temp',   $cal_photo], # should be last
10735				  ) {
10736			my($addinfo, $photo) = @$check;
10737			if ($addinfo{$addinfo} && $photo) {
10738			    $c->createImage($xm,$ym,
10739					    -anchor => shift(@anchors),
10740					    -image => $photo,
10741					    -tags => [@$tags,"attrib-$addinfo"]);
10742			    last;
10743			}
10744		    }
10745
10746		    if ($is_car && $car_photo) {
10747			$c->createImage($xm, $ym,
10748					-image => $car_photo,
10749					-anchor => shift(@anchors),
10750					-tags => [$abk, $sub_cat,$ret->[Strassen::NAME], $abk.'-'.$pos]);
10751		    }
10752		};
10753
10754		if ($advanced) { # XXX decide one day if this should be the default
10755		    # "Sparse plotting": only one symbol between a
10756		    # crossing-limited hop.
10757		    my @hops = split_by_crossings(@kreuzungen);
10758		    for my $hop (@hops) {
10759			my $inx;
10760			if ($cat eq StrassenNetz::BLOCKED_ONEWAY ||
10761			    $cat eq StrassenNetz::BLOCKED_ONEWAY_STRICT) {
10762			    $inx = $#{$hop}-1;
10763			} elsif ($cat eq StrassenNetz::BLOCKED_COMPLETE) {
10764			    $inx = int($#{$hop}/2);
10765			} else {
10766			    # may happen for "q4" entries from temp-blockings
10767			    $inx = 0; # dummy, to avoid warnings
10768			}
10769			$plot_one->($hop, $inx);
10770		    }
10771		} else {
10772		    # Plot symbol on every segment in the line
10773		    for my $inx (0 .. $#kreuzungen-1) {
10774			$plot_one->(\@kreuzungen, $inx);
10775		    }
10776		}
10777	    }
10778
10779	    if ($is_car && $car_photo && defined $icon_x) {
10780		$c->createImage($icon_x, $icon_y,
10781				-image => $car_photo,
10782				-anchor => $icon_anchor,
10783				-tags => [$abk, $sub_cat,$ret->[Strassen::NAME], $abk.'-'.$pos]);
10784	    }
10785	}
10786
10787	if (($edit_mode || $edit_normal_mode || $args{FastUpdate}) and !$diffed_str) {
10788	    warn "Try to copy original data" if $verbose;
10789	    my $r = $complete_str->copy_orig;
10790	    warn "Returned $r" if $verbose;
10791	}
10792
10793    };
10794    warn $@ if $@;
10795    DecBusy($top);
10796    status_message(Mfmt("Layer <Sperrungen> gezeichnet"), "info");
10797    Hooks::get_hooks("after_plot")->execute;
10798}
10799
10800sub _line_shorten {
10801    my($cref, $begin, $end) = @_;
10802    if (@$cref <= 2) {
10803	warn "Coordinate list too short for shortening either begin or end\n";
10804	return;
10805    }
10806    if ($begin && $end && @$cref <= 4) {
10807	warn "Coordinate list too short for shortening begin and end (@$cref)\n";
10808	return;
10809    }
10810
10811    if ($begin) {
10812	my $len1 = Strassen::Util::strecke([@{$cref}[0,1]], [@{$cref}[2,3]]);
10813	my $whole_len1 = $len1 > 20 ? 20 : $len1;
10814	@{$cref}[0,1] =
10815	    (($cref->[0]-$cref->[2])/$len1*$whole_len1+$cref->[2],
10816	     ($cref->[1]-$cref->[3])/$len1*$whole_len1+$cref->[3],
10817	    );
10818    }
10819    if ($end) {
10820	my $len2 = Strassen::Util::strecke([@{$cref}[-4,-3]], [@{$cref}[-2,-1]]);
10821	my $whole_len2 = $len2 > 20 ? 20 : $len2;
10822	@{$cref}[-2,-1] =
10823	    (($cref->[-2]-$cref->[-4])/$len2*$whole_len2+$cref->[-4],
10824	     ($cref->[-1]-$cref->[-3])/$len2*$whole_len2+$cref->[-3],
10825	    );
10826    }
10827}
10828
10829sub line_shorten_begin { _line_shorten(shift, 1, 0) }
10830sub line_shorten       { _line_shorten(shift, 1, 1) }
10831sub line_shorten_end   { _line_shorten(shift, 0, 1) }
10832
10833sub split_by_crossings {
10834    my @p = @_;
10835    return () if !@p;
10836    my $crossings = all_crossings();
10837    my @ret = [$p[0]];
10838    if (@p > 2) {
10839	for my $p_i (1 .. $#p-1) {
10840	    my $p = $p[$p_i];
10841	    push @{ $ret[-1] }, $p;
10842	    if (exists $crossings->{$p}) {
10843		push @ret, [$p];
10844	    }
10845	}
10846    }
10847    push @{ $ret[-1] }, $p[-1];
10848    @ret;
10849}
10850
10851######################################################################
10852# temp blockings
10853sub get_temp_blockings_files {
10854    my $temp_blockings_dir = "$datadir/temp_blockings";
10855    my $file = "$temp_blockings_dir/bbbike-temp-blockings.pl";
10856    my $optimized_file = "$temp_blockings_dir/bbbike-temp-blockings-optimized.pl";
10857    return { dir            => $temp_blockings_dir,
10858	     file           => $file,
10859	     optimized_file => $optimized_file,
10860	   };
10861}
10862
10863sub activate_temp_blockings {
10864    my $do_show_active_temp_blockings = shift;
10865    my(%args) = @_;
10866    my $now = $args{-now} || time;
10867    my $from = $args{-from};
10868
10869    my($temp_blockings_dir, $file, $optimized_file) =
10870	@{ get_temp_blockings_files() }{qw(dir file optimized_file)};
10871    if (!-r $file && !-r $optimized_file) {
10872	status_message(M("Kein Support fuer temporaere Sperrungen, das Verzeichnis $temp_blockings_dir fehlt. Dieses Verzeichnis ist per git erh�ltlich, siehe README."), "warn");
10873	return;
10874    }
10875
10876    # Use the optimized file?
10877    if (!-r $file) {
10878	$file = $optimized_file;
10879    } elsif (!defined $from || $from >= $now) {
10880	if (-r $optimized_file && -s $optimized_file &&
10881	    -M $optimized_file <= -M $file) {
10882	    $file = $optimized_file;
10883	}
10884    }
10885
10886    if (!$do_show_active_temp_blockings) {
10887	$show_active_temp_blockings = 0;
10888	plot("p", "temp_sperre", -draw => 0);
10889	plot("str", "temp_sperre_s", -draw => 0);
10890	#XXX del? ??? not needed??? make_net(); # XXX find more performant solution
10891	#XXX del? undef $temporary_handicap_s;
10892	#if ($handicap_s_net) {
10893	#    undef $handicap_s_net;
10894	#    make_handicap_net();
10895	#}
10896	undef $current_temp_blockings_net;
10897	undef $current_temp_blockings_ms;
10898	reset_temp_blockings();
10899	hide_blockings();
10900	hide_blockings_infobar();
10901	return;
10902    }
10903
10904    eval {
10905	use vars qw(@temp_blocking); # XXX do not use a global such as this
10906	use vars qw(%temp_blocking_inx_mapping); # XXX dito
10907	@temp_blocking = ();
10908	do $file; # XXX Safe?
10909	my($file_mtime) = (stat($file))[9];
10910	my @s;
10911	my $global_inx = -1;
10912	my $used_inx = -1;
10913	for my $o (@temp_blocking) {
10914	    $global_inx++;
10915	    next if !$o; # undefined entry
10916	    my $do_it = 0;
10917	    if (defined $from && (!defined $o->{until} || $o->{until} > $from)) {
10918		$do_it = 1;
10919	    }
10920	    if (!$do_it && ((!defined $o->{from} || $o->{from} < $now) &&
10921			    (!defined $o->{until} || $o->{until} > $now))) {
10922		$do_it = 1;
10923	    }
10924
10925	    if ($do_it) {
10926		require POSIX;
10927		my $datefmt = "%d.%m.%Y %H:%M:%S";
10928		my $date_spec;
10929		{
10930		    my $from_date_readable = defined $o->{from}  ? POSIX::strftime($datefmt, localtime($o->{from})) : "...";
10931		    my $to_date_readable   = defined $o->{until} ? POSIX::strftime($datefmt, localtime($o->{until})) : "...";
10932		    if ($from_date_readable eq '...' && $to_date_readable eq '...') {
10933			if ($o->{permanent} || $o->{recurring}) {
10934			    $date_spec = M"periodische Sperrung";
10935			} else {
10936			    $date_spec = M"Ende unbekannt";
10937			}
10938		    } else {
10939			$date_spec = $from_date_readable . " - " . $to_date_readable;
10940		    }
10941		}
10942		my $text = $o->{text} . " [" . $date_spec . "]";
10943		my $s;
10944		my $f;
10945		my $mtime;
10946		if ($o->{file}) {
10947		    $f = "$temp_blockings_dir/$o->{file}";
10948		    $s = Strassen->new($f);
10949		    $mtime = $s->{Modtime};
10950		} else {
10951		    $s = Strassen->new_from_data_string($o->{data});
10952		    $mtime = $file_mtime;
10953		}
10954		my $new_s = Strassen->new;
10955		push @{$new_s->{DependentFiles}}, $f if $f;
10956		$s->init;
10957		while(1) {
10958		    my $ret = $s->next;
10959		    last if !@{ $ret->[Strassen::COORDS()] };
10960		    $ret->[Strassen::NAME] = $text;
10961		    $new_s->push($ret);
10962		    $new_s->set_directives_for_current({ info => [$o] });
10963		    $used_inx++;
10964		    $temp_blocking_inx_mapping{$used_inx} = $global_inx;
10965		}
10966		$new_s->{Modtime} = $mtime;
10967		push @s, $new_s;
10968	    }
10969	}
10970	if (!@s) {
10971	    if ($verbose) {
10972		if (defined $args{-now}) {
10973		    print STDERR "Keine aktuellen Sperrungen am " . scalar(localtime($now)) . "\n";
10974		} else {
10975		    print STDERR "Keine aktuellen Sperrungen\n";
10976		}
10977	    }
10978	    return;
10979	}
10980	my $ms = MultiStrassen->new(@s);
10981	push @{ $ms->{DependentFiles} }, $file;
10982	if ($current_temp_blockings_ms && $current_temp_blockings_ms->shallow_compare($ms)) {
10983	    warn "INFO: no change in temp blockings detected...\n";
10984	} else {
10985	    $current_temp_blockings_ms = $ms;
10986	    $current_temp_blockings_net = StrassenNetz->new($ms);
10987	    $current_temp_blockings_net->make_net_cat(-onewayhack => 1, -net2name => 1);
10988	    $current_temp_blockings_net->make_sperre($ms, Type => ['wegfuehrung']);
10989	    print STDERR "Aktuelle Sperrungen: " . join(", ", $ms->dependent_files) . "\n" if $verbose;
10990	    add_temp_blockings_to_net();
10991	    plot("p", "temp_sperre", -object => $ms, -draw => 1);
10992	    plot("str", "temp_sperre_s", -object => $ms, -draw => 1);
10993	    if (@realcoords) {
10994		clear_undecided_temp_blockings();
10995		check_path_in_blockings_net(\@realcoords);
10996	    }
10997	}
10998    };
10999    if ($@) {
11000	$show_active_temp_blockings = 0;
11001	status_message($@, "warn"); # do not die, may be called before mainloop
11002    } else {
11003	$show_active_temp_blockings = 1;
11004    }
11005}
11006
11007sub gui_activate_temp_blockings {
11008    if (!$show_active_temp_blockings) {
11009	$show_active_temp_blockings = 1;
11010    }
11011    activate_temp_blockings($show_active_temp_blockings);
11012}
11013
11014sub refresh_temp_blockings {
11015    if ($show_active_temp_blockings) {
11016	activate_temp_blockings($show_active_temp_blockings);
11017    }
11018}
11019
11020sub apply_temp_blockings {
11021    make_net() if !$net;
11022    add_temp_blockings_to_net();
11023    re_search_gui();
11024}
11025
11026sub add_temp_blockings_to_net {
11027    make_net() if !$net;
11028    make_handicap_net() if !$handicap_s_net;
11029    my $add_sperre_s   = Strassen->new;
11030    my $add_handicap_s = Strassen->new;
11031    while(my($name,$v) = each %temp_blockings_on_route) {
11032	if ($v->{state} eq 'active') {
11033	    for my $r (@{ $v->{data} }) {
11034		my $s;
11035		if ($r->[Strassen::CAT] =~ m{^q}) {
11036		    $s = $add_handicap_s;
11037		} else {
11038		    $s = $add_sperre_s;
11039		}
11040		$s->push($r);
11041	    }
11042	}
11043    }
11044
11045    eval { # XXX check first if there's something to pop?
11046	$handicap_s_net->pop_stack;
11047    };
11048    my $add_handicap_s_net = StrassenNetz->new($add_handicap_s);
11049    $add_handicap_s_net->make_net_cat;
11050    $handicap_s_net->push_stack($add_handicap_s_net);
11051
11052    $net->remove_all_from_deleted(undef, 'std-temp-blockings');
11053    $net->make_sperre($add_sperre_s, Type => 'all', DelToken => 'std-temp-blockings');
11054}
11055
11056
11057sub reset_temp_blockings {
11058    %temp_blockings_on_route = ();
11059    apply_temp_blockings();
11060}
11061
11062sub _add_to_temp_blockings_on_route {
11063    my($r) = @_;
11064    my $blocking_text = $r->[Strassen::NAME];
11065    if (!exists $temp_blockings_on_route{$blocking_text}) {
11066	# Gather all records belonging to this blocking:
11067	my @data;
11068	$current_temp_blockings_ms->init;
11069	while() {
11070	    my $r = $current_temp_blockings_ms->next;
11071	    my @c = @{ $r->[Strassen::COORDS] };
11072	    last if !@c;
11073	    if ($r->[Strassen::NAME] eq $blocking_text) {
11074		push @data, $r;
11075	    }
11076	}
11077	$temp_blockings_on_route{$blocking_text} = { state => 'undecided',
11078						     data => \@data,
11079						   };
11080    }
11081}
11082
11083sub clear_undecided_temp_blockings {
11084    for my $name (keys %temp_blockings_on_route) {
11085	delete $temp_blockings_on_route{$name}
11086	    if $temp_blockings_on_route{$name}->{state} eq 'undecided';
11087    }
11088}
11089
11090sub check_path_in_blockings_net {
11091    return if !$current_temp_blockings_net;
11092    my($pathref) = @_;
11093    my $net         = $current_temp_blockings_net->{Net};
11094    my $wegfuehrung = $current_temp_blockings_net->{Wegfuehrung};
11095 PATH_SEGMENT: for my $p_i (0 .. $#$pathref-1) {
11096	my($xy0, $xy1) = (join(',', @{$pathref->[$p_i]}),
11097			  join(',', @{$pathref->[$p_i+1]}));
11098
11099	# Handling "1"/"2" and "qX" types
11100	if (exists $net->{$xy0} && exists $net->{$xy0}{$xy1}) {
11101	    my($pos) = $current_temp_blockings_net->net2name($xy0, $xy1);
11102	    if (defined $pos) {
11103		my $r = $current_temp_blockings_ms->get($pos);
11104		my $cat = $r->[Strassen::CAT];
11105		if ($cat ne '3') {
11106		    _add_to_temp_blockings_on_route($r);
11107		    next PATH_SEGMENT;
11108		}
11109		# XXX else: Handled in the Wegf�hrung part
11110	    }
11111	}
11112
11113	# Handling "3" (wegfuehrung) types
11114	if ($wegfuehrung && exists $wegfuehrung->{$xy1}) {
11115	    for my $wegfuehrung (@{ $wegfuehrung->{$xy1} }) {
11116	    CHECK_WEGFUEHRUNG: {
11117		    for(my $j=0; $j<$#$wegfuehrung; $j++) {
11118			last CHECK_WEGFUEHRUNG
11119			    if ($j > $p_i || join(",",@{$pathref->[$p_i-$j]}) ne $wegfuehrung->[$#$wegfuehrung-1-$j]);
11120		    }
11121		    # XXX Hackish: find a matching record in $current_temp_blockings_ms
11122		    my $matching_r;
11123		    $current_temp_blockings_ms->init;
11124		    while() {
11125			my $r = $current_temp_blockings_ms->next;
11126			my @c = @{ $r->[Strassen::COORDS] };
11127			last if !@c;
11128			for my $c_i (0 .. $#c-1) {
11129			    if ($xy0 eq $c[$c_i] && $xy1 eq $c[$c_i+1]) {
11130				_add_to_temp_blockings_on_route($r);
11131				next PATH_SEGMENT; # XXX is this correct? or should we get all the wegf�hrung here?
11132			    }
11133			}
11134		    }
11135		}
11136	    }
11137	}
11138    }
11139
11140    if (first { $temp_blockings_on_route{$_}->{state} eq 'undecided' } keys %temp_blockings_on_route) {
11141	show_blockings_infobar();
11142    } else {
11143	hide_blockings_infobar();
11144    }
11145    if (Tk::Exists($toplevel{temp_blockings})) {
11146	show_blockings();
11147    }
11148}
11149
11150sub show_blockings {
11151    my $blockings_toplevel = redisplay_top($top, 'temp_blockings',
11152					   -title => M"Aktuelle Sperrungen",
11153					  );
11154    my $toplevel_width = int($top->screenwidth*0.7);
11155    if (!defined $blockings_toplevel) {
11156	$blockings_toplevel = $toplevel{'temp_blockings'};
11157	# XXX quick'n'dirty solution... better to keep all the widgets
11158	# and to just clear the items from the hlists.
11159	$_->destroy for ($blockings_toplevel->children);
11160    } else {
11161	$blockings_toplevel->geometry($toplevel_width."x200");
11162    }
11163
11164    # packer priority -> draw first
11165    my $footer = $blockings_toplevel->Frame->pack(qw(-fill x -side bottom));
11166    my $cb = $footer->Button(Name => "close",
11167			     -command => sub { $blockings_toplevel->destroy })->pack(-anchor => 'e', -side => "right");
11168    $blockings_toplevel->bind('<Escape>' => sub { $cb->invoke });
11169
11170
11171    if (!keys %temp_blockings_on_route) {
11172	$blockings_toplevel->Label(-text => M"Keine Sperrungen auf der Route", -font => $font{bold})->pack;
11173    } else {
11174	my %gui_temp_blockings_on_route_active = map { ($_ => $temp_blockings_on_route{$_}->{state} eq 'active') } keys %temp_blockings_on_route;
11175
11176	require Tk::HList;
11177	my $hl;
11178	$hl = $blockings_toplevel->Scrolled
11179	    ('HList',
11180	     -columns => 3,
11181	     -header => 1,
11182	     -selectmode => 'single',
11183	     -browsecmd => sub {
11184		 my($hl_index) = @_;
11185		 return if !defined $hl_index;
11186		 my $name = $hl->info('data', $hl_index);
11187		 return if !defined $name;
11188		 my $coords = [
11189			       map { [ transpose_all(@{ Strassen::to_koord($_->[Strassen::COORDS]) }) ] }
11190			       @{ $temp_blockings_on_route{$name}->{data} }
11191			      ];
11192		 mark_street(-coords => $coords);
11193	     },
11194	     -scrollbars => 'osoe',
11195	    )->pack(qw(-fill both));
11196	$hl->anchorClear;
11197	$hl->headerCreate(0, -text => M"Aktivieren");
11198	$hl->headerCreate(1, -text => M"Sperrung");
11199	$hl->headerCreate(2, -text => M"Warn-Zeitraum");
11200	$hl->columnWidth(0, 80);
11201	my $descr_width = $toplevel_width - 200;
11202	$hl->columnWidth(1, $descr_width);
11203
11204	require Tk::ItemStyle;
11205	my(%header_style, %nopad_style, %descr_style, %text_style, %bg_color);
11206	for my $key (qw(odd even)) {
11207	    my $bg_color = $key eq 'even' ? $hl->cget('-background') : '#dddddd';
11208	    $bg_color{$key} = $bg_color;
11209	    $header_style{$key} = $hl->ItemStyle('text', -foreground => 'blue3', -font => $font{'bold'}, -background => $bg_color);
11210	    $nopad_style{$key} = $hl->ItemStyle('window', -anchor => 'nw', -pady => 0, -padx => 0); # no -background available here
11211	    $descr_style{$key} = $hl->ItemStyle('text', -wraplength => $descr_width-4, -anchor => 'nw', -background => $bg_color);
11212	    $text_style{$key} = $hl->ItemStyle('text', -background => $bg_color);
11213	}
11214
11215	my %seen_temp_blockings_on_route;
11216	my $path_i = 0;
11217
11218	my $add_line = sub {
11219	    my($name) = @_;
11220	    my $key = $path_i % 2 == 0 ? 'even' : 'odd';
11221	    $hl->add($path_i, -itemtype => 'window', -style => $nopad_style{$key},
11222		     -widget => $hl->Checkbutton(-variable => \$gui_temp_blockings_on_route_active{$name},
11223						 -onvalue => 1,
11224						 -offvalue => 0,
11225						 -background => $bg_color{$key},
11226						 -highlightthickness => 0,
11227						),
11228		     -data => $name,
11229		    );
11230	    if (my($desc, $date_spec) = $name =~ m{^(.*)\s\[(.*?)\]$}) {
11231		$hl->itemCreate($path_i, 1, -text => $desc, -style => $descr_style{$key});
11232		$hl->itemCreate($path_i, 2, -text => $date_spec, -style => $text_style{$key});
11233	    } else {
11234		warn "Could not parse '$name'";
11235		$hl->itemCreate($path_i, 1, -text => $name, -style => $descr_style{$key});
11236	    }
11237	    $path_i++;
11238	};
11239
11240	for my $name (sort keys %temp_blockings_on_route) {
11241	    if ($temp_blockings_on_route{$name}->{state} eq 'undecided') {
11242		$add_line->($name);
11243	    }
11244	}
11245
11246	my $not_used_header_shown;
11247	for my $name (sort keys %temp_blockings_on_route) {
11248	    next if $temp_blockings_on_route{$name}->{state} eq 'undecided';
11249	    if (!$not_used_header_shown) {
11250		my $key = $path_i % 2 == 0 ? 'even' : 'odd';
11251		$hl->add($path_i);
11252		$hl->itemCreate($path_i, 0, -style => $text_style{$key});
11253		$hl->itemCreate($path_i, 1, -text => M"Bereits behandelte Sperrungen", -style => $header_style{$key});
11254		$hl->itemCreate($path_i, 2, -style => $text_style{$key});
11255		$path_i++;
11256		$not_used_header_shown++;
11257	    }
11258	    $add_line->($name);
11259	}
11260
11261	$footer->Button(-text => M"Ausgew�hlte umfahren",
11262			-command => sub {
11263			    while(my($name,$v) = each %gui_temp_blockings_on_route_active) {
11264				$temp_blockings_on_route{$name}->{state} = $v ? 'active' : 'ignore';
11265			    }
11266			    apply_temp_blockings();
11267			})->pack(-anchor => 'w', -side => "left");
11268
11269	$footer->Button(-text => M"Alle umfahren",
11270			-command => sub {
11271			    while(my($name,$v) = each %temp_blockings_on_route) {
11272				$temp_blockings_on_route{$name}->{state} = 'active';
11273			    }
11274			    apply_temp_blockings();
11275			})->pack(-anchor => 'e', -side => "right");
11276	$footer->Button(-text => M"Alle ignorieren",
11277			-command => sub {
11278			    while(my($name,$v) = each %temp_blockings_on_route) {
11279				$temp_blockings_on_route{$name}->{state} = 'ignore';
11280			    }
11281			    apply_temp_blockings();
11282			})->pack(-anchor => 'e', -side => 'right');
11283    }
11284}
11285
11286sub hide_blockings {
11287    if (Tk::Exists($toplevel{temp_blockings})) {
11288	$toplevel{temp_blockings}->destroy;
11289    }
11290}
11291
11292######################################################################
11293### AutoLoad Sub
11294sub read_sperre_tragen {
11295    if (!eval { StrassenNetz::make_sperre_tragen(get_strassen_file($sperre_file), get_special_vehicle(), \%sperre_tragen, \%sperre_narrowpassage); 1 }) {
11296	warn $@;
11297    }
11298}
11299
11300# Liest aus der Datenbasis die Ampelinformation ein.
11301### AutoLoad Sub
11302sub read_ampeln {
11303    my($force) = @_;
11304    return if (!$force && keys %ampeln != 0);
11305    if (!eval {
11306	$p_obj{'lsa'} = new Strassen get_strassen_file($p_file{'lsa'});
11307	%ampeln = %{ $p_obj{'lsa'}->get_hashref_by_cat };
11308	1;
11309    }) {
11310	warn $@;
11311	%ampeln = ();
11312    }
11313}
11314
11315# Liest aus der Datenbasis die H�heninformation ein.
11316### AutoLoad Sub
11317sub read_hoehe {
11318    my(%args) = @_;
11319    return if (!$args{-force} && keys %hoehe != 0 &&
11320	       $p_obj{"hoehe"} && $p_obj{"hoehe"}->is_current);
11321    if (!eval {
11322	my $h = new Strassen ($args{-file}
11323			      ? $args{-file}
11324			      : get_strassen_file("hoehe")
11325			     );
11326	%hoehe = %{ $h->get_hashref };
11327	$p_obj{"hoehe"} = $h;
11328	1;
11329    }) {
11330	warn $@;
11331	%hoehe = ();
11332    }
11333}
11334
11335# Zeichnet die H�hendaten.
11336### AutoLoad Sub
11337sub plot_hoehe {
11338    my(%args) = @_;
11339    Hooks::get_hooks("before_plot")->execute;
11340    $c->delete('hoehe');
11341    if ($p_draw{'hoehe'}) {
11342	my $coordsys = $coord_system_obj->coordsys;
11343	IncBusy($top);
11344	eval {
11345	    read_hoehe(%args);
11346	    while(my($koord,$hoehe) = each %hoehe) {
11347		my($xx,$yy) = split(/,/, $koord);
11348		if ($edit_mode && $xx =~ /([A-Za-z])?(-?\d+)$/) {
11349		    my $this_coordsys = (defined $1 ? $1 : '');
11350		    if ($this_coordsys eq $coordsys ||
11351			!($this_coordsys ne '' || $coordsys ne 'B')) {
11352			$xx = $2;
11353		    } else {
11354			next; # while
11355		    }
11356		}
11357		my($x, $y) = transpose($xx, $yy);
11358		$c->createLine($x, $y, $x+1, $y+1,
11359			       -fill => 'red',
11360			       -tags => 'hoehe',
11361			      );
11362		$c->createText($x+1, $y+1, -anchor => 'nw',
11363			       -font => $font{'small'},
11364			       -text => $hoehe,
11365			       -tags => 'hoehe',
11366			      );
11367	    }
11368	};
11369	warn __LINE__ . ": $@" if $@;
11370	DecBusy($top);
11371    }
11372    Hooks::get_hooks("after_plot")->execute;
11373}
11374
11375# XXX Folgende drei Funktionen zusammenfassen
11376# Gibt ein Gew�sser-Objekt zur�ck.
11377### AutoLoad Sub
11378sub _get_wasser_obj {
11379    my $filename = shift;
11380    my @obj;
11381    if ($wasserstadt) {
11382	push @obj, Strassen->new($filename);
11383    }
11384    if ($wasserumland) {
11385	push @obj, Strassen->new(get_strassen_file("wasserumland"));
11386    }
11387    if ($str_far_away{'w'}) {
11388	push @obj, Strassen->new(get_strassen_file("wasserumland2"));
11389    }
11390    return if !@obj;
11391    return $obj[0] if (@obj == 1);
11392    new MultiStrassen @obj;
11393}
11394
11395# Gibt ein Orte-Objekt zur�ck.
11396### AutoLoad Sub
11397sub _get_orte_obj {
11398    my $type = shift || "o";
11399    my $fname  = ($type eq 'oo' ? 'orte_city' : 'orte');
11400    my @obj;
11401    push @obj, new Strassen get_strassen_file($fname);
11402    if ($p_far_away{$type}) {
11403	push @obj, new Strassen get_strassen_file($fname . "2");
11404    }
11405    return $obj[0] if (@obj == 1);
11406    new MultiStrassen @obj;
11407}
11408
11409# Gibt ein Landstra�en-Objekt zur�ck.
11410### AutoLoad Sub
11411sub _get_landstr_obj {
11412    my @obj;
11413    push @obj, new Strassen get_strassen_file($str_file{'l'});
11414    if ($str_far_away{'l'}) {
11415	my $file = "landstrassen2";
11416	push @obj, new Strassen get_strassen_file($file);
11417    }
11418    if ($str_regions{'l'}) {
11419	foreach my $file (@{ $str_regions{'l'} }) {
11420	    push @obj, new Strassen get_strassen_file($file);
11421	}
11422    }
11423    return $obj[0] if (@obj == 1);
11424    new MultiStrassen @obj;
11425}
11426
11427# Gibt ein F�hren-Objekt zur�ck.
11428### AutoLoad Sub
11429sub _get_ferry_obj {
11430    my @obj;
11431    push @obj, Strassen->new(get_strassen_file($str_file{'e'}));
11432    push @obj, eval { Strassen->new(get_strassen_file($str_file{'comm-ferry'})) };
11433    warn $@ if $@;
11434    return $obj[0] if (@obj == 1);
11435    MultiStrassen->new(@obj);
11436}
11437
11438# Gibt ein Kommentar-Objekt zur�ck.
11439### AutoLoad Sub
11440sub _get_comments_obj {
11441    my @objs;
11442    for my $type (@comments_types) {
11443	next if $type eq "mount";
11444	eval {
11445	    my $f = get_strassen_file("comments_$type");
11446	    push @objs, Strassen->new($f);
11447	}; warn $@ if $@;
11448    }
11449    MultiStrassen->new(@objs);
11450}
11451
11452# Gibt ein Fragezeichen-Objekt zur�ck.
11453### AutoLoad Sub
11454sub _get_fragezeichen_obj {
11455    my @files;
11456    push @files, get_strassen_file("fragezeichen");
11457##XXX hmmm. When editing, I don't want to see the non-orig fragezeichen.
11458##XXX But sometimes I like to... need to gather wisdom
11459#    if ($files[0] ne 'fragezeichen') { # happens in edit mode
11460#	push @files, "fragezeichen";
11461#    }
11462    my $xxx_file = catfile($FindBin::RealBin, "tmp", "XXX.bbd");
11463    if (0 && -r $xxx_file) { # XXX soll ich oder soll ich nicht XXX.bbd mit einbinden?
11464	push @files, $xxx_file;
11465    }
11466    if (@files > 1) {
11467	MultiStrassen->new(@files);
11468    } else {
11469	Strassen->new($files[0]);
11470    }
11471}
11472
11473# Zeichnet Punkte auf dem Canvas.
11474# plotp ist nur ein Dispatcher.
11475### AutoLoad Sub
11476sub plotp {
11477    my($abk, %args) = @_;
11478    return if $abk =~ /^pp/; # wird in plotstr gezeichnet
11479    return if !$c;
11480    if ($abk eq 'p') {
11481	require BBBikeAdvanced;
11482	ploths();
11483    } elsif ($abk eq 'o') {
11484	plotorte(Shortname => 1, %args);
11485    } elsif ($abk eq 'obst') {
11486	plotobst();
11487    } elsif ($abk eq 'hoehe') {
11488	plot_hoehe();
11489    } else {
11490	plot_point($abk, %args);
11491    }
11492}
11493
11494# Konfiguriert Punktsymbole, z.B. U-Bahn-Zeichen
11495### AutoLoad Sub
11496sub config_symbol {
11497    my($c, $abk, %args) = @_;
11498    my $tag_bg    = $args{'-tag_bg'} || "$abk-bg";
11499    my $tag_fg    = $args{'-tag_fg'} || "$abk-fg";
11500    my $tag_label = $args{'-tag_label'} || "$abk-label";
11501    if ($XXX_use_old_R_symbol && $abk eq 'r') {
11502	my %arg = get_symbol_scale('r');
11503	while(my($cat,$v) = each %{ $str_restrict{'r'} }) {
11504	    $c->itemconfigure
11505		("$abk-$cat-bg",
11506		 -fill => ($cat =~ m{^R[ABC]$} ? $category_color{'R'} : $category_color{$cat}),
11507		 -capstyle => $capstyle_round,
11508		 -width => $arg{-width},
11509		);
11510	}
11511	$c->itemconfigure
11512	    ($tag_fg, -anchor => 'c', -fill => 'white',
11513	     -text => (defined $arg{-font}
11514		       ? ($abk eq 'b' ? 'S' : 'R') : ''),
11515	     (defined $arg{-font} ? (-font => $arg{-font}) : ()),
11516	    );
11517	$c->itemconfigure($tag_label, -font => "$sans_serif_font_family -12");
11518	change_label_visibility($c, undef, undef, ["r-label"]);
11519    } elsif ($abk =~ /^[ubr]$/) {
11520	$c->itemconfigure($tag_fg, -image => get_symbol_scale($abk));
11521	if ($abk eq 'u') {
11522	    $c->itemconfigure('u-U0-fg', -image => get_symbol_scale('u-U0'));
11523	    $c->itemconfigure('u-UBau-fg', -image => get_symbol_scale('u-UBau'));
11524	} elsif ($abk eq 'b') {
11525	    $c->itemconfigure('b-S0-fg', -image => get_symbol_scale('b-S0'));
11526	    $c->itemconfigure('b-SBau-fg', -image => get_symbol_scale('b-SBau'));
11527	} elsif ($abk eq 'r') {
11528	    $c->itemconfigure('r-R0-fg', -image => get_symbol_scale('r-R0'));
11529	    $c->itemconfigure('r-RBau-fg', -image => get_symbol_scale('r-RBau'));
11530	    $c->itemconfigure('r-RP-fg', -image => get_symbol_scale('r-RP'));
11531	}
11532	$c->itemconfigure($tag_label, -font => "$sans_serif_font_family -12");
11533	change_label_visibility($c, undef, undef, ["$abk-label"]);
11534    } elsif ($abk =~ /^L\d+/) {
11535  	eval {
11536  	    $c->itemconfigure($tag_fg,
11537  			      -capstyle => $capstyle_round,
11538			     );
11539  	}; warn $@ if $@;
11540    } elsif ($abk eq 'pl') {
11541	$c->itemconfigure($tag_fg, -fill => 'red', -capstyle => 'projecting',
11542			  -width => 8);
11543    } elsif ($abk eq 'vf') {
11544	for my $cat (qw(Vf Kz)) {
11545	    $c->itemconfigure("$abk-$cat-fg", -image => get_symbol_scale("$abk-$cat"));
11546	}
11547	$c->itemconfigure($tag_bg, -fill => 'black',
11548			  -width => 3); # XXX width skalierbar machen
11549    } elsif ($abk =~ /^(kn|rest)$/) {
11550	$c->itemconfigure($tag_fg, -image => get_symbol_scale($abk));
11551    } elsif ($abk eq 'ki') {
11552	$c->itemconfigure($tag_fg, -image => $kino_klein_photo);
11553    }
11554}
11555
11556# Zeichnen von Punkten. Hiermit werden U-/S-/R-Bahnh�fe, Ampeln und alle
11557# sonstigen Punkte gezeichnet.
11558# Arguments:
11559#  $abk: layer token
11560#  -filename => $filename (Alias: Filename => $filename)
11561#  NameDraw => $boolean
11562### AutoLoad Sub
11563sub plot_point {
11564    my($abk, %args) = @_;
11565
11566    status_message("");
11567
11568    # Tags l�schen
11569    my @del_tags = ("$abk-bg", "$abk-img", "$abk-fg", "$abk-label");
11570
11571    if (!$args{FastUpdate}) {
11572	$c->delete($_) for (@del_tags);
11573    }
11574
11575    my($ampel_photo, $ampelf_photo, $andreaskr_photo, $andreaskr_grey_photo, $zugbruecke_photo);
11576    if ($abk eq 'lsa') {
11577	$ampel_photo      = get_symbol_scale('lsa-X');
11578	$ampelf_photo     = get_symbol_scale('lsa-F');
11579	$andreaskr_photo  = get_symbol_scale('lsa-B');
11580	$andreaskr_grey_photo = get_symbol_scale('lsa-B0');
11581	$zugbruecke_photo = get_symbol_scale('lsa-Zbr');
11582	$c->delete('lsas'); # Ampelschaltung-Symbole l�schen
11583	$c->delete('lsas-t'); # Ampelschaltung-Symbole l�schen
11584    }
11585    if (!$p_draw{$abk}) {
11586	if ($main::lazy_p{$abk}) {
11587	    bbbikelazy_remove_data("p", $abk);
11588	}
11589	status_message(Mfmt("Layer <%s> entfernt", exists $p_attrib{$abk} ? $p_attrib{$abk}->[ATTRIB_PLURAL] : $abk), "info");
11590	return;
11591    }
11592
11593    my $filename = $args{-filename} || $args{Filename};
11594    my $filename_maybe;
11595    if (!defined $filename) {
11596	$filename = get_strassen_file($p_file{$abk});
11597	$filename_maybe = $p_file{$abk} if $edit_mode_flag;
11598    }
11599    if (!defined $filename) {
11600	status_message("Filename is not defined", 'err');
11601	return;
11602    }
11603
11604    my $lazy = defined $args{-lazy} ? $args{-lazy} : $lazy_plot;
11605    if ($lazy && !$args{FastUpdate}) {
11606	status_message(Mfmt("Layer <%s> gezeichnet", exists $p_attrib{$abk} ? $p_attrib{$abk}->[ATTRIB_PLURAL] : $abk), "info");
11607	return bbbikelazy_add_data("p", $abk, $filename, {exists $args{NameDraw} ? (NameDraw => $args{NameDraw}) : ()});
11608    }
11609
11610    # XXX die anderen R�ckgabewerte (..._list, $ignore) werden noch ignoriert
11611    my($restrict) = _set_restrict($abk);
11612
11613    my $default_width;
11614    if (defined $args{Width}) { $default_width = $args{Width} }
11615
11616    my $coordsys = $coord_system_obj->coordsys;
11617
11618    destroy_delayed_restack();
11619
11620    IncBusy($top);
11621    $progress->Init(-dependents => $c,
11622		    (defined $filename ? (-label => $filename) : ()),
11623		   );
11624
11625    eval {
11626	my $bhf;
11627	if ($args{FastUpdate} ||
11628	    (defined $p_obj{$abk} &&
11629	     $p_obj{$abk}->is_current &&
11630	     $coord_system eq 'standard' &&
11631	     $abk !~ /^L\d+/)
11632	   ) {
11633	    $bhf = $p_obj{$abk};
11634	} else {
11635	    cache_decider_init();
11636	    eval {
11637		$bhf = new Strassen $filename;
11638	    };
11639	    if ($@ && $filename_maybe) {
11640		eval {
11641		    $bhf = Strassen->new($filename_maybe);
11642		};
11643	    }
11644	    if ($@) {
11645		$p_draw{$abk} = 0;
11646		die "OK" if ($abk eq 'r' && $coord_system ne 'standard');
11647		die "no-original-datadir" if $no_original_datadir;
11648		die $@;
11649	    }
11650	    if (($coord_system eq 'standard' &&
11651		 (cache_decider() || $abk =~ /^L\d+/ || $abk eq 'kn') # 'L...' und 'kn' wegen Info
11652		) ||
11653		$edit_normal_mode # Always cache in edit mode to make "reload all" work
11654	       ) {
11655		$p_obj{$abk} = $bhf;
11656	    }
11657	}
11658
11659	handle_global_directives($bhf, $abk);
11660	# XXX obsolete:
11661	if (-e "$filename.desc") {
11662	    require BBBikeAdvanced;
11663	    read_desc_file("$filename.desc", $abk);
11664	}
11665
11666	my $complete_str = $bhf;
11667	my $diffed_str = 0;
11668	my $indexmap;
11669	if ($args{FastUpdate}) {
11670	    my($new_str, $todelref);
11671	    ($new_str, $todelref, $indexmap) = $bhf->diff_orig(-clonefile => 1);
11672	    if (!defined $new_str) {
11673		print STDERR M("Diff-Ausgabe wird nicht verwendet") if $verbose;
11674		$c->delete($_) for (@del_tags);
11675	    } else {
11676		if ($verbose) {
11677		    print STDERR M("Diff-Ausgabe wird verwendet"), "\n";
11678		    print STDERR Mfmt("Anzahl der neu zu zeichnenden Punkte: %d", scalar @{$new_str->data}), "\n";
11679		    print STDERR Mfmt("Anzahl der zu l�schenden Punkte: %d", scalar @$todelref), "\n";
11680		}
11681		foreach my $id (@$todelref) {
11682		    for my $ptagadd ("") { # XXX what's necessary of the following?, "-fg", "-bg", "-img", "-label") {
11683			$c->delete("$abk$ptagadd-$id");
11684		    }
11685		}
11686		$bhf = $new_str;
11687		$diffed_str = 1;
11688	    }
11689	}
11690
11691	my %conv_args;
11692	if ($args{-map}) {
11693	    $conv_args{Map} = $args{-map};
11694	}
11695	my $conv = $bhf->get_conversion(%conv_args);
11696
11697	# XXX Experiment!!!
11698	if ($orientation eq 'landscape' &&
11699	    !$edit_mode &&
11700#XXX?       !$edit_normal_mode &&
11701	    $abk eq 'lsa' &&
11702	    !$diffed_str &&
11703	    !$conv &&
11704	    defined &BBBike::fast_plot_point) {
11705	    eval {
11706		die if $bhf->isa("Strassen::Storable");
11707		my(@files) = $bhf->file;
11708		if (grep { /\.gz$/ } @files) {
11709		    die "fast_plot_point can't handle gzipped files yet";
11710		}
11711		my(@args) = ($c, $abk,
11712			     (@files > 1 ? \@files : @files),
11713			     $progress);
11714		BBBike::fast_plot_point(@args);
11715	    };
11716	    my $err = $@;
11717	    if (!$err) {
11718		%ampeln = %{ $bhf->get_hashref_by_cat };
11719		goto PLOTPOINT_CONT;
11720	    } else {
11721		warn $err if $^W;
11722	    }
11723	}
11724
11725	my $real_i = 0;
11726	my $i;
11727	my $anzahl_eindeutig = $bhf->count;
11728	$bhf->init;
11729	# XXX Duplikat in BBBikeLazy:
11730	my $rbahn_length = ($abk eq 'r'
11731			    ? do { my(%a) = get_symbol_scale('r');
11732				   $a{-width}/2 }
11733			    : 0);
11734	my $name_draw = (exists $args{NameDraw}
11735			 ? $args{NameDraw} : $p_name_draw{$abk});
11736	my $name_draw_tag = "$abk-label";
11737	my $name_draw_other = ($name_draw_tag =~ /^[ubr]-label$/
11738			       ? [qw(u-label b-label r-label)]
11739			       : $name_draw_tag);
11740	my $no_overlap_label = (exists $args{NoOverlapLabel}
11741				? $args{NoOverlapLabel} : $no_overlap_label{$abk});
11742	my $xadd_anchor = $xadd_anchor_type->{'u'};
11743	my $yadd_anchor = $yadd_anchor_type->{'u'};
11744	my $label_spaceadd = $label_spaceadd{'u'};
11745
11746	my $draw_sub = eval $plotpoint_draw_sub;
11747	string_eval_die($@, $plotpoint_draw_sub) if $@;
11748
11749	while(1) {
11750	    my $ret = $bhf->next;
11751	    last if !@{$ret->[Strassen::COORDS]};
11752	    $progress->Update($real_i/$anzahl_eindeutig) if $real_i % 80 == 0;
11753	    $i = $indexmap && exists $indexmap->{$real_i} ? $indexmap->{$real_i} : $real_i;
11754	    $draw_sub->($ret);
11755	    $real_i++;
11756	}
11757	config_symbol($c, $abk);
11758      PLOTPOINT_CONT:
11759
11760	if (($edit_mode || $edit_normal_mode || $abk eq 'relgps' || $args{FastUpdate})) { # XXX? and !$diffed_str) {
11761	    warn "Try to copy original data" if $verbose;
11762	    my $r = $complete_str->copy_orig;
11763	    warn "Returned $r" if $verbose;
11764	}
11765
11766	restack_delayed(); # XXX check!
11767    };
11768    if ($@) {
11769	if ($@ =~ /^no-original-datadir/) {
11770	    # silently ignore
11771	} elsif ($@ !~ /^OK/) {
11772	    status_message($@, ($edit_mode || $edit_normal_mode ? 'info-stack-trace' : 'err'));
11773	}
11774    }
11775    $progress->Finish;
11776    DecBusy($top);
11777}
11778
11779# Gibt einen eindeutigen Bezeichner f�r das Caching der Orts/Stra�enlisten
11780# zur�ck.
11781### AutoLoad Sub
11782sub get_cache_identifier {
11783    my($linetype, $type) = @_;
11784    if ($linetype eq 'p') {
11785	my $fa = $p_far_away{$type} || '';
11786	$fa;
11787    } elsif ($linetype eq 's' || $linetype eq 'str') { # XXX 'str' is probably wrong...
11788	my $fa = $str_far_away{$type} || '';
11789	# XXX str_regions?
11790	my $ret = $fa;
11791	if ($type eq 'w') {
11792	    $ret .= "-$wasserstadt-$wasserumland";
11793	}
11794	$ret;
11795    } else {
11796	die "Unknown linetype: $linetype";
11797    }
11798}
11799
11800# Dialog zum Ausw�hlen einer Stra�e oder eines Ortes.
11801### AutoLoad Sub
11802sub choose_ort {
11803    my($linetype, $type, %args) = @_;
11804
11805    my $data = $args{-data};
11806    my $nodraw = $args{-nodraw};
11807    my $ondestroy = $args{-ondestroy};
11808    my $additionalframe = $args{-additionalframe};
11809    my $sorted = "auto";
11810    if (exists $args{-unsorted}) {
11811	$sorted = "unsorted";
11812    }
11813    my $splitter = $args{-splitter};
11814    my $columnwidths = $args{-columnwidths};
11815    my $container = $args{-container};
11816    my $do_popup = exists $args{-popup} ? $args{-popup} : 1;
11817    my $see = $args{-see};
11818
11819    unless ($nodraw) {
11820	if ($linetype =~ /^s/) {
11821	    if (!$str_draw{$type}) {
11822		$str_draw{$type} = 1;
11823		plot('str',$type);
11824	    }
11825	} elsif ($linetype =~ /^p/) {
11826	    if (!$p_draw{$type}) {
11827		$p_draw{$type} = 1;
11828		plot('p',$type);
11829	    }
11830	} else {
11831	    die "Unknown linetype: $linetype";
11832	}
11833    }
11834
11835    my $action = (exists $args{'-action'}
11836		  ? $args{'-action'}
11837		  : ($linetype =~ /^s/
11838		     ? \&mark_street
11839		     : ($linetype =~ /^p/
11840			? \&mark_point
11841			: die "Unknown linetype: $linetype"
11842		       )
11843		    )
11844		 );
11845
11846    if (!$args{-rebuild}) {
11847	if (!defined $choose_ort_cache{"$linetype-$type"} or
11848	    get_cache_identifier($linetype, $type)
11849	    ne $choose_ort_cache{"$linetype-$type"}) {
11850	    $args{-rebuild} = 1;
11851	}
11852    }
11853
11854    my $lb;
11855
11856    if (!$toplevel{"chooseort-$type-$linetype"} or
11857	!Tk::Exists($toplevel{"chooseort-$type-$linetype"}) or
11858	$args{'-rebuild'} or
11859	$container) {
11860	if (defined $toplevel{"chooseort-$type-$linetype"} and
11861	    Tk::Exists($toplevel{"chooseort-$type-$linetype"})) {
11862	    $toplevel{"chooseort-$type-$linetype"}->destroy;
11863	    delete $toplevel{"chooseort-$type-$linetype"};
11864	}
11865
11866	my $attrib = ($linetype eq 's'
11867		      ? $str_attrib{$type}
11868		      : $p_attrib{$type});
11869
11870	IncBusy($top);
11871	my $t;
11872	eval {
11873	    my %orte;
11874	    my @orte;
11875	    my $object;
11876	    my $conv;
11877	    my $title = $attrib ? $attrib->[ATTRIB_PLURAL] : undef;
11878	    if ($linetype =~ /^p/) {
11879		if ($data) {
11880		    $object = $data;
11881		} elsif (defined $p_obj{$type} && $coord_system eq 'standard') {
11882		    $object = $p_obj{$type};
11883		} else {
11884		    cache_decider_init();
11885		    if ($type eq 'o') {
11886			$object = _get_orte_obj("o");
11887		    } else {
11888			$object = get_strassen_obj($p_file{$type});
11889		    }
11890		    if ($coord_system eq 'standard' && cache_decider()) {
11891			$p_obj{$type} = $object;
11892		    }
11893		}
11894
11895		my $i = 0;
11896		$object->init;
11897		while(1) {
11898		    my $ret = $object->next;
11899		    last if @{$ret->[Strassen::COORDS]} == 0;
11900		    my $strname = $ret->[Strassen::NAME];
11901		    $strname =~ s/\|/ /g; # Bla|Foo: Pipe-Zeichen entfernen
11902		    $orte{$strname} = $i;
11903		    $i++;
11904		    push @orte, $strname;
11905		}
11906	    } elsif ($linetype =~ /^s/) {
11907		if ($data) {
11908		    $object = $data;
11909		} elsif (defined $str_obj{$type} && $coord_system eq 'standard') {
11910		    $object = $str_obj{$type};
11911		} else {
11912		    cache_decider_init();
11913		    $object = get_any_strassen_obj("str", $type);
11914		    if ($coord_system eq 'standard' && cache_decider()) {
11915			$str_obj{$type} = $object;
11916		    }
11917		}
11918
11919		my $i = 0;
11920		$object->init;
11921		while(1) {
11922		    my $ret = $object->next;
11923		    last if @{$ret->[Strassen::COORDS]} == 0;
11924		    my $strname = $ret->[Strassen::NAME];
11925		    $strname =~ s/\|/ /g; # Bla|Foo: Pipe-Zeichen entfernen
11926		    my @strname;
11927		    if ($attrib->[ATTRIB_LINES]) { # Linien?
11928			@strname = split(/,/, $strname);
11929		    } else {
11930			@strname = ($strname);
11931		    }
11932		    foreach $strname (@strname) {
11933			if (exists $orte{$strname}) {
11934			    $orte{$strname} .= ",$i";
11935			} else {
11936			    $orte{$strname} = $i;
11937			}
11938			push @orte, $strname;
11939		    }
11940		    $i++;
11941		}
11942	    }
11943
11944	    if ($sorted eq 'auto') {
11945		if ($object && $object->can("get_global_directive")) {
11946		    my $listing_sort = $object->get_global_directive("listing_sort");
11947		    if ($listing_sort && $listing_sort =~ m{^(unsorted|natural)$}) {
11948			$sorted = $1;
11949		    }
11950		}
11951	    }
11952	    if ($sorted eq 'auto') {
11953		$sorted = "alphabetic";
11954	    }
11955	    if ($sorted eq 'natural') {
11956		if (!eval { require Sort::Naturally; 1 }) {
11957		    status_message(M"Sort::Naturally kann nicht geladen werden, nat�rliches Sortieren ist nicht m�glich.", "info");
11958		    $sorted = "alphabetic";
11959		}
11960	    }
11961
11962	    if (!defined $title && $object && $object->can("get_global_directive")) {
11963		$title = $object->get_global_directive("title.$Msg::lang");
11964		if (!defined $title) {
11965		    $title = $object->get_global_directive("title");
11966		    if (!defined $title) {
11967			if (defined $object->file) {
11968			    $title = basename($object->file);
11969			}
11970			if (!defined $title) {
11971			    $title = "Layer $linetype/$type";
11972			}
11973		    }
11974		}
11975	    }
11976
11977	    $conv = $object && $object->get_conversion;
11978
11979	    my $Listbox = "Listbox";
11980	    if ($splitter) {
11981		$Listbox = "HList";
11982	    } else {
11983		if ($sorted eq 'alphabetic') {
11984		    if (!defined $K2Listbox) {
11985		    TRYLISTBOX: {
11986			    foreach my $try (qw(K2Listbox KListbox WListbox)) {
11987				if (eval q{ require Tk::} . $try . q{; 1;} && !$@) {
11988				    $K2Listbox = $Listbox = $try;
11989				    last TRYLISTBOX;
11990				} else {
11991				    warn "Can't use module Tk::$try: $@";
11992				}
11993			    }
11994			}
11995		    } else {
11996			$Listbox = $K2Listbox;
11997		    }
11998		}
11999	    }
12000
12001	    if ($container) {
12002		$t = $container;
12003	    } else {
12004		$t = $top->Toplevel(-title => $title,
12005				    -class => "Bbbike Chooser");
12006		set_as_toolwindow($t);
12007		if ($coord_system eq 'standard') {
12008		    if ($ondestroy) {
12009			$t->protocol('WM_DELETE_WINDOW', [$ondestroy, $t]);
12010		    } else {
12011			$t->protocol('WM_DELETE_WINDOW', sub { $t->withdraw });
12012		    }
12013		    $toplevel{"chooseort-$type-$linetype"} = $t;
12014		}
12015	    }
12016	    my($showb, $closeb);
12017
12018	    my $f = $t->Frame->pack(-side => "bottom"); # Button-Frame
12019
12020	    if ($args{'-completelistbutton'}) {
12021		my $ff = $t->Frame->pack(-side => "bottom");
12022		my $label = $args{'completelistbuttonlabel'} || M"Komplette Liste";
12023		$ff->Button(-text => $label,
12024			    -command => $args{'-completelistbutton'},
12025			   )->pack;
12026	    }
12027	    if ($additionalframe) {
12028		my $ff = $t->Frame->pack(-side => "bottom", -fill => "both");
12029		$additionalframe->($t, $ff);
12030	    }
12031
12032	    my $markf;
12033	    if ($args{'-markstartifactive'}) {
12034		if (($linetype eq 's' && $type =~ /^[sl]$/ &&
12035		     $net_type eq 's')                       ||
12036		    ($linetype eq 'p' && $type =~ /^[ub]$/ &&
12037		     $net_type eq 'us')                      ||
12038		    ($linetype eq 'p' && $type =~ /^[ubr]$/ &&
12039		     $net_type eq 'rus')                     ||
12040		    ($linetype eq 'p' && $type eq 'r' &&
12041		     $net_type eq 'r')			     ||
12042		    ($linetype eq 's' && $type =~ /^wr/ &&
12043		     $net_type eq 'wr')
12044		   ) {
12045		    $args{-markstart} = 1;
12046		}
12047	    }
12048
12049	    if ($args{'-markstart'}) {
12050		 $markf = $t->Frame->pack(-side => "bottom");
12051	    }
12052
12053	    my $max_cols;
12054	    if ($Listbox =~ /K.*Listbox/ && $Tk::VERSION >= 800) {
12055	        my $c = $t->Canvas(-takefocus => 0)->pack;
12056		my $x = 2; # 2, otherwise A may be cropped with some fonts
12057		for ('A'..'Z') {
12058		    $c->createText($x, 1,
12059				   -text => $_,
12060				   -font => $font{'small'},
12061				   -anchor => 'nw',
12062				   -tags => $_,
12063				   -fill => 'black',
12064				  );
12065		    $x += $t->fontMeasure($font{'small'}, $_);
12066		}
12067		$x+=2; # otherwise Z may be cropped
12068		my $asc = $t->fontMetrics($font{'small'}, '-ascent');
12069		my $des = $t->fontMetrics($font{'small'}, '-descent');
12070		# Note that this Canvas is NOT adjusted if the font
12071		# is changed at runtime.
12072		$c->GeometryRequest($x, $asc+$des+2);
12073		$c->bind('all', '<ButtonPress-1>' => sub {
12074			     my(@c) = $c->gettags('current');
12075			     $lb->Goto($c[0]);
12076			 });
12077		$c->bind('all', '<Enter>' => sub {
12078			     $c->itemconfigure('current', -fill => 'red');
12079			 });
12080		$c->bind('all', '<Leave>' => sub {
12081			     $c->itemconfigure('current', -fill => 'black');
12082			 });
12083	    }
12084
12085	    if ($splitter) {
12086		keys %orte; # reset
12087		my($first_ort, $first_index) = each %orte;
12088		keys %orte; # reset
12089		my(@cols) = $splitter->($first_ort, $first_index);
12090		$max_cols = scalar @cols;
12091	    }
12092
12093	    $lb = $t->Scrolled($Listbox,
12094			       -scrollbars => 'osoe',
12095			       -selectmode => 'single',
12096			       ($splitter
12097				? (-columns => $max_cols,
12098				   -exportselection => 1,
12099				  )
12100				: ()
12101			       ),
12102			      )->pack(-expand => 1, -fill => 'both');
12103	    $t->Advertise(Listbox => $lb->Subwidget("scrolled"));
12104
12105	    if ($splitter) {
12106		my @wraplength;
12107		if ($columnwidths) {
12108		    @wraplength = @$columnwidths;
12109		} else {
12110		    my $wraplength = $max_cols > 1 ? int($top->screenwidth/($max_cols)) : $top->screenwidth;
12111		    @wraplength = ($wraplength) x $max_cols;
12112		}
12113		my @text_style;
12114		require Tk::ItemStyle;
12115		for my $col (0 .. $max_cols-1) {
12116		    push @text_style, $lb->ItemStyle('text', -wraplength => $wraplength[$col] || 100);
12117		}
12118		my $inx = 0;
12119		# XXX no support for sort styles here XXX
12120		for my $ort (sort keys %orte) {
12121		    my(@cols) = $splitter->($ort, $orte{$ort});
12122		    $lb->add($inx,
12123			     -text => $cols[0],
12124			     -data => $ort,
12125			     -style => $text_style[0],
12126			    );
12127		    for my $col (1 .. $#cols) {
12128			next if $col > $max_cols; # XXX off by one?
12129			$lb->itemCreate($inx, $col,
12130					-text => $cols[$col],
12131					-style => $text_style[$col],
12132				       );
12133		    }
12134		    $inx++;
12135		}
12136		# XXX destroy text_styles?
12137	    } else {
12138		if ($sorted eq 'unsorted') {
12139		    $lb->insert('end',
12140				@orte);
12141		} elsif ($sorted eq 'natural') {
12142		    $lb->insert('end',
12143				Sort::Naturally::nsort(keys %orte));
12144		} else {
12145		    # XXX use Sort::Naturally if $sorted eq 'natural'
12146		    # "use locale" is not used here because:
12147		    # - there's maybe no locale support at all
12148		    # - the german locale may be missing
12149		    # - with various perl versions and OSes I had in the
12150		    #   past problems with "use locale"
12151		    my $tf_sub = \&BBBikeUtil::umlauts_for_german_locale;
12152		    $lb->insert('end',
12153				map { $_->[1] }
12154				sort { $a->[0] cmp $b->[0] }
12155				map { [ do { /^\(?(.*)/; $tf_sub->($1) }, $_] }
12156				keys %orte);
12157		}
12158	    }
12159
12160	    eval {
12161		if ($lb->can("Cache")) {
12162		    $lb->Cache(1);
12163		}
12164	    };
12165
12166	    my $show_sub =  sub {
12167		my %args = @_;
12168		my $lb_index = ($splitter
12169				? $lb->info('anchor')
12170				: $lb->index('active')
12171			       );
12172		return if !defined $lb_index;
12173		my $index;
12174		if ($sorted eq 'unsorted') {
12175		    $index = $lb_index;
12176		} else {
12177		    my $ort = ($splitter
12178			       ? $lb->info("data", $lb_index)
12179			       : $lb->get($lb_index)
12180			      );
12181		    $index = $orte{$ort};
12182		}
12183		my $tcoords = [];
12184		$args{'-type'} = $type;
12185		if ($type eq 'o' || $type eq 'p') { # XXX is 'p' OK here?
12186		    my $p = $object->get($index)->[Strassen::COORDS]->[0];
12187		    $p = $conv->($p) if $conv;
12188		    $tcoords->[0][0] = [ transpose(split /,/, $p) ];
12189		} else {
12190		    my @i = split(/,/, $index);
12191		    my $i;
12192		    foreach $i (@i) {
12193			my $r = $object->get($i);
12194			my @c = @{ $r->[Strassen::COORDS] };
12195			if ($conv) {
12196			    @c = map { $conv->($_) } @c;
12197			}
12198			push @{$tcoords}, [ transpose_all(@{ Strassen::to_koord(\@c) }) ];
12199		    }
12200		    if ($linetype =~ /^p/) {
12201			$args{'-width'} = 20;
12202			$args{'-type'} = "$type-bg";
12203		    } else {
12204			# Is it an area or rectangle?
12205			if (@{$tcoords->[0]} >= 2 &&
12206			    "$tcoords->[0][0][0],$tcoords->[0][0][1]" eq
12207			    "$tcoords->[0][-1][0],$tcoords->[0][-1][1]") {
12208			    # Use first point (usually upper left?)
12209			    $args{'-scrollto'} = $tcoords->[0][0];
12210			} else {
12211			    # Use middle point of first segment:
12212			    $args{'-scrollto'} = $tcoords->[0][$#{$tcoords->[0]}/2];
12213			}
12214		    }
12215		}
12216		$action->(-coords        => $tcoords,
12217			  '-index'       => $index,
12218			  -showbutton    => $showb,
12219			  -cancelbutton  => $closeb,
12220			  -clever_center => 1,
12221			  %args,
12222			 );
12223	    };
12224
12225	    if ($args{'-markstart'}) {
12226		my $markstart_sub = sub {
12227		    my($type) = @_;
12228		    my $lb_index = $lb->index('active');
12229		    return if !defined $lb_index;
12230		    my $index = $orte{$lb->get($lb_index)};
12231		    my @i = split(/,/, $index);
12232		    my $r = $object->get($i[0]);
12233		    my $coords = $r->[Strassen::COORDS];
12234		    my $coord = $coords->[$#$coords/2]; # choose middle one
12235		    if ($type eq 'start') {
12236			set_route_start($coord);
12237		    } else {
12238			set_route_ziel($coord, -caller => "chooseort");
12239		    }
12240		    if ($type eq 'start' || $zoom_new_route_chooseort == 0) {
12241			$show_sub->();
12242		    }
12243		};
12244		$markf->Label(-text => M('Markieren als').' ...',
12245			      -font => $font{'small'},
12246			     )->pack(-side => 'left');
12247		$markf->Button(-text => M"Start",
12248			       -command => sub { $markstart_sub->('start') },
12249			      )->pack(-side => 'left');
12250		$markf->Button(-text => M"Ziel",
12251			       -command => sub { $markstart_sub->('ziel') },
12252			      )->pack(-side => 'left');
12253	    }
12254
12255	    my @bfb;
12256	    $showb  = $f->Button(Name => 'show',
12257				 -command => sub { $show_sub->() },
12258				);
12259	    push @bfb, $showb;
12260	    $showb->bind("<2>" => sub { $show_sub->(-zoom_view => 1) });
12261	    $showb->bind("<3>" => sub { $show_sub->(-dont_center => 1) });
12262	    $closeb = $f->Button(Name => 'close',
12263				 -command => sub {
12264				     if ($ondestroy) {
12265					 $ondestroy->($t);
12266				     } else {
12267					 if ($t->can("withdraw")) {
12268					     $t->withdraw;
12269					 } else {
12270					     $t->destroy;
12271					 }
12272				     }
12273				 },
12274				);
12275	    push @bfb, $closeb;
12276	    pack_buttonframe($f, \@bfb);
12277
12278	    $t->bind('<<CloseWin>>' => sub { $closeb->invoke });
12279	    for (qw(Return Double-1 2)) {
12280		$lb->bind("<$_>", sub { $showb->invoke });
12281	    }
12282	    my $find_and_select_nearest = sub {
12283		my($w, $y) = @_;
12284		my $inx = $w->nearest($y);
12285		$w->selectionClear(0, "end");
12286		$w->selectionSet($inx);
12287		$w->activate($inx);
12288	    };
12289	    $lb->bind("<2>" =>
12290		      [sub {
12291			   $find_and_select_nearest->(@_);
12292			   $show_sub->(-zoom_view => 1);
12293		       }, Ev('y')]);
12294	    $lb->bind("<3>" =>
12295		      [sub {
12296			   $find_and_select_nearest->(@_);
12297			   $show_sub->(-dont_center => 1);
12298		       }, Ev('y')]);
12299	    $lb->focus;
12300	};
12301	warn __LINE__ . ": $@" if $@;
12302	DecBusy($top);
12303
12304	$choose_ort_cache{"$linetype-$type"} =
12305	    get_cache_identifier($linetype, $type);
12306	if ($t->isa("Tk::Wm") && $do_popup) {
12307	    if (@popup_style == 0) {
12308		if (eval {require Tk::Placement; 1; }) {
12309		    # XXX use placer also for other toplevels --- replace
12310		    # all Popup(@popup_style) calls?
12311		    Tk::Placement::placer($t, -screen => $c,
12312					  -addx => 20, -addy => 25, # XXX for fvwm
12313					 );
12314		} else {
12315		    $t->Popup(-overanchor => "nw", -popanchor => "nw", -popover => $c);
12316		}
12317	    } else {
12318		my_popup($t);
12319	    }
12320	}
12321    } else {
12322	my $t = $toplevel{"chooseort-$type-$linetype"};
12323	$t->deiconify;
12324	# win32 ben�tigt zus�tzliches raise
12325	$t->raise;
12326	$lb = $t->Subwidget("Listbox");
12327    }
12328
12329    if (defined $see) {
12330	if ($splitter) {
12331	TRY: {
12332		for my $inx ($lb->info('children')) {
12333		    if ($lb->itemCget($inx, 0, '-text') eq $see) {
12334			$lb->see($inx);
12335			$lb->anchorSet($inx);
12336			last TRY;
12337		    }
12338		}
12339		# XXX inconsistency: in splitter/HList mode, do only
12340		#     exact match, no substring match
12341		warn "Cannot find <$see> in listbox content";
12342	    }
12343	} else {
12344	    my $found_index;
12345	TRY: {
12346		# first: exact match
12347		for my $inx (0 .. $lb->index("end")-1) {
12348		    if ($lb->get($inx) eq $see) {
12349			$found_index = $inx;
12350			last TRY;
12351		    }
12352		}
12353		# then: substring match
12354		for my $inx (0 .. $lb->index("end")-1) {
12355		    if (index($lb->get($inx), $see) >= 0) {
12356			$found_index = $inx;
12357			last TRY;
12358		    }
12359		}
12360		warn "Cannot find <$see> in listbox content";
12361	    }
12362	    if (defined $found_index) {
12363		$lb->see($found_index);
12364		$lb->selectionSet($found_index);
12365	    }
12366	}
12367    }
12368
12369}
12370
12371# Spezialisierung von choose_ort f�r Stadtstra�en
12372### AutoLoad Sub
12373sub choose_streets {
12374    choose_ort(qw(s s),
12375	       -markstartifactive => 1,
12376	       (!$city_obj->is_osm_source
12377		? (-completelistbutton => sub { choose_from_plz(-interactive => 1) },
12378		   -completelistbuttonlabel => "Alle Stra�en"
12379		  )
12380		: ()
12381	       )
12382	      );
12383}
12384
12385# Markiert einen Punkt und/oder zentriert darauf Als Argumente werden
12386# Canvas-Koordinaten erwartet (Ergebnis von transpose), entweder als
12387# -x/-y, als -point oder als -coords-Argument (komplizierter, siehe
12388# Source)
12389# Weitere Optionen:
12390#   -dont_mark: nur zentrieren, aber nicht markieren
12391#   -dont_center: nur markieren, aber nicht zentrieren
12392#   -clever_center: m�glichst so zentrieren, dass die Markierung nicht durch
12393#                   andere Fenster verdeckt wird
12394#   -dont_delete_old: alte Markierungen beibehalten
12395#   -endlessmark: ?
12396#   -addtag => $tag   : add another tag to the canvas item; this is used as the point name
12397#   -addtag => \@tags : add more than one tag; by convention the first additional tag is used as the point name
12398#   -inactive: Markierung reagiert nicht auf Events (insbesondere Tooltips)
12399### AutoLoad Sub
12400sub mark_point {
12401    my(%args) = @_;
12402    my($tx, $ty);
12403    if (exists $args{'-x'} && exists $args{'-y'}) {
12404	($tx, $ty) = ($args{'-x'}, $args{'-y'});
12405    } elsif (exists $args{'-point'}) {
12406	($tx, $ty) = split /,/, $args{'-point'};
12407    } else {
12408	($tx, $ty) = ($args{'-coords'}->[0][0][0], $args{'-coords'}->[0][0][1]);
12409    }
12410    my $width = $args{'-width'} || 9;
12411    my $do_also_overview_canvas = Tk::Exists($overview_canvas);
12412    if (!$args{'-dont_mark'} && !$args{'-dont_delete_old'}) {
12413	$c->delete('show');
12414	if ($do_also_overview_canvas) {
12415	    $overview_canvas->delete('show');
12416	}
12417    }
12418    my @show_mark_args;
12419    if ($args{-endlessmark}) {
12420	push @show_mark_args, -endlessmark => 1;
12421    }
12422    unless ($args{'-dont_mark'}) {
12423	my(@tags) = ('show');
12424	if (exists $args{'-addtag'}) {
12425	    if (ref $args{'-addtag'} eq 'ARRAY') {
12426		push @tags, @{$args{'-addtag'}};
12427	    } else {
12428		push @tags, $args{'-addtag'};
12429	    }
12430	}
12431	my @common_args = (-capstyle => $capstyle_round,
12432			   ($args{-inactive} ? (-state => "disabled") : ()),
12433			   -tags => \@tags,
12434			  );
12435	$c->createLine($tx, $ty, $tx, $ty,
12436		       -width => $width,
12437		       -fill => $mark_color,
12438		       @common_args,
12439		      );
12440	if ($do_also_overview_canvas) {
12441	    my($otx,$oty) = _convert_transposed_to_overview_coord($tx, $ty);
12442	    $overview_canvas->createLine($otx,$oty,$otx,$oty,
12443					 -width => 2,
12444					 -fill => $mark_color_overview,
12445					 @common_args,
12446					);
12447	}
12448	show_mark(undef, @show_mark_args);
12449    }
12450    if (!$args{'-dont_center'}) {
12451	if ($args{'-clever_center'} && clever_center($tx, $ty)) {
12452	    # NOP
12453	} else {
12454	    $c->center_view($tx, $ty);
12455	}
12456    }
12457    unless ($args{'-dont_mark'}) {
12458	eval { local $SIG{__DIE__}; $c->lower('show', $args{'-type'}) };
12459    }
12460}
12461
12462sub clever_center {
12463    my($tx,$ty,$tx2,$ty2) = @_;
12464    # For now, $tx2 and $ty2 are not used, but should be used to move
12465    # the region towards this point. See Tk::CanvasUtil::center_view2.
12466    return 0 if (!eval { require Tk::Placement; 1 });
12467    # Is ($tx/$ty) already visible? Then do nothing
12468    my($rx, $ry) = ($c->rootx+$c->widgetx($tx), $c->rooty+$c->widgety($ty));
12469    my $curr_w = $top->containing($rx, $ry);
12470    { local $^W = 0; return 1 if $curr_w eq $c; }
12471    my @win = Tk::Placement::get_toplevel_regions($top);
12472    if (!@win) { # no clever placement needed --- fallback to normal center
12473	return 0;
12474    }
12475    for (@win) {
12476	# adjust to canvas frame
12477	$_->{"x"} -= $c->rootx;
12478	$_->{"y"} -= $c->rooty;
12479    }
12480    my $box_w = $top->width/3;
12481    my $box_h = $top->height/3;
12482    my $dim = {width=>$box_w,height=>$box_h};
12483    my $scr = {x=>0,y=>0,width=>$c->width,height=>$c->height};
12484    my($px,$py) = Tk::Placement::Clever::placement
12485	($dim, $scr, \@win, 0, 0, 0);
12486    $px += $box_w/2; # move to center of box
12487    $py += $box_h/2;
12488    $c->scroll_canvasxy_to_rootxy($tx,$ty,
12489				  $c->rootx+$px,$c->rooty+$py);
12490    1;
12491}
12492
12493# Markiert und/oder zentriert auf die Linie
12494# Coordinates must be map coords, not BBBike standard coords
12495#   (that is, use transpose())
12496# Important arguments:
12497#   -coords => [[[x,y],[x2,y2]], # first line
12498#               [[x3,y3],[x4,y4]], # second line
12499#              ]
12500#   -labels => ["first line", "second line" ...]
12501#   -scrollto => [x,y]
12502#   -dont_mark => 1: don't mark
12503#   -dont_center => 1: don't center
12504### AutoLoad Sub
12505sub mark_street {
12506    my(%args) = @_;
12507    my $do_also_overview_canvas = Tk::Exists($overview_canvas);
12508    unless ($args{'-dont_delete_old'}) {
12509	$c->delete('show');
12510	if ($do_also_overview_canvas) {
12511	    $overview_canvas->delete('show');
12512	}
12513    }
12514    my @res_coords;
12515    # adapt width of mark
12516    my $line_width = $args{'-linewidth'} || get_line_width("s-H")+6; # outline takes 2 pixels...
12517    my $point_width = $args{'-pointwidth'} || $line_width+6;
12518    my @labels = $args{'-labels'} ? @{ $args{'-labels'} } : ();
12519    my($minx, $miny, $maxx, $maxy);
12520    my @all_coords = ();
12521    foreach (@{$args{'-coords'}}) {
12522	my @coords = @$_;
12523	@res_coords = ();
12524	foreach (@coords) {
12525	    if (ref $_ eq 'ARRAY') {
12526	        if (!defined $minx || $_->[0] < $minx) { $minx = $_->[0] }
12527	        if (!defined $maxx || $_->[0] > $maxx) { $maxx = $_->[0] }
12528	        if (!defined $miny || $_->[1] < $miny) { $miny = $_->[1] }
12529	        if (!defined $maxy || $_->[1] > $maxy) { $maxy = $_->[1] }
12530	    }
12531	    push @res_coords, (ref $_ eq 'ARRAY'
12532			       ? ($_->[0], $_->[1])
12533			       : $_);
12534	}
12535	push @all_coords, @res_coords;
12536	unless ($args{'-dont_mark'}) {
12537	    my $label = shift @labels;
12538	    my @common_args = (-tags => ['show', (defined $label ? $label : ())],
12539			       ($args{-inactive} ? (-state => "disabled") : ()),
12540			      );
12541	    if ($args{'-polygon'}) {
12542		if (@res_coords == 2) {
12543		    push @res_coords, (@res_coords) x 2;
12544		}
12545		$c->createPolygon(@res_coords,
12546				  -width => 5,
12547				  -fill => $mark_color,
12548				  @common_args,
12549				 );
12550		if ($do_also_overview_canvas) {
12551		    my @overview_coords;
12552		    for(my $i=0; $i<$#res_coords; $i+=2) {
12553			push @overview_coords, _convert_transposed_to_overview_coord(@res_coords[$i,$i+1]);
12554		    }
12555		    $c->createPolygon(@overview_coords,
12556				      -width => 1,
12557				      -fill => $mark_color_overview,
12558				      @common_args,
12559				     );
12560		}
12561	    } elsif (@res_coords) {
12562		my @add_args;
12563		if (@res_coords == 2) {
12564		    push @res_coords, @res_coords;
12565		    push @add_args, -capstyle => $capstyle_round,
12566			            -width => $point_width;
12567		} else {
12568		    push @add_args, -width => $line_width,
12569		}
12570		$c->createLine(@res_coords,
12571			       @add_args,
12572			       @common_args,
12573			      );
12574		if ($do_also_overview_canvas) {
12575		    my @overview_coords;
12576		    for(my $i=0; $i<$#res_coords; $i+=2) {
12577			push @overview_coords, _convert_transposed_to_overview_coord(@res_coords[$i,$i+1]);
12578		    }
12579		    if (@overview_coords == 2) {
12580			push @overview_coords, @overview_coords;
12581			push @add_args, -capstyle => $capstyle_round;
12582		    }
12583		    my %add_args = @add_args;
12584		    $add_args{-width} = 1; # overwrite
12585		    $overview_canvas->createLine(@overview_coords,
12586						 %add_args,
12587						 @common_args,
12588						);
12589		}
12590	    }
12591 	}
12592    }
12593    show_mark() unless $args{'-dont_mark'};
12594    if ($args{'-zoom_view'} && defined $minx) {
12595	zoom_view($minx, $miny, $maxx, $maxy);
12596    } else {
12597	my($vx,$vy);
12598	if ($args{'-scrollto'}) {
12599	    ($vx,$vy) = @{ $args{'-scrollto'} };
12600	} elsif (!$args{'-dont_scroll'}) {
12601	    # Prefer an already visible point to scroll to
12602	    ($vx,$vy) = find_visible_point(\@all_coords);
12603	    if (!defined $vx) {
12604		($vx,$vy) = @all_coords[0,1];
12605	    }
12606	}
12607	if (!$args{'-dont_center'}) {
12608	    if ($args{'-clever_center'} && clever_center($vx,$vy,@all_coords[$#all_coords-1,$#all_coords])) {
12609		# NOP
12610	    } else {
12611		$c->center_view2($vx,$vy,@all_coords[$#all_coords-1,$#all_coords]);
12612	    }
12613	}
12614    }
12615    unless ($args{'-dont_mark'}) {
12616	eval { local $SIG{__DIE__}; $c->lower('show', $args{'-type'}) };
12617    }
12618}
12619
12620sub delete_markers {
12621    $c->delete('show');
12622    if (Tk::Exists($overview_canvas)) {
12623	$overview_canvas->delete('show');
12624    }
12625    if ($showmark_after) {
12626	$showmark_after->cancel;
12627	undef $showmark_after;
12628    }
12629}
12630
12631sub find_visible_point {
12632    my($c_ref) = @_;
12633    my($x1,$y1,$x2,$y2) = $c->get_corners;
12634    for(my $i = 0; $i < $#$c_ref; $i+=2) {
12635	my($cx,$cy) = @{$c_ref}[$i,$i+1];
12636	if (point_in_grid($cx,$cy,$x1,$y1,$x2,$y2)) {
12637	    return($cx,$cy);
12638	}
12639    }
12640    ();
12641}
12642
12643# Dialog zum Auswahl eines Stra�e aus der Postleitzahl-Datenbank
12644### AutoLoad Sub
12645sub choose_from_plz {
12646    my(%args) = @_;
12647
12648    return if !defined $city || $city ne "Berlin";
12649
12650    my $batch = (defined $args{'-str'} || defined $args{'-coord'});
12651    if (!$batch) {
12652	if ($toplevel{"chooseplz"} && Tk::Exists($toplevel{"chooseplz"})) {
12653	    $toplevel{"chooseplz"}->deiconify;
12654	    $toplevel{"chooseplz"}->raise;
12655	    return;
12656	}
12657    }
12658
12659    my $plz = make_plz();
12660    if (!$plz) {
12661	$plzmcmd->configure(-state => 'disabled');
12662	status_message(M"Keine PLZ-Datenbank vorhanden!", 'err');
12663	return;
12664    }
12665
12666    my $show_sub = sub {
12667	my($street_obj, $dont_mark) = @_;
12668
12669	IncBusy($top);
12670	eval {
12671	    if (!defined $str_obj{'s'}) {
12672		$str_obj{'s'} = new Strassen $str_file{'s'};
12673	    }
12674	    my $s = $str_obj{'s'};
12675	    if (!defined $str_obj{'z'}) {
12676		$str_obj{'z'} = new Strassen $str_file{'z'};
12677	    }
12678	    my $z = $str_obj{'z'};
12679	    die "Str ($s)/PLZ ($z)-Objekt?" if !$s || !$z;
12680	    my($street, $bezirk, $plz_nr, $xy) = @$street_obj;
12681
12682	    if (defined $xy) {
12683		mark_point(-coords => [[[ transpose(split /,/, $xy) ]]],
12684			   -clever_center => $args{-interactive});
12685	    } else {
12686		my(@pos) = $s->choose_street($street, $bezirk);
12687		if (!@pos || !defined $pos[0]) {
12688
12689		    # PLZ-Gebiet markieren
12690		    $z->init;
12691		    while(1) {
12692			my $ret = $z->next;
12693			last if !@{$ret->[Strassen::COORDS]};
12694			if ($ret->[Strassen::NAME] eq $plz_nr) {
12695			    mark_street
12696				(-coords =>
12697				 [[ transpose_all(@{Strassen::to_koord($ret->[Strassen::COORDS])}) ]],
12698				 -type => 's',
12699				 -dont_mark => $dont_mark,
12700				 -polygon => 1,
12701				 );
12702			    return;
12703			}
12704		    }
12705
12706		    my $plz_re = $plz->make_plz_re($plz_nr);
12707		    my @streets = $plz->look($plz_re, Noquote => 1);
12708		    @pos = $s->union(\@streets, Nouniq => 1);
12709		    if (!@pos) {
12710			die Mfmt("Keine Stra�en im PLZ-Gebiet %s.\n", $plz_nr);
12711		    }
12712		}
12713
12714		# Stra�en im PLZ-Gebiet markieren
12715		my $i;
12716		for($i = 0; $i <= $#pos; $i++) {
12717		    my $o = $pos[$i];
12718		    mark_street
12719			(-coords =>
12720			 [[ transpose_all(@{Strassen::to_koord($s->get($o)->[Strassen::COORDS])}) ]],
12721			 -type => 's',
12722			 -dont_delete_old => ($i != 0),
12723			 -dont_center     => ($i != $#pos),
12724			 -dont_mark       => $dont_mark,
12725			 );
12726		}
12727		if (@pos > 1 && !$dont_mark) {
12728		    status_message(Mfmt("%s liegt im markierten Gebiet",
12729					$street), 'info');
12730		}
12731	    }
12732	};
12733	if ($@) {
12734	    status_message($@, 'err');
12735	}
12736	DecBusy($top);
12737    };
12738
12739
12740    my $str;
12741    if (defined $args{'-str'}) { # auf Stra�e zentrieren
12742	return if ($args{'-str'} eq "");
12743	$str = $args{'-str'};
12744	my($matchref) = $plz->look_loop($str, Agrep => 3, Max => 20);
12745	my(@match) = @$matchref;
12746	return if !@match;
12747	$show_sub->($match[0], 1) if !$args{-noshow};
12748	return $match[0]->[PLZ::LOOK_COORD()]; # return coords
12749    } elsif (defined $args{'-coord'}) { # auf Koordinaten zentrieren
12750	return if ($args{'-coord'} eq "");
12751	eval {
12752	    mark_point(-coords => [[[ transpose(split(/,/, $args{'-coord'})) ]]],
12753		       -dont_mark => 1);
12754	};
12755	warn $@ if $@;
12756    } else { # interaktiv
12757	my $t = $top->Toplevel(-title => M"Auswahl aus kompletter Stra�enliste",
12758			       -class => "Bbbike Extended Chooser");
12759	set_as_toolwindow($t);
12760	$toplevel{"chooseplz"} = $t;
12761
12762	my $bf   = $t->Frame->pack(-fill => 'x', -side => "bottom");
12763	my $strf = $t->Frame->pack(-fill => 'x', -side => "top");
12764
12765	$strf->Label(-text => M('Stra�e').':'
12766		    )->pack(-side => "left");
12767	my $Entry = 'Entry';
12768	my @extra_args;
12769	my $this_history_file;
12770	eval {
12771	    require Tk::HistEntry;
12772	    Tk::HistEntry->VERSION(0.37);
12773	    @extra_args = (-match => 1, -dup => 0, #-case => 0
12774			  );
12775	    $Entry = 'HistEntry';
12776	    $this_history_file = "$bbbike_configdir/bbbike_street_hist";
12777	};
12778	my $e = $strf->$Entry(-textvariable => \$str,
12779			      @extra_args,
12780			      -width => 30)->pack(-side => "left");
12781	$e->historyMergeFromFile($this_history_file)
12782	    if $e->can('historyMergeFromFile');
12783
12784	$e->focus;
12785	my $srchb =
12786	  $strf->Button(Name => 'search',
12787			-padx => 0,
12788			-pady => 0,
12789		       )->pack(-side => "left");
12790	my $showb;
12791	my $lb = $t->Scrolled('Listbox',
12792			      -scrollbars => 'osoe',
12793			     )->pack(-fill => "x");
12794	my @match;
12795	my $show_sub_lb = sub {
12796	    $show_sub->($match[$lb->index('active')], 0);
12797	};
12798
12799	for (qw(Double-1 2)) {
12800	    $lb->bind("<$_>" => sub {
12801			  $show_sub->($match
12802				      [$lb->nearest
12803				       ($lb->Subwidget('scrolled'
12804						      )->XEvent->y)], 0);
12805		      });
12806	}
12807	$t->OnDestroy(sub { delete $toplevel{"chooseplz"} });
12808	my $close_window = sub { $t->destroy; };
12809	my $search_window = sub {
12810	    if ($e->can('historyAdd') &&
12811		$e->can('historySave')) {
12812		$e->historyAdd;
12813		$e->historySave($this_history_file);
12814	    }
12815
12816	    IncBusy($t);
12817	    eval {
12818		my($matchref) = $plz->look_loop($str, Agrep => 3, Max => 20);
12819		@match = @$matchref;
12820		if (!@match) {
12821		    $showb->configure(-state => 'disabled');
12822		    die M"Keine Stra�en gefunden.\n";
12823		} else {
12824		    $lb->delete(0, 'end');
12825		    foreach (@match) {
12826			$lb->insert('end', join("/", @{$_}[0..2]));
12827		    }
12828		    $lb->selection('set', 0);
12829		    $showb->configure(-state => 'normal');
12830		    $lb->focus;
12831		}
12832	    };
12833	    if ($@) {
12834		status_message($@, 'err');
12835	    }
12836	    DecBusy($t);
12837	};
12838	$e->bind('<Return>' => $search_window);
12839	$srchb->configure(-command => $search_window);
12840	$t->bind('<<CloseWin>>' => $close_window);
12841	my @bfb;
12842	$showb = $bf->Button
12843	  (Name => 'show',
12844	   -state => 'disabled',
12845	   -command => $show_sub_lb);
12846	push @bfb, $showb;
12847	$lb->bind('<Return>' => $show_sub_lb);
12848	push @bfb, $bf->Button(Name => 'close',
12849		    	       -command => $close_window);
12850	pack_buttonframe($bf, \@bfb);
12851	#$t->Popup(@popup_style);
12852	my($x,$y) = ($c->rootx+10, $c->rooty+10);
12853	$t->geometry("+$x+$y");
12854
12855    }
12856}
12857
12858# Gibt die aktuelle Fontgr��e f�r die �bergebene Ortskategorie zur�ck.
12859### AutoLoad Sub
12860sub get_orte_label_font {
12861    my($category, $is_overview_canvas) = @_;
12862    my $base_index = 0;
12863    if ($is_overview_canvas) {
12864	$base_index = -2;
12865    } else {
12866	if ($scale >= 6) {
12867	    $base_index = 2;
12868	} elsif ($scale >= 3) {
12869	    $base_index = 1;
12870	} else {
12871	    $base_index = 0;
12872	}
12873    }
12874    my $fix_index = sub {
12875	my $index = shift;
12876	if ($index < 0) { $index = 0 }
12877	$index;
12878    };
12879    my $font;
12880    # This should handle the range MIN_ORT_CAT .. MAX_ORT_CAT:
12881    if      ($category == 0) {
12882	my $index = $fix_index->($base_index + $orte_label_size - 2);
12883	$font = $font{$font[$index] . "-italic"};
12884    } elsif ($category == 1) {
12885	my $index = $fix_index->($base_index + $orte_label_size - 1);
12886	$font = $font{$font[$index]};
12887    } elsif ($category <= 2) {
12888	my $index = $fix_index->($base_index + $orte_label_size);
12889	$font = $font{$font[$index]};
12890    } elsif ($category == 3) {
12891	my $index = $fix_index->($base_index + $orte_label_size + 1);
12892	$font = $font{$font[$index]};
12893    } elsif ($category == 4) {
12894	$font = $font{$font[$base_index + $orte_label_size+2]};
12895    } elsif ($category == 5) {
12896	$font = $font{$font[$base_index + $orte_label_size+3]};
12897    } elsif ($category > 5) {
12898	$font = $font{$font[$base_index + $orte_label_size+4]};
12899    } else {
12900	die "Unknown category $category";
12901    }
12902
12903    if (!defined $font) {
12904	$font = $font{'veryhuge'};
12905    }
12906
12907    $font;
12908}
12909
12910# Zeichnet Orte.
12911# XXX Modus zum Zeichnen von Bezirken
12912### AutoLoad Sub
12913sub plotorte {
12914    my(%args) = @_;
12915
12916    my $std;
12917    my $c = $c;
12918    my $transpose;
12919    my $municipality = $args{-municipality};
12920    my $type         = $args{-type} || 'o';
12921    my $label_tag    = uc($type);
12922    my $is_overview_canvas;
12923    if (exists $args{Canvas}) {
12924	$c = $args{Canvas};
12925	$std = 0;
12926	$transpose = ($show_overview_mode eq 'region'
12927		      ? \&transpose_small
12928		      : \&transpose_medium);
12929	$is_overview_canvas = 1;
12930    } else {
12931	$std = 1;
12932	$transpose = \&transpose;
12933    }
12934
12935    # evtl. alte Koordinaten l�schen
12936    if (!$args{FastUpdate}) {
12937	$c->delete($type);
12938	$c->delete($label_tag);
12939    }
12940
12941    delete $pending{"replot-p-$type"};
12942
12943    if ($std && !$p_draw{$type}) {
12944	undef $p_obj{$type};
12945	if ($main::lazy_p{$type}) {
12946	    bbbikelazy_remove_data("p", $type);
12947	}
12948	return;
12949    }
12950
12951    my $orte = _get_orte_obj($type);
12952
12953    my $lazy = defined $args{-lazy} ? $args{-lazy} : $lazy_plot;
12954    if ($std && $lazy) {
12955	status_message(Mfmt("Layer <%s> gezeichnet", exists $p_attrib{$type} ? $p_attrib{$type}->[ATTRIB_PLURAL] : $type), "info");
12956	return bbbikelazy_add_data("p", $type, $orte);
12957    }
12958
12959    my $coordsys = $coord_system_obj->coordsys;
12960
12961    destroy_delayed_restack();
12962    IncBusy($top);
12963    $progress->Init(-dependents => $c,
12964		    -label => 'orte');
12965    eval {
12966	my $place_category = (exists $args{PlaceCategory}
12967			      ? $args{PlaceCategory} : $place_category);
12968	my $name_o        = (exists $args{NameDraw}
12969			     ? $args{NameDraw}     : $p_name_draw{$type});
12970	my $no_overlap_label = (exists $args{NoOverlapLabel}
12971				? $args{NoOverlapLabel} : $no_overlap_label{$type});
12972	my $progress_hack = $name_o && $no_overlap_label;
12973
12974	my $complete_str = $orte;
12975	my $diffed_orte = 0;
12976	if (#XXX del? ($edit_mode || $edit_normal_mode) &&
12977	    $args{FastUpdate}) {
12978	    my($new_orte, $todelref) = $orte->diff_orig(-clonefile => 1);
12979	    if (!defined $new_orte) {
12980		warn "Not using diff output" if $verbose;
12981		$c->delete($type); # evtl. alte Koordinaten l�schen
12982		$c->delete($label_tag);
12983	    } else {
12984		warn "Using diff output" if $verbose;
12985		# XXX not used due to lack of tag $type-$i
12986		#foreach (@$todelref) {
12987		#    $c->delete("$type-$_");
12988		#}
12989		$orte = $new_orte;
12990		$diffed_orte = 1;
12991	    }
12992	}
12993
12994	my @orte_coords_labeling;
12995
12996	my $next_meth;
12997	my $i;
12998	my $i_inc;
12999	if ($no_overlap_label) {
13000	    $orte->init;
13001	    $next_meth = 'next';
13002	    $i = 0;
13003	    $i_inc = +1;
13004	} else {
13005	    # in diesem Fall sollten die gr��eren Orte _sp�ter_ d.h. �ber
13006	    # den kleineren gezeichnet werden
13007	    $orte->set_last;
13008	    $next_meth = 'prev';
13009	    $i = $orte->count; # XXX off by one???
13010	    $i_inc = -1;
13011	}
13012	my $anzahl_eindeutig = $orte->count;
13013	my $do_outline_text = $do_outline_text{$type};
13014
13015	my %conv_args;
13016	if ($args{-map}) {
13017	    $conv_args{Map} = $args{-map};
13018	}
13019	my $conv = $orte->get_conversion(%conv_args);
13020
13021	my $draw_sub = eval $plotorte_draw_sub;
13022	die $@ if $@;
13023
13024	my $prog_i = 0;
13025	while(1) {
13026	    my $ret = $orte->$next_meth();
13027	    last if !@{$ret->[Strassen::COORDS]};
13028	    $progress->Update($prog_i/$anzahl_eindeutig*($progress_hack ? 0.5 : 1))
13029	      if $prog_i % 80 == 0;
13030	    $prog_i++;
13031	    $i += $i_inc;
13032	    $draw_sub->($ret);
13033	}
13034
13035	if ($type eq 'o') {
13036	    for my $def ([0 => {-width => 3, -fill => '#0000c0'}],
13037			 [1 => {-width => 3}],
13038			 [2 => {-width => 4}],
13039			 [3 => {-width => 5}],
13040			 [4 => {-width => 6}],
13041			 [5 => {-width => 7}],
13042			 [6 => {-width => 7}],
13043			) {
13044		my($cat, $args) = @$def;
13045		my %args = (-capstyle => $capstyle_round,
13046			    -fill     => '#000080',
13047			    %$args,
13048			   );
13049		$c->itemconfigure("OP$cat", %args);
13050	    }
13051	} else {
13052	    $c->itemconfigure($type,
13053			      -capstyle => $capstyle_round,
13054			      -width => 5,
13055			      -fill => '#000080',
13056			     );
13057	}
13058
13059	if ($name_o) {
13060	    if ($no_overlap_label) {
13061		# nach Kategorie sortieren
13062		@orte_coords_labeling
13063		  = sort { $b->[3] <=> $a->[3] } @orte_coords_labeling;
13064		my $i = 0;
13065		foreach my $ort_def (@orte_coords_labeling) {
13066		    $progress->Update($i/$anzahl_eindeutig*.5+0.5)
13067		      if $i % 80 == 0;
13068		    $i++;
13069		    my($text, $tx, $ty, $cat, $point_item) = @$ort_def;
13070		    my $font = get_orte_label_font($cat, $is_overview_canvas);
13071		    my(@tags) = ($label_tag, "$label_tag$cat");
13072		    if (!draw_text_intelligent($c, $tx, $ty,
13073					       -text => $text,
13074					       -font => $font,
13075					       -tags => \@tags,
13076					       -abk  => $label_tag,
13077					      )) {
13078			if ($cat <= $place_category+1 || $no_overlap_label eq 'drop_non_fitting') {
13079			    $c->delete($point_item);
13080			} else {
13081			    my $anchor = 'w';
13082			    $c->createText
13083			      ($tx+$xadd_anchor_type->{'o'}{$anchor},
13084			       $ty+$yadd_anchor_type->{'o'}{$anchor},
13085			       -text => $text,
13086			       -font => $font,
13087			       -tags => \@tags,
13088			       -anchor => $anchor,
13089			       -justify => 'left',
13090			      );
13091			}
13092		    }
13093		}
13094	    }
13095	    if (!$no_overlap_label && !$municipality &&
13096		!$do_outline_text) {
13097		$c->itemconfigure($label_tag,
13098				  -anchor => 'w', -justify => 'left');
13099	    }
13100	    if ($municipality) {
13101		$c->itemconfigure($label_tag, -fill => '#7e7e7e');
13102	    } elsif (!$do_outline_text) {
13103		$c->itemconfigure($label_tag, -fill => '#000080');
13104	    }
13105	    if ($orientation eq 'landscape' &&
13106		!$do_outline_text) {
13107		foreach my $category (MIN_ORT_CAT .. MAX_ORT_CAT) {
13108		    $c->itemconfigure
13109			("$label_tag$category",
13110			 -font => get_orte_label_font($category, $is_overview_canvas));
13111		}
13112	    }
13113	}
13114
13115	if (!($edit_mode || $edit_normal_mode) && !$municipality) {
13116	    change_place_visibility($c);
13117	}
13118
13119	if (($edit_mode || $edit_normal_mode) and !$diffed_orte) {
13120	    warn "Try to copy original data" if $verbose;
13121	    my $r = $complete_str->copy_orig;
13122	    warn "Returned $r" if $verbose;
13123	}
13124
13125	if ($std) {
13126	    restack_delayed();
13127	}
13128    };
13129    if ($@) {
13130	status_message($@, 'err');
13131    }
13132    $progress->Finish;
13133    DecBusy($top);
13134}
13135
13136# Zeichnet Labels, wobei versucht wird, �berlappungen zu vermeiden.
13137# Auf $canvas wird gezeichnet, die Koordinaten sind $tx/$ty
13138### AutoLoad Sub
13139sub draw_text_intelligent {
13140    my($canvas, $tx, $ty, %args) = @_;
13141    my @ct_args;
13142    foreach my $arg (qw(-text -font -tags -fill -font)) {
13143	push @ct_args, $arg => $args{$arg} if exists $args{$arg};
13144    }
13145    # mit welchen Tags �berlappungen vermeiden
13146    my $abkrx = (ref $args{-abk} eq 'ARRAY'
13147		 ? '^(' . join('|', @{$args{-abk}}) . ")\$"
13148		 : "^$args{-abk}\$");
13149    # Anchor => X/Y-Versetzung
13150    my $xadd = (exists $args{-xadd} ? $args{-xadd} : $xadd_anchor_type->{'o'});
13151    my $yadd = (exists $args{-yadd} ? $args{-yadd} : $yadd_anchor_type->{'o'});
13152    my $check_tag_index = (exists $args{-checktagindex}
13153			   ? $args{-checktagindex}
13154			   : 0);
13155  LOOP:
13156    foreach my $anchor (qw(w e nw n sw s)) {
13157	my $item = $canvas->createText
13158	  ($tx+$xadd->{$anchor}, $ty+$yadd->{$anchor},
13159	   @ct_args,
13160	   -anchor => $anchor,
13161	   -justify => 'left',
13162	  );
13163	my(@bbox) = $canvas->bbox($item);
13164	if (@bbox) {
13165	    my(@overlap) = $canvas->find('overlapping', @bbox);
13166	    foreach my $i (@overlap) {
13167		next if $i == $item;
13168		my(@tags) = $canvas->gettags($i);
13169		next if !@tags;
13170		if ($check_tag_index eq 'all') {
13171		    foreach my $tag (@tags) {
13172			if ($tag =~ /$abkrx/) {
13173			    $canvas->delete($item);
13174			    next LOOP;
13175			}
13176		    }
13177		} else {
13178		    next if !defined $tags[$check_tag_index];
13179		    if ($tags[$check_tag_index] =~ /$abkrx/) {
13180			$canvas->delete($item);
13181			next LOOP;
13182		    }
13183		}
13184	    }
13185	}
13186	$ {$args{-returnanchor}} = $anchor
13187	    if ref $args{-returnanchor} eq 'SCALAR';
13188	if ($args{-outline}) {
13189	    $c->delete($item);
13190	    outline_text($c, $tx+$xadd->{$anchor}, $ty+$yadd->{$anchor},
13191			 @ct_args, -anchor => $anchor,
13192			 -outlinewidth => $args{-outlinewidth});
13193	}
13194	return 1;
13195    }
13196    0;
13197}
13198
13199# Zeichnen von Stellen mit Obstvorkommen
13200### AutoLoad Sub
13201sub plotobst {
13202    my(%args) = @_;
13203
13204    my $canvas = $c;
13205    my $transpose = \&transpose;
13206
13207    # evtl. alte Koordinaten l�schen
13208    $canvas->delete('obst');
13209
13210    delete $pending{'replot-p-obst'};
13211
13212    if (!$p_draw{'obst'}) {
13213	return;
13214    }
13215
13216    destroy_delayed_restack();
13217    IncBusy($top);
13218    $progress->Init(-dependents => $canvas,
13219		    -label => $p_file{'obst'});
13220    eval {
13221	my $i = 0;
13222 	my $obst = get_strassen_obj($p_file{'obst'});
13223	$obst->init;
13224	my $anzahl_eindeutig = $obst->count;
13225	while(1) {
13226	    my $ret = $obst->next;
13227	    last if !@{$ret->[Strassen::COORDS]};
13228	    $progress->Update($i/$anzahl_eindeutig) if $i % 80 == 0;
13229	    $i++;
13230	    my $type = lc($ret->[Strassen::NAME]);
13231	    next if !exists $obst_file{$type}; # XXX warning
13232	    if ($ret->[Strassen::COORDS][0] =~ /(-?\d+),(-?\d+)/) {
13233		my($x, $y) = ($1, $2);
13234		my($tx, $ty) = $transpose->($x, $y);
13235		if (!exists $obst_photo{$type}) {
13236		    $obst_photo{$type} = load_photo($top, $obst_file{$type});
13237		}
13238		next if (!defined $obst_photo{$type});
13239		my $img = $obst_photo{$type};
13240		$canvas->createImage($tx, $ty,
13241				     -image => $img,
13242				     -tags => 'obst');
13243	    }
13244	}
13245
13246	restack_delayed();
13247    };
13248    if ($@) {
13249	status_message($@, 'err');
13250    }
13251    $progress->Finish;
13252    DecBusy($top);
13253}
13254
13255### AutoLoad Sub
13256sub draw_bridge {
13257    my($cl,%args) = @_;
13258    my $width = $args{'-width'}||10;
13259    my $color = '#808080';
13260    my $thickness = 2; # make configurable XXX
13261#XXX complicated code, make nicer!
13262#XXX an den Enden etwas verk�rzen
13263    for(my $i = 0; $i < $#$cl/2-1; $i++) {
13264	my($x1,$y1,$x2,$y2) = @{$cl}[$i*2..$i*2+3];
13265	my $alpha = atan2($y2-$y1,$x2-$x1);
13266	my $beta = $alpha - pi()/2;
13267	my $delta = $width/2;
13268	my($dx,$dy) = ($delta*cos($beta), $delta*sin($beta));
13269	$c->createLine($x1+$dx,$y1+$dy,$x2+$dx,$y2+$dy,
13270		       -width => $thickness,
13271		       -tags => $args{'-tags'},
13272		       -fill => $color,
13273		      );
13274	$c->createLine($x1-$dx,$y1-$dy,$x2-$dx,$y2-$dy,
13275		       -width => $thickness,
13276		       -tags => $args{'-tags'},
13277		       -fill => $color,
13278		      );
13279    }
13280    {
13281	my $alpha = atan2($cl->[3]-$cl->[1],$cl->[2]-$cl->[0]);
13282	my $beta  = $alpha - pi()/2;
13283	my $knick = $alpha - pi()/4;
13284	my $knick2 = $alpha + pi()/4;
13285	my $delta = $width/2;
13286	my $knick_length = $width/2;
13287	my($dx, $dy) = ($delta*cos($beta), $delta*sin($beta));
13288	my($kx, $ky) = ($knick_length*cos($knick), $knick_length*sin($knick));
13289	my($k2x, $k2y) = ($knick_length*cos($knick2), $knick_length*sin($knick2));
13290	$c->createLine($cl->[0]+$dx-$k2x, $cl->[1]+$dy-$k2y,
13291		       $cl->[0]+$dx, $cl->[1]+$dy,
13292		       -width => $thickness,
13293		       -tags => $args{'-tags'},
13294		       -fill => $color,
13295		      );
13296	$c->createLine(
13297		       $cl->[0]-$dx, $cl->[1]-$dy,
13298		       $cl->[0]-$dx-$kx, $cl->[1]-$dy-$ky,
13299		       -width => $thickness,
13300		       -tags => $args{'-tags'},
13301		       -fill => $color,
13302		      );
13303    }
13304
13305    {
13306	my $alpha = atan2($cl->[-1]-$cl->[-3],$cl->[-2]-$cl->[-4]);
13307	my $beta  = $alpha - pi()/2;
13308	my $knick = $alpha - pi()/4;
13309	my $knick2 = $alpha + pi()/4;
13310	my $delta = $width/2;
13311	my $knick_length = $width/2;
13312	my($dx, $dy) = ($delta*cos($beta), $delta*sin($beta));
13313	my($kx, $ky) = ($knick_length*cos($knick), $knick_length*sin($knick));
13314	my($k2x, $k2y) = ($knick_length*cos($knick2), $knick_length*sin($knick2));
13315	$c->createLine($cl->[-2]+$dx+$kx, $cl->[-1]+$dy+$ky,
13316		       $cl->[-2]+$dx, $cl->[-1]+$dy,
13317		       -width => $thickness,
13318		       -tags => $args{'-tags'},
13319		       -fill => $color,
13320		      );
13321	$c->createLine(
13322		       $cl->[-2]-$dx, $cl->[-1]-$dy,
13323		       $cl->[-2]-$dx+$k2x, $cl->[-1]-$dy+$k2y,
13324		       -width => $thickness,
13325		       -tags => $args{'-tags'},
13326		       -fill => $color,
13327		      );
13328    }
13329
13330}
13331
13332### AutoLoad Sub
13333sub draw_tunnel_entrance {
13334    my($cl,%args) = @_;
13335    my $width = $args{'-width'}||20;
13336    my $color = '#505050';
13337    my $thickness = 3;
13338    my $mounds = delete $args{'-mounds'} || "Tu";
13339#XXX complicated code, make nicer!
13340    if ($mounds !~ m{^_}) {
13341	my $alpha = atan2($cl->[3]-$cl->[1],$cl->[2]-$cl->[0]);
13342	my $beta  = $alpha - pi()/2;
13343	my $knick = $alpha - pi()/4;
13344	my $knick2 = $alpha + pi()/4;
13345	my $delta = $width/2;
13346	my $knick_length = $width/3;
13347	my($dx, $dy) = ($delta*cos($beta), $delta*sin($beta));
13348	my($kx, $ky) = ($knick_length*cos($knick), $knick_length*sin($knick));
13349	my($k2x, $k2y) = ($knick_length*cos($knick2), $knick_length*sin($knick2));
13350	$c->createLine($cl->[0]+$dx-$k2x, $cl->[1]+$dy-$k2y,
13351		       $cl->[0]+$dx, $cl->[1]+$dy,
13352		       $cl->[0]-$dx, $cl->[1]-$dy,
13353		       $cl->[0]-$dx-$kx, $cl->[1]-$dy-$ky,
13354		       -width => $thickness,
13355		       -tags => $args{'-tags'},
13356		       -fill => $color,
13357		      );
13358    }
13359    if ($mounds !~ m{_$}) {
13360	my $alpha = atan2($cl->[-1]-$cl->[-3],$cl->[-2]-$cl->[-4]);
13361	my $beta  = $alpha - pi()/2;
13362	my $knick = $alpha - pi()/4;
13363	my $knick2 = $alpha + pi()/4;
13364	my $delta = $width/2;
13365	my $knick_length = $width/3;
13366	my($dx, $dy) = ($delta*cos($beta), $delta*sin($beta));
13367	my($kx, $ky) = ($knick_length*cos($knick), $knick_length*sin($knick));
13368	my($k2x, $k2y) = ($knick_length*cos($knick2), $knick_length*sin($knick2));
13369	$c->createLine($cl->[-2]+$dx+$kx, $cl->[-1]+$dy+$ky,
13370		       $cl->[-2]+$dx, $cl->[-1]+$dy,
13371		       $cl->[-2]-$dx, $cl->[-1]-$dy,
13372		       $cl->[-2]-$dx+$k2x, $cl->[-1]-$dy+$k2y,
13373		       -width => $thickness,
13374		       -tags => $args{'-tags'},
13375		       -fill => $color,
13376		      );
13377    }
13378}
13379
13380# L�scht alle derzeitig gezeichneten Stra�en und Punkte und liefert
13381# eine Subroutine zur�ck, mit der die gel�schten Objekte wieder
13382# gezeichnet werden k�nnen.
13383### AutoLoad Sub
13384sub get_plotted {
13385    my(@plotted_p, @plotted_str);
13386    while(my($k,$v) = each %str_draw) {
13387	push @plotted_str, $k if ($v);
13388    }
13389    while(my($k,$v) = each %p_draw) {
13390	push @plotted_p, $k if ($v);
13391    }
13392    sub {
13393	$progress->InitGroup;
13394	foreach (@plotted_p) {
13395	    plot('p',$_);
13396	}
13397	foreach (@plotted_str) {
13398	    plot('str',$_);
13399	}
13400	$progress->FinishGroup;
13401    }
13402}
13403
13404# Setzt den Canvas in den Landscape-Modus (Default).
13405sub set_landscape {
13406    local($^W) = 0; # wegen sub-Redefinition
13407    $orientation = 'landscape';
13408    *transpose = \&transpose_ls;
13409    *anti_transpose   = \&anti_transpose_ls;
13410    *transpose_small  = \&transpose_ls_small;
13411    *transpose_medium = \&transpose_ls_medium;
13412    *anti_transpose_small  = \&anti_transpose_ls_small;
13413    *anti_transpose_medium = \&anti_transpose_ls_medium;
13414    delete_overview();
13415}
13416
13417# Setzt den Canvas in den Portraint-Modus.
13418### AutoLoad Sub
13419sub set_portrait {
13420    local($^W) = 0; # wegen sub-Redefinition
13421    $orientation = 'portrait';
13422    *transpose = \&transpose_pt;
13423    *anti_transpose   = \&anti_transpose_pt;
13424    *transpose_small  = \&transpose_pt_small;
13425    *transpose_medium = \&transpose_pt_medium;
13426    *anti_transpose_small  = \&anti_transpose_pt_small;
13427    *anti_transpose_medium = \&anti_transpose_pt_medium;
13428    delete_overview();
13429}
13430
13431# �ndert das aktuelle Koordinatensystem.
13432# XXX verbessern...
13433### AutoLoad Sub
13434sub set_coord_system {
13435    my($o) = @_;
13436    if (!defined $o) {
13437	$o = $Karte::map{'standard'};
13438    }
13439    my $old_coord_system = $coord_system_obj ? $coord_system_obj->token : "";
13440    if ($old_coord_system eq $o->token) {
13441	# No change
13442	return;
13443    }
13444    if ($o->token eq 'standard') {
13445	set_landscape(); # XXX set scrollregion
13446	$coord_system = 'standard';
13447	$scale_coeff = 1;
13448	set_canvas_scale(DEFAULT_SCALE);
13449    } else {
13450	{
13451	    local($^W) = 0;
13452	    *transpose             = sub { ($_[0]*$scale, $_[1]*$scale) };
13453	    *anti_transpose        = sub { ($_[0]/$scale, $_[1]/$scale) };
13454	    *transpose_small       = sub { ($_[0]*$small_scale_edit, $_[1]*$small_scale_edit) };
13455	    *anti_transpose_small  = sub { ($_[0]/$small_scale_edit, $_[1]/$small_scale_edit) };
13456	    *transpose_medium      = sub { ($_[0]*$medium_scale_edit, $_[1]*$medium_scale_edit) };
13457	    *anti_transpose_medium = sub { ($_[0]/$medium_scale_edit, $_[1]/$medium_scale_edit) };
13458	}
13459	$scale_coeff = $o->scale_coeff;
13460	set_canvas_scale(1);
13461    }
13462    @scrollregion = $o->scrollregion;
13463    if ($o->token eq 'standard') { # XXX hack
13464	foreach (@scrollregion) {
13465	    $_ *= DEFAULT_SCALE;
13466	}
13467    }
13468    scalecanvas($c, 1);
13469    $coord_system_obj = $o;
13470    undef %hoehe;
13471}
13472
13473# Setzt die GUI f�r den Edit-Mode
13474sub gui_set_edit_mode {
13475    my($onoff) = @_;
13476    if ($onoff) {
13477	$edit_mode_indicator->configure(-fg => 'black'); # XXX don't hardcode
13478	$edit_mode_type->configure(-text => uc($onoff));
13479	if ($onoff eq 'std-no-orig') {
13480	    undef $edit_mode;
13481	    $edit_normal_mode = 1;
13482	} else {
13483	    $edit_mode = $onoff;
13484	}
13485	$edit_mode_flag = 1;
13486    } else {
13487	$edit_mode_indicator->configure(-fg => $dim_color);
13488	$edit_mode_type->configure(-text => '');
13489	undef $edit_mode;
13490	undef $edit_normal_mode;
13491	$edit_mode_flag = 0;
13492    }
13493}
13494
13495sub gui_start_bbbike_server {
13496    require BBBikeServer;
13497    if (!BBBikeServer::running()) {
13498	BBBikeServer::create_server($top);
13499	status_message("Der BBBike-Server kann jetzt mit dem Programm <bbbikeclient> angesprochen werden", "info");
13500    } else {
13501	status_message("Der BBBike-Server l�uft bereits.", "infodlg");
13502    }
13503}
13504
13505# Zeigt Namen der aktuellen Haltestelle oder des aktuellen Ortes
13506# (unterhalb des Cursors).
13507sub enterpoint {
13508    my $c = shift;
13509    my(@tags) = $c->gettags('current');
13510    if ($tags[0] eq 'p') {
13511	$act_value{Haltestelle} = $names[$tags[1]];
13512	$hs_label->configure(-fg => 'black');
13513    } elsif ($tags[0] eq 'o' || $tags[0] =~ /^[ubr](?:-|_bg)/) {
13514	my $prefix = '';
13515	my $name = $tags[2];
13516	if      ($tags[0] =~ /^u(?:-|_bg)/) {
13517	    $prefix = 'U ';
13518	} elsif ($tags[0] =~ /^b(?:-|_bg)/) {
13519	    $prefix = 'S ';
13520	} elsif ($tags[0] =~ /^r(?:-|_bg)/) {
13521	    $prefix = 'Bhf. '; # XXX language?
13522	}
13523	$act_value{Haltestelle} = $prefix . $name;
13524	$hs_label->configure(-fg => 'black');
13525    } elsif ($tags[0] eq 'pp' || $tags[0] =~ /^(L\d+|kn|ki|rest)/) {
13526	if (defined $tags[2] && $tags[2] ne 'current') {
13527	    $act_value{Haltestelle} = $tags[2];
13528	} else {
13529	    $act_value{Haltestelle} = '';
13530	}
13531	if (exists $hoehe{$tags[1]}) {
13532	    $act_value{Haltestelle} .= " ($hoehe{$tags[1]}m)";
13533	}
13534	$hs_label->configure(-fg => 'black');
13535    } elsif ($tags[0] =~ /sperre/) {
13536	if ($tags[1] eq 'sperre0') {
13537	    $act_value{Haltestelle} = $tags[2] || M"tragen notwendig";
13538	} elsif ($tags[1] =~ /^sperre1/) {
13539	    $act_value{Haltestelle} = M("Einbahnstra�e") .
13540		(defined $tags[2] and $tags[2] ne "" ? " - " . $tags[2] : "");
13541	} elsif ($tags[1] eq 'sperre2') {
13542	    if (defined $tags[2] and $tags[2] ne "") {
13543		$act_value{Haltestelle} = $tags[2];
13544	    } else {
13545		$act_value{Haltestelle} = M("gesperrte Stra�e");
13546	    }
13547	} else {
13548	    $act_value{Haltestelle} = $tags[2] || '';
13549	}
13550	$hs_label->configure(-fg => 'black');
13551    } elsif ($tags[0] =~ /^lsa-/) {
13552	my $exact_cat = $tags[3];
13553	if ($exact_cat !~ /^lsa-X/) {
13554	    $act_value{Haltestelle} = ($exact_cat =~ /^lsa-F/
13555				       ? M"Fu�g�ngerampel"
13556				       : ($exact_cat =~ /^lsa-B/
13557					  ? M"Bahn�bergang"
13558					  : ($exact_cat =~ /^lsa-Zbr/
13559					     ? M"Zugbr�cke (" . $tags[2] . ")"
13560					     : substr($exact_cat, 4, 1)
13561					    )
13562					 )
13563				      );
13564	    $hs_label->configure(-fg => 'black');
13565	} else {
13566	    $act_value{Haltestelle} = "";
13567	}
13568    } elsif ($tags[0] =~ /^show/) {
13569	if (defined $tags[1] && $tags[1] ne 'current') {
13570	    $act_value{Haltestelle} = $tags[1];
13571	    $hs_label->configure(-fg => 'black');
13572	}
13573	if (defined $tags[2] && $tags[1] ne 'current' && $tags[2] ne 'current') {
13574	    $act_value{Strasse} = $tags[2];
13575	    $str_label->configure(-fg => 'black');
13576	} else {
13577	    $str_label->configure(-fg => $dim_color);
13578	}
13579    } elsif ($tags[0] =~ /^pl/) {
13580	$act_value{Haltestelle} = $tags[2];
13581	$hs_label->configure(-fg => 'black');
13582    }
13583
13584    my @l;
13585    my $str = show_below_str($c);
13586    if (defined $act_value{Haltestelle}
13587	     && $act_value{Haltestelle} ne '') {
13588	push @l, $act_value{Haltestelle};
13589    }
13590    if (defined $str && $str ne '') {
13591	push @l, $str;
13592    }
13593    if (defined $c_balloon) {
13594	if (@l && $use_c_balloon >= 2) {
13595	    if ($leave_after) {	$leave_after->cancel; undef $leave_after }
13596	    my $str = join(" / ", @l);
13597	    if (1) {
13598		my $add_str = balloon_info_from_all_tags($c);
13599		if ($add_str) {
13600		    $str .= "\n$add_str";
13601		}
13602	    }
13603	    $c_balloon->Popup($str);
13604	} else {
13605	    $c_balloon->Deactivate;
13606	}
13607    }
13608
13609}
13610
13611# Wird beim Verlassen eines Punktes aufgerufen.
13612sub leavepoint {
13613    $hs_label->configure(-fg => $dim_color);
13614    $c_balloon->Deactivate(undef, -from => "event") if defined $c_balloon;
13615    leavestr();
13616}
13617
13618# Zeigt aktuellen Stra�enzugnamen.
13619sub enterstr {
13620    my $c = shift;
13621
13622    my @balloon_info = balloon_info_from_all_tags($c);
13623    if (@balloon_info) {
13624	$act_value{Strasse} = $balloon_info[0];
13625	$str_label->configure(-fg => 'black');
13626	if (defined $c_balloon) {
13627	TRY_BALLOON:
13628	    {
13629		if ($use_c_balloon >= 2) {
13630		    if ($leave_after) {
13631			$leave_after->cancel;
13632			undef $leave_after;
13633		    }
13634		    my $str = balloon_info_from_all_tags($c);
13635		    if (defined $str) {
13636			$c_balloon->Popup($str);
13637			last TRY_BALLOON;
13638		    }
13639		}
13640		$c_balloon->Deactivate;
13641	    }
13642	}
13643    }
13644}
13645
13646# Wird beim Verlassen einer Strecke aufgerufen.
13647sub leavestr {
13648    $str_label->configure(-fg => $dim_color);
13649    $c_balloon->Deactivate(undef, -from => "event") if defined $c_balloon;
13650}
13651
13652# Zeigt den Strecken- und/oder Punktnamen unterhalb der Route.
13653sub enterroute {
13654    my($c, $item) = @_;
13655    return if !defined $c_balloon;
13656    $item = 'current' unless defined $item;
13657    my(@tags) = $c->gettags($item);
13658    my $routenr;
13659    if (defined $tags[2] && $tags[2] eq 'viaflag') {
13660	my($item2,@tags2) = find_below_rx($c, ['^route-'],[1]);
13661	if (defined $item2) {
13662	    ($item, @tags) = ($item2, @tags2);
13663	}
13664    }
13665    if (defined $tags[1] && $tags[1] =~ /^route-(.*)/) {
13666	$routenr = $1;
13667	if ($routenr eq "") { warn "@tags" } # XXXXX
13668    } else {
13669	if (!grep { $_ eq "viaflag" } @tags) {
13670	    warn "Unexpected: no route number in <@tags>";
13671	}
13672	return;
13673    }
13674    my @l;
13675    my $str = show_below_str($c);
13676    if (!defined $str) {
13677	# next try with bigger tolerance
13678	my $old_closeenough = $c->cget(-closeenough);
13679	$c->configure(-closeenough => 5);
13680	$str = show_below_str($c);
13681	# restore old tolerance value
13682	$c->configure(-closeenough => $old_closeenough);
13683    }
13684    push @l, Strassen::strip_bezirk($str)      if (defined $str);
13685    if (defined $routenr && $routenr >= 0) { # wenn mehr als nur der Startpunkt angew�hlt ist
13686	push @l, s2hm($route_time[$routenr]) . "h" if ($route_time[$routenr]);
13687	push @l, m2km($route_distance[$routenr])   if ($route_distance[$routenr]);
13688    }
13689    if (@l) {
13690	if ($leave_after) { $leave_after->cancel; undef $leave_after }
13691	my $b_str = join(" / ", @l);
13692	if (defined $str && 1) {
13693	    my $bi_str = balloon_info_from_all_tags($c);
13694	    $bi_str =~ s{\Q$str\E\n?}{} if $bi_str;
13695	    $b_str .= "\n" . $bi_str if $bi_str;
13696	}
13697	$c_balloon->Popup($b_str);
13698    } else {
13699	$c_balloon->Deactivate;
13700    }
13701}
13702
13703# Wird beim Verlassen einer Route aufgerufen.
13704sub leaveroute {
13705    if (!$leave_after) { # XXX not well tested yet!
13706	$leave_after =
13707	    $c->after(100, sub {
13708			  $str_label->configure(-fg => $dim_color);
13709			  $c_balloon->Deactivate(1) if defined $c_balloon;
13710			  undef $leave_after;
13711		      });
13712    }
13713}
13714
13715# Gibt den ersten Tag aus @allowed_tags aus, der sich unter dem jetzigen
13716# Tag befindet.
13717sub find_below {
13718    my($c, @allowed_tags) = @_;
13719    my $e = $c->XEvent;
13720    my($xx, $yy) = ($c->canvasx($e->x), $c->canvasy($e->y));
13721    my(@items) = $c->find(overlapping => $xx-1, $yy-1, $xx+1, $yy+1);
13722    my %allowed_tags;
13723    foreach (@allowed_tags) { $allowed_tags{$_} = 1 }
13724    my %res;
13725    # Now using "reverse", so top-most items are preferred
13726    # XXX Hopefully this change does not break anything.
13727    foreach my $item (reverse @items) {
13728	my(@tags) = $c->gettags($item);
13729	if ($allowed_tags{$tags[0]} && !exists $res{$tags[0]}) {
13730	    $res{$tags[0]} = $item;
13731	}
13732    }
13733    foreach (@allowed_tags) {
13734	if (exists $res{$_}) {
13735	    return ($res{$_}, $c->gettags($res{$_}));
13736	}
13737    }
13738    undef;
13739}
13740
13741# Similar to find_below, but use a list of regexes and restrict to
13742# a list of tag positions.
13743#
13744# The position is determined by the optional argument -cxy => [$cx,$cy],
13745# or the position of the current canvas event.
13746sub find_below_rx {
13747    my($c, $allowed_tags_rxs, $tag_pos, $forbidden_tags_rxs, %args) = @_;
13748    my $cxy = delete $args{-cxy};
13749    warn "ERROR: Unhandled args: " . join(" ", %args) if %args; # XXX consider to make this a die()
13750    my($cx,$cy);
13751    if ($cxy) {
13752	($cx, $cy) = @$cxy;
13753    } else {
13754	my $e = $c->XEvent;
13755	($cx, $cy) = ($c->canvasx($e->x), $c->canvasy($e->y));
13756    }
13757
13758    my(@items) = $c->find(overlapping => $cx-1, $cy-1, $cx+1, $cy+1);
13759    # Now using "reverse", so top-most items are preferred
13760 ITEM:
13761    foreach my $item (reverse @items) {
13762	my(@tags) = $c->gettags($item);
13763	my @restricted_tags = $tag_pos ? @tags[@$tag_pos] : @tags;
13764	my $ok = 0;
13765	for my $tag (@restricted_tags) {
13766	    for my $rx (@$allowed_tags_rxs) {
13767                if ($tag =~ /$rx/) {
13768		    if ($forbidden_tags_rxs) {
13769			for my $frx (@$forbidden_tags_rxs) {
13770			    if ($tag =~ /$frx/) {
13771				next ITEM;
13772			    }
13773			}
13774		    }
13775		    $ok = 1;
13776		}
13777	    }
13778	}
13779	if ($ok) {
13780	    return ($item, @tags);
13781	}
13782    }
13783    undef;
13784}
13785
13786# Doc pending XXX
13787# tag list imcomplete, should be roughly the same like in set_bindings XXX
13788sub show_below_str {
13789    my($c) = @_;
13790    my($item, @tags) = find_below($c,
13791				  (qw/s sBAB l u b r fz f w/, (map { "comm-$_" } @comments_types)),
13792				 );
13793    return if !defined $item;
13794    $act_value{Strasse} = $tags[1];
13795    $str_label->configure(-fg => 'black');
13796    $act_value{Strasse};
13797}
13798
13799# Guckt zun�chst nach, ob sich darunter eine Route befindet und leitet
13800# bei Erfolg die Bearbeitung an enterroute() weiter, ansonsten wird
13801# show_below_str() verwendet.
13802sub show_below_route_str {
13803    my $c = shift;
13804    my($item, @tags) = find_below($c, qw/route/);
13805    if (!defined $item) {
13806	show_below_str($c); # R�ckgabe: String
13807    } else {
13808	enterroute($c, $item);
13809	undef; # R�ckgabe: undef
13810    }
13811}
13812
13813use vars qw($show_info_url);
13814sub handle_show_info_url {
13815    my($offset,$maxbytes) = @_;
13816    return undef if $offset > length($show_info_url);
13817    substr($show_info_url, $offset, $maxbytes);
13818}
13819
13820# Zeigt Informationen zum aktuellen Tag.
13821### AutoLoad Sub
13822sub show_info {
13823    my($x, $y) = @_;
13824    my(@tags) = $c->gettags('current');
13825    return if !@tags || !defined $tags[0];
13826    my($base_tag, $is_p);
13827
13828    my $in_2nd_pass = 0;
13829    my $recursion_breaker=0;#XXX
13830    while (1) {
13831	if($recursion_breaker++>10){die}#XXX
13832	$base_tag = $tags[0];
13833	@tags = grep { $_ ne "current" } @tags;
13834	$is_p = ($base_tag =~ /-(?:[fb]g|img)$/);
13835	$base_tag =~ s/-(?:[fb]g|img)$//;
13836	last unless !exists $p_file{$base_tag} and !$str_file{$base_tag};
13837	my($below_item, @below_tags) = find_below($c, qw/s l u b r f w o v fz/);
13838	if (!defined $below_item) {
13839	    # 2nd pass: check for markers etc.
13840	    my($below_item, @below_tags) = find_below($c, qw/show/); # XXX still necessary? 'show' is now -state=>'disabled'
13841	    if (!defined $below_item) {
13842		# XXX Alert! Hardcoded for special osm layer, see BBBikeOsmUtil XXX
13843		%BBBikeOsmUtil::osm_layer = %BBBikeOsmUtil::osm_layer if 0; # cease -w
13844		if (defined $BBBikeOsmUtil::osm_layer{item} && grep { $_ eq 'osm' } @tags) {
13845		    # just accept
13846		    last;
13847		} else {
13848		    main::status_message("Es wurde kein Kartenelement an dieser Position gefunden.", "err");
13849		    warn "Current tags=@tags\nBase tag=$base_tag\nBelow item/tags=$below_item @below_tags";
13850		    return;
13851		}
13852	    }
13853	    $in_2nd_pass = 1;
13854	}
13855	@tags = @below_tags;
13856	last if $in_2nd_pass;
13857    }
13858
13859    my $index;
13860    if ($#tags >= 3) {
13861	($index = $tags[3]) =~ s/^$base_tag-//;
13862	#warn $index;
13863    }
13864    my $strname = $tags[1];
13865    my $good_link_for_strname = 1;
13866    my $outside_berlin = 0; # XXX works only for landstrassen, but not for wasser, flaechen, s/rbahn, sehenswuerdigkeiten ... outside berlin
13867    if ($tags[0] =~ m{^(?:
13868			(?:[ub]|kn)-fg
13869		       )
13870		    }x) {
13871	$strname = $tags[2];
13872    } elsif ($tags[0] eq 'GU-img') {
13873	$strname = $tags[2];
13874    } elsif ($tags[0] =~ m{^sperre}) {
13875	$strname = $tags[2];
13876	$good_link_for_strname = 0;
13877    } elsif ($tags[0] =~ m{^(?:qs|hs|ql|hl)}) {
13878	$good_link_for_strname = 0;
13879    } elsif ($tags[0] =~ m{^l$}) {
13880	$outside_berlin = 1;
13881    } elsif ($tags[0] =~ /^lsa/) {
13882	undef $strname; # no meaningful name
13883    } elsif ($tags[0] =~ m{^o$}) {
13884	$outside_berlin = 1;
13885	$strname = $tags[2];
13886    }
13887
13888    my(@coords) = $c->coords('current');
13889    my $current_is_label = $c->type('current') eq 'text';
13890    if (!@coords || @coords > 2 || $current_is_label) {
13891	my($px,$py) = $c->pointerxy;
13892        $px -= $c->rootx;
13893        $py -= $c->rooty;
13894	@coords = ($c->canvasx($px), $c->canvasy($py));
13895    }
13896    require Karte::Polar;
13897    require Karte::UTM;
13898    require Karte::ETRS89;
13899    my($sx,$sy) = $Karte::Standard::obj->trim_accuracy(anti_transpose($coords[0], $coords[1]));
13900    my($px,$py);
13901    if ($city_obj->can("standard_to_polar")) {
13902	($px,$py) = $city_obj->standard_to_polar($sx,$sy);
13903    } else {
13904	($px,$py) = $Karte::Polar::obj->trim_accuracy($coord_system_obj->map2map($Karte::Polar::obj, $sx, $sy));
13905    }
13906    my @polarcoord = (Karte::Polar::dms_human_readable("lat", Karte::Polar::ddd2dms($py)),
13907		      Karte::Polar::dms_human_readable("long", Karte::Polar::ddd2dms($px)));
13908    my @polarcoord2 = (Karte::Polar::dmm_human_readable("lat", Karte::Polar::ddd2dmm($py)),
13909		       Karte::Polar::dmm_human_readable("long", Karte::Polar::ddd2dmm($px)));
13910    my($gkk_zone_potsdam, $gkk_easting_potsdam, $gkk_northing_potsdam) = Karte::UTM::DegreesToGKK($py, $px, "Potsdam");
13911    my($gkk_zone_wgs84, $gkk_easting_wgs84, $gkk_northing_wgs84) = Karte::UTM::DegreesToGKK($py, $px, "WGS 84");
13912    my($utm_ze, $utm_zn, $utm_x, $utm_y) = Karte::UTM::DegreesToUTM($py, $px, "WGS 84");
13913    my($etrs_east, $etrs_north) = Karte::ETRS89::UTMToETRS89($utm_ze, $utm_zn, $utm_x, $utm_y);
13914
13915    my @comments;
13916    if (!$str_obj{"comm"}) {
13917	$str_obj{'comm'} = _get_comments_obj();
13918    }
13919    if (!$comments_pos_net) {
13920	eval {
13921	    $comments_pos_net = $str_obj{"comm"}->make_coord_to_pos
13922		(sub {
13923		     my $cat = $_[0]->[Strassen::CAT];
13924		     $cat =~ /^(?:CS|[-+][12])/ ? 2 : 0;
13925		 });
13926	}; warn $@ if $@;
13927    }
13928    if ($comments_pos_net && $str_obj{"comm"}) {
13929	eval {
13930	    my($first, $second);
13931	    (undef,undef,$first,$second) = nearest_line_points_mouse($c);
13932	    $first = join(",",@$first);
13933	    $second = join(",",@$second);
13934	    if (defined $first && defined $second &&
13935		$comments_pos_net->{"${first}_${second}"}) {
13936		foreach my $pos (@{$comments_pos_net->{"${first}_${second}"}}) {
13937		    my $r = $str_obj{"comm"}->get($pos);
13938		    if ($r->[Strassen::NAME] ne $strname) {
13939			push @comments, $r->[Strassen::NAME];
13940		    }
13941		}
13942	    }
13943	}; warn $@ if $@;
13944    }
13945
13946    my($area, $total_len);
13947    if (defined $index && $index =~ /^\d+/) {
13948	my $s = eval { get_any_strassen_obj("str", $base_tag) };
13949	if (!$s) {
13950	    $s = get_any_strassen_obj("p", $base_tag);
13951	}
13952	if ($s) {
13953	    require Strassen::Stat;
13954	    my $r = $s->get($index);
13955# XXX bei weitem noch nicht perfekt: statt des Indexes sollte der
13956# NAME verwendet werden, um alle gleichnamigen Objekte zusammenzufassen
13957# Au�erdem sind manche Gew�sser gleichzeitig Seen und Fl�sse (Havel), bei
13958# diesen sollten aus der Fl�che eine vern�nftige L�nge berechnet werden
13959# und diese zu der normalen L�nge dazuaddiert werden.
13960	    if ($r) {
13961		if ($r->[Strassen::CAT()] =~ /^F:/) {
13962		    $area = Strassen::area($r);
13963#XXX Noch nicht --- siehe Kommentare in wasserstrassen-orig und data/Makefile
13964#  	    # Inseln abziehen
13965#  	    $s->set_index($index + 1);
13966#  	    while(1) {
13967#  		my $r = $s->next;
13968#  		last if !@{ $r->[Strassen::COORDS] };
13969#  		last if $r->[Strassen::CAT] ne 'F:I';
13970#  		$area - Strassen::area($r) / 1_000_000;
13971#  	    }
13972		} else {
13973		    $total_len = Strassen::total_len($r) / 1_000;
13974		}
13975	    }
13976	}
13977    }
13978
13979    my $show_info_sub = sub {
13980	my($name, $good_link_for_strname, $outside_berlin, $important_txt_and_tag, $unimportant_txt_and_tag) = @_;
13981	#my $tl_tag = "info-$base_tag"; # one window per canvas type
13982	my $tl_tag = "info"; # one window for all
13983	my $info_top = redisplay_top($top, $tl_tag,
13984				     -title => M"Information",
13985				     -class => "BbbikePassive",
13986				    );
13987	if (defined $info_top) {
13988	    require Tk::ROText;
13989	    $info_text = hypertext_widget($info_top);
13990	    $info_top->Button(Name => 'close',
13991			      -command => sub { $info_top->destroy },
13992			     )->pack(-fill => "x");
13993	    toplevel_checker($info_top);
13994	} else {
13995	    $info_top = $toplevel{$tl_tag};
13996	    soft_flash($info_text);
13997	}
13998
13999	my $link_menu = $info_text->Menu(-title => M"Linkmen�",
14000					 -tearoff => 0);
14001
14002	my $copy_link = sub {
14003	    my($url) = @_;
14004	    $show_info_url = $url if defined $url;
14005
14006	    $info_top->SelectionOwn;
14007	    $info_top->SelectionHandle; # calling this mysteriously solves the closure problem...
14008	    $info_top->SelectionHandle(\&handle_show_info_url);
14009	};
14010	$link_menu->command
14011	    (-label => M"Link kopieren",
14012	     -command => sub { $copy_link->() },
14013	    );
14014	my $show_url = sub {
14015	    my($linkcount, $url, $my_link_menu) = @_;
14016	    $info_text->tagBind
14017		("link$linkcount", "<ButtonRelease-1>" => sub {
14018		     my $url = ref $url eq 'CODE' ? $url->() : $url;
14019		     require WWWBrowser;
14020		     main::status_message("URL: $url", "info");
14021		     WWWBrowser::start_browser($url);
14022		 }
14023		);
14024	    if (!$my_link_menu) {
14025		$my_link_menu = $link_menu;
14026	    }
14027	    $info_text->tagBind
14028		("link$linkcount", "<Button-3>" => sub {
14029		     my $e = $_[0]->XEvent;
14030		     $show_info_url = ref $url eq 'CODE' ? $url->() : $url;
14031		     $my_link_menu->Post($e->X, $e->Y);
14032		     Tk->break;
14033		 });
14034	};
14035
14036	# Longest text for first column:
14037	$info_text->configure(-tabs => [$info_text->fontMeasure($font{normal}, "Sonnenuntergang: ")]);
14038
14039	my($yview) = $info_text->yview;
14040	$info_text->delete("1.0", "end");
14041	my $linkcount = 1;
14042
14043	if (defined $name && $name !~ m{^\s*$}) {
14044	    $info_text->insert("end", M("Name")."\n", "bold");
14045
14046	    my $url;
14047	    my $common_url;
14048	    if ($name =~ m{(https?://\S+)}) {
14049		$url = $1;
14050	    } elsif ($good_link_for_strname) {
14051		my $google_url = "http://www.google.com/search?";
14052		require CGI;
14053		CGI->import('-oldstyle_urls');
14054		(my $name = $name) =~ s{(str)\.}{$1a�e}gi;
14055		# XXX duplicated in LuiseBerlin.pm
14056		$name =~ s{\[.*\]}{}g; # remove special [...] parts
14057		$name =~ s{:\s+.*}{}g; # also remove everything after ":"
14058		($name, my @cityparts) = Strasse::split_street_citypart($name);
14059		my $common_q = ($outside_berlin ? '' : qq{Berliner }) .
14060		    qq{"$name"} . (@cityparts ? " ".join(" ",@cityparts) : "");
14061		$url        = $google_url . CGI->new({ 'q' => qq{site:de.wikipedia.org $common_q} })->query_string;
14062		$common_url = $google_url . CGI->new({ 'q' => $common_q })->query_string;
14063	    }
14064
14065	    if (!$url) {
14066		$info_text->insert("end", "$name\n");
14067	    } else {
14068		$info_text->insert("end", $name, "link$linkcount");
14069
14070		my $www_link_menu = $info_text->Menu(-title => M"Linkmen�",
14071						     -tearoff => 0);
14072		$www_link_menu->command
14073		    (-label => M"Link kopieren",
14074		     -command => sub { $copy_link->($url) },
14075		    );
14076		if ($common_url) {
14077		    $www_link_menu->command
14078			(-label => M"Allgemeine Google-Suche",
14079			 -command => sub {
14080			     require WWWBrowser;
14081			     main::status_message("URL: $common_url", "info");
14082			     WWWBrowser::start_browser($common_url);
14083			 }
14084		     );
14085		}
14086
14087		$show_url->($linkcount, $url, $www_link_menu);
14088		$linkcount++;
14089		$info_text->insert("end", "\n");
14090	    }
14091	    $info_text->insert("end", "\n");
14092	}
14093
14094	my $write_txt_and_tag = sub {
14095	    my(@txt_and_tag) = @_;
14096	    for (my $i=0; $i<=$#txt_and_tag; $i+=2) {
14097		my($txt, $tag) = @txt_and_tag[$i, $i+1];
14098		for my $txtline (split /\n/, $txt) {
14099		    my $pos = 0;
14100		    while ($txtline =~ m{^(.*?)((?:ftp|https?)://\S+)}g) {
14101			my($pre_text, $link_text) = ($1, $2);
14102			$info_text->insert("end", $pre_text, $tag);
14103			$info_text->insert("end", $link_text, "link$linkcount");
14104			$show_url->($linkcount, $link_text);
14105			$linkcount++;
14106			$pos = pos($txtline);
14107		    }
14108		    $info_text->insert("end", substr($txtline, $pos), $tag);
14109		    $info_text->insert("end", "\n");
14110		}
14111	    }
14112	    if (@txt_and_tag) {
14113		$info_text->insert("end", "\n\n");
14114	    }
14115	};
14116
14117	$write_txt_and_tag->(@$important_txt_and_tag) if @{ $important_txt_and_tag || [] };
14118
14119	my $comment_label_end_index;
14120	if (@comments) {
14121	    $info_text->insert("end", M("Kommentare").": ", "bold");
14122	    $comment_label_end_index = $info_text->index("end - 1c");
14123	    $info_text->insert("end", "\t" . join("\n\t", @comments), "comments_text");
14124	    $info_text->insert("end", "\n\n");
14125	}
14126	if (defined $area) {
14127	    my($area_value, $area_unit);
14128	    if ($area > 10_000) {
14129		$area_value = $area / 1_000_000;
14130		$area_unit = 'km�';
14131	    } else {
14132		$area_value = $area;
14133		$area_unit = 'm�';
14134	    }
14135	    $info_text->insert("end", M("Fl�che") . ":", "bold",
14136			       sprintf("\t%.2f %s", $area_value, $area_unit) . M(" (dieses Teilst�ck)"), undef); # XXX Msg
14137	    $info_text->insert("end", "\n\n");
14138	}
14139	if (defined $total_len) {
14140	    $info_text->insert("end", M("L�nge") . ":", "bold",
14141			       sprintf("\t%.2f km", $total_len) . M(" (dieses Teilst�ck)"), undef); # XXX Msg
14142	    $info_text->insert("end", "\n\n");
14143	}
14144
14145	$info_text->insert("end", "Links\n", "bold");
14146	# Mapserver XXX move to function for creating URL
14147	my @mapserver_def = ([$BBBike::BBBIKE_MAPSERVER_ADDRESS_URL,
14148			      "Mapserver"]);
14149	if ($devel_host) {
14150	    push @mapserver_def, [defined $ENV{BBBIKE_TEST_CGIDIR} ? "$ENV{BBBIKE_TEST_CGIDIR}/mapserver_address.cgi" : "http://localhost/bbbike/cgi/mapserver_address.cgi", "Lokaler Mapserver", "is_local"];
14151	}
14152
14153	my @mapext = $c->get_corners;
14154	@mapext[0,1] = map { int } anti_transpose(@mapext[0,1]);
14155	@mapext[2,3] = map { int } anti_transpose(@mapext[2,3]);
14156
14157	my @layers;
14158	# XXX move mapping or this function to a config-like module
14159	my @str_draw_mapping = ([w => "gewaesser"],
14160				[f => "flaechen"],
14161				[[qw(g gP gD gBO)] => "grenzen"],
14162				[[qw(u b r)] => "bahn"],
14163				[[qw(qs ql)] => "qualitaet"],
14164				[[qw(hs hl)] => "handicap"],
14165				[rw => "radwege"],
14166				[e => "faehren"],
14167				[fz => "fragezeichen"],
14168				[v => "sehenswuerdigkeit"],
14169			       );
14170	my @p_draw_mapping   = ([o => "orte"],
14171				[lsa => "ampeln"],
14172				[obst => "obst"],
14173				[sperre => "blocked"],
14174			       );
14175	for my $type (qw(str p)) {
14176	    my $mapping = $type eq 'str' ? \@str_draw_mapping : \@p_draw_mapping;
14177	    my $draw    = $type eq 'str' ? \%str_draw         : \%p_draw;
14178	    for my $check (@$mapping) {
14179		my($abk, $ms_layer) = @$check;
14180		my $doit;
14181		if (ref $abk eq 'ARRAY') {
14182		    for (@$abk) {
14183			if ($draw->{$_}) {
14184			    $doit = 1;
14185			    last;
14186			}
14187		    }
14188		} elsif ($draw->{$abk}) {
14189		    $doit = 1;
14190		}
14191		if ($doit) {
14192		    push @layers, $ms_layer;
14193		}
14194	    }
14195	}
14196	push @layers, "route"; # the "mark" is also in the "route" layer
14197
14198	# XXX maybe use Karte::trim_accuracy instead of int?
14199	my $real_coords = join(",", map { int } anti_transpose($coords[0], $coords[1]));
14200	my $wgs84_coords = "$px,$py";
14201
14202	if ($city_obj->cityname eq 'Berlin') { # only mapserver links for Berlin data
14203	    my $mapserver_logo_photo = load_photo($top, 'mapserver_logo', -persistent => 1);
14204	    my $need_indentation = !$mapserver_logo_photo;
14205	    for my $def (@mapserver_def) {
14206		my($mapserver_url, $mapserver_label, $is_local) = @$def;
14207		my $url = "$mapserver_url/coords=" . $real_coords;
14208		$url .= "/mapext=" . join(",",@mapext);
14209		if (@layers) {
14210		    $url .= "/" . join("/", map { "layer=$_" } @layers);
14211		}
14212		if ($mapserver_logo_photo) {
14213		    $info_text->imageCreate("end", -image => $mapserver_logo_photo,
14214					    -align => "bottom", -padx => 2, -pady => 2);
14215		}
14216		$info_text->insert("end", $mapserver_label, ["link$linkcount", ($need_indentation ? "iconindent" : ())]);
14217		$show_url->($linkcount, $url);
14218		$info_text->insert("end", "\n");
14219		$linkcount++;
14220
14221		if ($advanced && !$is_local) {
14222		    if ($mapserver_logo_photo) {
14223			$info_text->imageCreate("end", -image => $mapserver_logo_photo,
14224						-align => "bottom", -padx => 2, -pady => 2);
14225		    }
14226		    $info_text->insert("end", $mapserver_label . " (kurzer Link)",
14227				       ["link$linkcount", ($need_indentation ? "iconindent" : ())]);
14228		    $show_url->($linkcount, sub {
14229				    if (exists $long_url_to_short_url{$url}) {
14230					return $long_url_to_short_url{$url};
14231				    }
14232				    if (!eval { require WWW::Shorten; WWW::Shorten->import(); 1 }) {
14233					status_message("Das Modul WWW::Shorten ist nicht vorhanden.", "die");
14234				    }
14235				    my $short_url = makeashorterlink($url);
14236				    $long_url_to_short_url{$url} = $short_url;
14237				    $short_url;
14238				});
14239		    $info_text->insert("end", "\n");
14240		    $linkcount++;
14241		}
14242	    }
14243	}
14244
14245	if ($city_obj->cityname eq 'Berlin') { # only bbbike.de links for Berlin data (XXX but maybe bbbike.org links could be done instead?)
14246	    my @bbbike_cgi_def = ([$BBBike::BBBIKE_DIRECT_WWW, "BBBike im WWW"]);
14247	    if ($devel_host) {
14248		push @bbbike_cgi_def, [defined $ENV{BBBIKE_TEST_CGIDIR} ? "$ENV{BBBIKE_TEST_CGIDIR}/bbbike.cgi" : "http://localhost/bbbike/cgi/bbbike.cgi", "lokal: BBBike im WWW"];
14249	    }
14250
14251	    my $zielname = "";
14252	    {
14253		my $is_first = 1;
14254		for my $def (@bbbike_cgi_def) {
14255		    my $bbbike_cgi_url = $def->[0];
14256
14257		    my $need_indentation;
14258		    if ($srtbike16_icon) {
14259			$info_text->imageCreate("end", -image => $srtbike16_icon,
14260						-align => "bottom", -padx => 2, -pady => 1);
14261		    } else {
14262			$need_indentation = 1;
14263		    }
14264
14265		    $info_text->insert("end", $def->[1],
14266				       ["link$linkcount", ($need_indentation ? "iconindent" : ())]);
14267		    $info_text->insert("end", " ");
14268		    if ($is_first) {
14269			my $zielname_e = $info_text->Entry(-textvariable => \$zielname,
14270							   -width => 10);
14271			$info_text->insert("end", " Zielname:");
14272			$info_text->windowCreate("end", -window => $zielname_e);
14273			$is_first = 0;
14274		    }
14275
14276		    my $www_link_menu = $info_text->Menu(-title => M"Linkmen�",
14277							 -tearoff => 0);
14278		    $www_link_menu->command
14279			(-label => M"Link kopieren als Ziel",
14280			 -command => sub { $copy_link->() },
14281			);
14282		    $www_link_menu->command
14283			(-label => M"Link kopieren als Start",
14284			 -command => sub {
14285			     my $current_start_link_url = $show_info_url;
14286			     $current_start_link_url =~ s{ziel}{start}g;
14287			     $copy_link->($current_start_link_url);
14288			 });
14289		    $www_link_menu->command
14290			(-label => M"Link kopieren als Start und Ziel",
14291			 -command => sub {
14292			     my $current_start_link_url = $show_info_url;
14293			     $current_start_link_url =~ s{ziel}{start}g;
14294			     my $complete_link_url = "$current_start_link_url\n$show_info_url";
14295			     $copy_link->($complete_link_url);
14296			 });
14297
14298		    $show_url->($linkcount, sub {
14299				    require CGI;
14300				    # sigh, ";" still makes problems...
14301				    my $zielname = $zielname;
14302				    if ($Tk::VERSION >= 804) {
14303					$zielname = Encode::encode("iso-8859-1", $zielname);
14304				    }
14305				    CGI->import('-oldstyle_urls');
14306				    my $q = CGI->new({zielc_wgs84 => $wgs84_coords,
14307						      zielname    => $zielname,
14308						     });
14309				    my $url = "$bbbike_cgi_url?" . $q->query_string;
14310				    $url;
14311				},
14312				$www_link_menu,
14313			       );
14314		    $info_text->insert("end", "\n");
14315		    $linkcount++;
14316		}
14317	    }
14318	}
14319
14320	if ($advanced
14321	    && !$city_obj->is_osm_source # no fragezeichen form link for osm data
14322	    && grep { $_ eq 'fz' } @tags
14323	   ) {
14324	    my $need_indentation = 1; # XXX unless I have an icon
14325	    $info_text->insert("end", "fragezeichenform",
14326			       ["link$linkcount", ($need_indentation ? "iconindent" : ())]);
14327	    $show_url->($linkcount, sub {
14328			    require CGI;
14329			    my $fragezeichen_comment = $strname;
14330			    if ($Tk::VERSION >= 804) {
14331				$fragezeichen_comment = Encode::encode("iso-8859-1", $fragezeichen_comment);
14332			    }
14333			    CGI->import('-oldstyle_urls');
14334			    my $qs = CGI->new({strname => $fragezeichen_comment,
14335					       strname_html => CGI::escapeHTML($fragezeichen_comment),
14336					       supplied_coord => $real_coords,
14337					      })->query_string;
14338			    # XXX $BBBIKE_UPDATE_WWW shows also to root bbbike directory at server
14339			    my $url = "$BBBike::BBBIKE_UPDATE_WWW/html/fragezeichenform.html?$qs";
14340			    $url;
14341			});
14342	    $info_text->insert("end", "\n");
14343	    $linkcount++;
14344	}
14345
14346	my($mapscale_scale) = $mapscale =~ /:\s*(\d+)/;
14347
14348	if (!$google_photo) {
14349	    $google_photo = load_photo($top, 'google');
14350	}
14351	if (!$bbbike_google_photo) {
14352	    $bbbike_google_photo = load_photo($top, 'bbbike_google');
14353	}
14354	if (!$google_streetview_photo) {
14355	    $google_streetview_photo = load_photo($top, 'google_streetview');
14356	}
14357
14358	{
14359	    my @bbbike_google_map_defs = (($devel_host
14360					   ? ["lokal: Google Maps (BBBike)", "http://localhost/bbbike/cgi/bbbikegooglemap.cgi"]
14361					   : ()
14362					  ),
14363					  ["Google Maps (BBBike)", $BBBike::BBBIKE_GOOGLEMAP_URL],
14364					 );
14365	    for my $def (@bbbike_google_map_defs) {
14366		my($label, $baseurl) = @$def;
14367		my $need_indentation;
14368		if ($bbbike_google_photo) {
14369		    $info_text->imageCreate("end", -image => $bbbike_google_photo,
14370					    -align => "bottom", -padx => 2, -pady => 1);
14371		} else {
14372		    $need_indentation = 1;
14373		}
14374		$info_text->insert("end", $label,
14375				   ["link$linkcount", ($need_indentation ? "iconindent" : ())]);
14376		$show_url->($linkcount, sub {
14377				require CGI;
14378				my $center = "$px,$py";
14379				my $zoom;
14380				if ($mapscale_scale < 2000) {
14381				    $zoom = 18;
14382				} elsif ($mapscale_scale < 4000) {
14383				    $zoom = 17;
14384				} elsif ($mapscale_scale < 8000) {
14385				    $zoom = 16;
14386				} elsif ($mapscale_scale < 16000) {
14387				    $zoom = 15;
14388				} else {
14389				    $zoom = 14;
14390				}
14391				my $q2 = CGI->new({ center => $center,
14392						    zoom => $zoom,
14393						    autosel => 1,
14394						    maptype => "hybrid",
14395						    coordsystem => "polar",
14396						    mapmode => "addroute",
14397						  });
14398				my $url = $baseurl . "?" . $q2->query_string;
14399				$url;
14400			    });
14401		$info_text->insert("end", "\n");
14402		$linkcount++;
14403	    }
14404	}
14405
14406	{
14407	    my $need_indentation;
14408	    if ($google_photo) {
14409		$info_text->imageCreate("end", -image => $google_photo,
14410					-align => "bottom", -padx => 2, -pady => 1);
14411	    } else {
14412		$need_indentation = 1;
14413	    }
14414
14415	    $info_text->insert("end", "Google Maps (Original)",
14416			       ["link$linkcount", ($need_indentation ? "iconindent" : ())]);
14417	    $show_url->($linkcount, sub {
14418			    require CGI;
14419			    my $q2 = CGI->new({ ll => "$py,$px" });
14420			    my $url = "http://www.google.com/maps?" . $q2->query_string;
14421			    $url;
14422			});
14423	    $info_text->insert("end", "\n");
14424	    $linkcount++;
14425
14426	    if ($google_streetview_photo) {
14427		$info_text->imageCreate("end", -image => $google_streetview_photo,
14428					-align => "bottom", -padx => 2, -pady => 1);
14429	    }
14430
14431	    $info_text->insert("end", "Google Maps (StreetView)",
14432			       ["link$linkcount", ($need_indentation ? "iconindent" : ())]);
14433	    $show_url->($linkcount, sub {
14434			    require CGI;
14435			    my $q2 = CGI->new({ cbll => "$py,$px",
14436						layer => 'c',
14437						cbp => '0,0,,0,0', # whatever is the meaning of these params
14438					      });
14439			    my $url = "http://www.google.com/maps?" . $q2->query_string;
14440			    $url;
14441			});
14442	    $info_text->insert("end", "\n");
14443	    $linkcount++;
14444	}
14445
14446	{
14447	    my $need_indentation = 1;
14448	    my $show_leaflet_url = sub {
14449		my($baseurl, $linkcount) = @_;
14450		$show_url->($linkcount, sub {
14451				require CGI;
14452				my $scale = 17 - log(($mapscale_scale)/3000)/log(2);
14453				$scale = 18 if $scale > 18;
14454				my $q2 = CGI->new({ mlat => $py,
14455						    mlon => $px,
14456						    ($Msg::lang eq 'en' ? (lang => "en") : ()),
14457						    zoom => int($scale),
14458						  });
14459				my $url = $baseurl . "?" . $q2->query_string;
14460				$url;
14461			    }
14462			   );
14463	    };
14464
14465	    if ($devel_host) {
14466		$info_text->insert("end", "BBBike Leaflet (local)",
14467				   ["link$linkcount", ($need_indentation ? "iconindent" : ())]);
14468		$show_leaflet_url->("http://localhost/bbbike/html/bbbikeleaflet.html", $linkcount);
14469		$info_text->insert("end", "\n");
14470		$linkcount++;
14471	    }
14472
14473	    $info_text->insert("end", "BBBike Leaflet",
14474			       ["link$linkcount", ($need_indentation ? "iconindent" : ())]);
14475	    $show_leaflet_url->($BBBike::BBBIKE_LEAFLET_URL, $linkcount);
14476	    $info_text->insert("end", "\n");
14477	    $linkcount++;
14478	}
14479
14480	{
14481
14482	    if (!$wikipedia_photo) {
14483		$wikipedia_photo = load_photo($top, 'wikipedia');
14484	    }
14485
14486	    my(@x) = Karte::Polar::ddd2dms($px);
14487	    my(@y) = Karte::Polar::ddd2dms($py);
14488	    push @x, $x[0] < 0 ? do { $x[0] *= -1; "W" } : "E";
14489	    push @y, $y[0] < 0 ? do { $y[0] *= -1; "S" } : "N";
14490	    #my $url = "http://stable.toolserver.org/geohack/geohack.php?params=" . join("_", @y, @x) . "_type:landmark_region:DE-BE";
14491	    my $url = "http://toolserver.org/~geohack/geohack.php?params=" . join("_", @y, @x) .
14492		($city_obj->cityname eq 'Berlin' ? "_type:landmark_region:DE-BE" : '');
14493
14494	    my $need_indentation;
14495	    if ($wikipedia_photo) {
14496		$info_text->imageCreate("end", -image => $wikipedia_photo,
14497					-align => "bottom", -padx => 2, -pady => 1);
14498	    } else {
14499		$need_indentation = 1;
14500	    }
14501
14502	    $info_text->insert("end", "Wikipedia Mapsources",
14503			       ["link$linkcount", ($need_indentation ? "iconindent" : ())]);
14504	    $show_url->($linkcount, $url);
14505	    $info_text->insert("end", "\n");
14506	    $linkcount++;
14507	}
14508
14509	my($px0,$py0,$px1,$py1);
14510	{
14511	    my($x0,$y0,$x1,$y1) = $c->get_corners;
14512	    my($sx0,$sy0,$sx1,$sy1) = (anti_transpose($x0,$y0),
14513				       anti_transpose($x1,$y1));
14514	    if ($city_obj->can("standard_to_polar")) {
14515		($px0,$px0) = $city_obj->standard_to_polar($sx0,$sy0);
14516		($px1,$px1) = $city_obj->standard_to_polar($sx1,$sy1);
14517	    } else {
14518		($px0,$py0) = $Karte::Polar::obj->trim_accuracy($coord_system_obj->map2map($Karte::Polar::obj, $sx0, $sy0));
14519		($px1,$py1) = $Karte::Polar::obj->trim_accuracy($coord_system_obj->map2map($Karte::Polar::obj, $sx1, $sy1));
14520	    }
14521	}
14522
14523	for my $key (keys %info_plugins) {
14524	    my $plugin = $info_plugins{$key};
14525
14526	    my @args = (coords => $real_coords,
14527			street => $strname,
14528			px => $px,
14529			py => $py,
14530			px0 => $px0, # left
14531			px1 => $px1, # right
14532			py0 => $py0, # top
14533			py1 => $py1, # bottom
14534			mapscale_scale => $mapscale_scale,
14535			tags => \@tags,
14536		       );
14537	    if ($plugin->{visibility} && !$plugin->{visibility}->(@args)) {
14538		next;
14539	    }
14540
14541	    my $need_indentation;
14542	    if ($plugin->{icon}) {
14543		eval {
14544		    $info_text->imageCreate("end", -image => $plugin->{icon},
14545					    -align => "bottom", -padx => 2, -pady => 1);
14546		};
14547		warn $@ if $@;
14548	    } else {
14549		$need_indentation = 1;
14550	    }
14551
14552	    $info_text->insert("end", $plugin->{name},
14553			       ["link$linkcount", $need_indentation ? 'iconindent' : ()]);
14554	    if ($plugin->{using_current_region}) {
14555		$info_text->insert('end', " \x{2610}" # BALLOT BOX
14556				  );
14557	    }
14558	    $info_text->insert('end', "\n");
14559	    $info_text->tagBind
14560		("link$linkcount", "<ButtonRelease-1>" => sub {
14561		     $plugin->{callback}->(@args);
14562		 });
14563	    if ($plugin->{callback_3_std}) {
14564		$info_text->tagBind
14565		    ("link$linkcount", "<Button-3>" => sub {
14566			 my $e = $_[0]->XEvent;
14567			 $show_info_url = $plugin->{callback_3_std}->(@args);
14568			 $link_menu->Post($e->X, $e->Y);
14569			 Tk->break;
14570		     });
14571	    } elsif ($plugin->{callback_3}) {
14572		$info_text->tagBind
14573		    ("link$linkcount", "<Button-3>" => sub {
14574			 $plugin->{callback_3}->(@args, widget => $_[0]);
14575		     });
14576	    }
14577	    $linkcount++;
14578	}
14579
14580	{
14581	    $info_text->insert("end", "\n\n" . M("Koordinaten") . "\n", "bold");
14582	    if (@polarcoord) {
14583		$info_text->insert("end", M("Polar (DMS)") . ":\t$polarcoord[0]\n\t$polarcoord[1]\n");
14584	    }
14585	    if (@polarcoord2) {
14586		$info_text->insert("end", M("Polar (DMM)") . ":\t$polarcoord2[0]\n\t$polarcoord2[1]\n");
14587	    }
14588	    if (defined $px && defined $py) {
14589		$info_text->insert("end", M("Polar (DDD)") . ":\t$py\n\t$px\n");
14590		$info_text->insert("end", M("Polar (DDD,DDD)") . "\t$px,$py\n"); # alternative
14591	    }
14592	    if (defined $gkk_zone_potsdam) {
14593		$info_text->insert("end", "GKK (Potsdam):\t[$gkk_zone_potsdam] $gkk_easting_potsdam/$gkk_northing_potsdam\n");
14594	    }
14595	    if (defined $gkk_zone_wgs84) {
14596		$info_text->insert("end", "GKK (WGS 84):\t[$gkk_zone_wgs84] $gkk_easting_wgs84/$gkk_northing_wgs84\n");
14597	    }
14598	    if (defined $utm_ze) {
14599		$info_text->insert("end", "UTM (WGS 84):\t[$utm_ze/$utm_zn] $utm_x/$utm_y\n");
14600	    }
14601	    if (defined $etrs_east) {
14602		$info_text->insert("end", "ETRS 89:\t$etrs_east/$etrs_north\n");
14603	    }
14604	    if (defined $px && defined $py) {
14605		$info_text->insert("end", "URI:\t");
14606		my $uri = "geo:$py,$px";
14607		$info_text->insert("end", $uri, ["link$linkcount"]);
14608		$info_text->insert("end", "\n");
14609		$info_text->tagBind
14610		    ("link$linkcount", "<ButtonRelease-1>" => sub {
14611			 $info_text->SelectionOwn;
14612			 $info_text->SelectionHandle; # calling this mysteriously solves the closure problem...
14613			 $info_text->SelectionHandle(sub { return $uri });
14614			 main::status_message("Geo URI $uri in selection", "info");
14615		     });
14616		$linkcount++;
14617	    }
14618	    $info_text->insert("end", "BBBike:\t$sx,$sy\n");
14619	    if (defined $px && defined $py) {
14620		$info_text->insert("end", "Wikipedia-Markup:\tNS=$py|EW=$px");
14621	    }
14622	    $info_text->insert("end", "\n");
14623	}
14624
14625	# Das war der letzte Link
14626	for (1 .. $linkcount) {
14627	    $info_text->tagConfigure("link$_", -underline => 1,
14628				     -foreground => "blue3");
14629	    $info_text->tagBind("link$_", "<Enter>" => sub {
14630				    $info_text->configure(-cursor => "hand2");
14631				});
14632	    $info_text->tagBind("link$_", "<Leave>" => sub {
14633				    $info_text->configure(-cursor => undef);
14634				});
14635	}
14636
14637	eval {
14638	    require Astro::Sunrise;
14639	    Astro::Sunrise->VERSION(0.85);
14640
14641	    my $get_sun_rise = sub {
14642		my $alt = shift;
14643		Astro::Sunrise::sun_rise($px,$py, $alt);
14644	    };
14645	    my $get_sun_set = sub {
14646		my $alt = shift;
14647		Astro::Sunrise::sun_set($px,$py, $alt);
14648	    };
14649
14650	    my $sunrise_real     = $get_sun_rise->();
14651	    my $sunrise_civil    = $get_sun_rise->(-6);
14652	    my $sunrise_nautical = $get_sun_rise->(-12);
14653	    my $sunrise_astro    = $get_sun_rise->(-15);
14654
14655	    my $sunset_real      = $get_sun_set->();
14656	    my $sunset_civil     = $get_sun_set->(-6);
14657	    my $sunset_nautical  = $get_sun_set->(-12);
14658	    my $sunset_astro     = $get_sun_set->(-15);
14659
14660	    $info_text->insert("end", "\nSonnenaufgang/-untergang\n", "bold");
14661	    $info_text->insert("end", <<EOF);
14662Sonnenaufgang:\t$sunrise_real
14663D�mmerung ab:
14664  b�rgerliche:\t$sunrise_civil
14665  nautische:\t$sunrise_nautical
14666  astronomische:\t$sunrise_astro
14667
14668Sonnenuntergang:\t$sunset_real
14669D�mmerung bis:
14670  b�rgerliche:\t$sunset_civil
14671  nautische:\t$sunset_nautical
14672  astronomische:\t$sunset_astro
14673
14674EOF
14675	};
14676	warn $@ if $@;
14677
14678	$write_txt_and_tag->(@$unimportant_txt_and_tag) if @{ $unimportant_txt_and_tag || [] };
14679
14680	if (defined &show_info_ext) {
14681	    eval {
14682		my $txt = show_info_ext($c, @tags);
14683		if (defined $txt) {
14684		    $info_text->insert("end", "$txt\n");
14685		}
14686	    };
14687	    warn $@ if $@;
14688	}
14689
14690	if (defined $comment_label_end_index) {
14691	    $info_text->update;
14692	    my @bbox = $info_text->bbox($comment_label_end_index);
14693	    $info_text->tagConfigure
14694		("comments_text",
14695		 -lmargin2 => $bbox[0]-1-$info_text->cget(-bd)-$info_text->cget(-highlightthickness),
14696		);
14697	}
14698
14699	if (defined $yview) {
14700	    $info_text->yviewMoveto($yview);
14701	}
14702
14703    };
14704
14705    my @important_txt_and_tag;
14706    my @info_txt_and_tag;
14707    my @internal_canvas_tags;
14708
14709 FIND_INFO: {
14710	if (defined $str_file{$base_tag} && $str_file{$base_tag} =~ /\.shp$/) {
14711	    (my $dbf_file = $str_file{$base_tag}) =~ s/\.shp$/.dbf/;
14712	    require BBBikeAdvanced;
14713	    my $index;
14714	    for (@tags) {
14715		if (/^$base_tag-(\d+)/) {
14716		    $index = $1;
14717		    last;
14718		}
14719	    }
14720	    if (defined $index) {
14721		my $dbf_info = get_dbf_info($dbf_file, $index);
14722		if (defined $dbf_info) {
14723		    if (@tags > 3) {
14724			my $text = splice @tags, 2, 1;
14725			unshift @tags, $text, "";
14726		    }
14727		    push @important_txt_and_tag, "$dbf_info\n", undef;
14728		    push @internal_canvas_tags, join("\n", @tags), undef;
14729		    last FIND_INFO;
14730		}
14731	    }
14732	}
14733
14734	my(%info, $info_file);
14735	eval {
14736	    require DB_File;
14737	    require Fcntl;
14738	    if (!$is_p) {
14739		if ($str_file{$base_tag} !~ m|^/|) {
14740		    $str_file{$base_tag} = "$datadir/$str_file{$base_tag}";
14741		}
14742		$info_file = $str_file{$base_tag} . "-info";
14743	    } else {
14744		if ($p_file{$base_tag} !~ m|^/|) {
14745		    $p_file{$base_tag} = "$datadir/$p_file{$base_tag}";
14746		}
14747		$info_file = $p_file{$base_tag} . "-info";
14748	    }
14749	}; warn $@ if $@;
14750
14751	if ($info_file && tie %info, 'DB_File', $info_file, &Fcntl::O_RDONLY) {
14752	    warn "Use $info_file ...\n";
14753	TRY: {
14754		foreach my $i (1 .. 4) {
14755		    if (defined $tags[$i]) {
14756			if (defined $info{$tags[$i]}) {
14757			    push @info_txt_and_tag, $info{$tags[$i]}, undef;
14758			    last TRY;
14759			}
14760			if ($tags[$i] =~ /^L\d+-(\d+)/) {
14761			    my $id = $1;
14762			    foreach my $type (qw(s p)) {
14763				if (defined $info{"$type-$id"}) {
14764				    push @info_txt_and_tag, $info{"$type-$id"}, undef;
14765				    last TRY;
14766				}
14767			    }
14768			    if (defined $info{$id}) {
14769				push @info_txt_and_tag, $info{$id}, undef;
14770				last TRY;
14771			    }
14772			}
14773		    }
14774		}
14775	    }
14776	    push @internal_canvas_tags, join("\n", @tags), undef;
14777	    untie %info;
14778	    last FIND_INFO;
14779	}
14780
14781	if ($advanced) {
14782	    if (@tags > 3) {
14783		my $text = splice @tags, 2, 1;
14784		unshift @tags, $text, "";
14785	    }
14786
14787	    # XXX slightly hackish: link to the OSM node/way/... browser
14788	    for (@tags) {
14789		if (my($type,$id) = $_ =~ m{^osm-(node|way|relation)-(\d+)$}) {
14790		    push @tags, "http://www.openstreetmap.org/browse/$type/$id";
14791		    last;
14792		}
14793	    }
14794
14795	    push @internal_canvas_tags, join("\n", @tags), undef;
14796	}
14797    }
14798
14799    if (@internal_canvas_tags) {
14800	unshift @internal_canvas_tags, M("Interne Canvas-Tags").":\n", "bold";
14801    }
14802    if (@info_txt_and_tag) {
14803	unshift @info_txt_and_tag, M("Info").":\n", "bold";
14804    }
14805    $show_info_sub->($strname,
14806		     $good_link_for_strname,
14807		     $outside_berlin,
14808		     [
14809		      @important_txt_and_tag,
14810		      @info_txt_and_tag,
14811		     ],
14812		     [
14813		      @internal_canvas_tags,
14814		     ],
14815		    );
14816}
14817
14818sub hypertext_widget {
14819    my($t, %args) = @_;
14820
14821    require Tk::ROText;
14822    my $info_text = $t->Scrolled('ROText',
14823				 -wrap => 'word',
14824				 -scrollbars => 'osoe',
14825				 -highlightthickness => 0,
14826				 -borderwidth => 0,
14827				 -insertwidth => 0,
14828				 -width => 40,
14829				 -height => 30,
14830				)->pack(-expand => 1, -fill => "both");
14831    # Hack as described in http://wiki.tcl.tk/6101
14832    my $info_real_text = $info_text->Subwidget("scrolled");
14833    $info_real_text->bindtags(["myTextTag", $info_real_text->bindtags]);
14834    $info_real_text->bind
14835	("myTextTag",
14836	 "<Button-3>",
14837	 [sub {
14838	      my($w,$x,$y) = @_;
14839	      if (grep { /^link/ } $w->tagNames("\@$x,$y")) {
14840		  Tk->break;
14841	      }
14842	  }, Ev("x"), Ev("y")]);
14843
14844    $info_text->tagConfigure("bold", -font => $font{'bold'});
14845    $info_text->tagConfigure("fixed", -font => $font{'fixed'});
14846    $info_text->tagConfigure("iconindent", -lmargin1 => 16 + 2);
14847
14848    $info_text;
14849}
14850
14851### AutoLoad Sub
14852sub show_statistics {
14853    my $update_statistics;
14854    $update_statistics = sub {
14855        # XXX some day $dataset should replace all of %str_obj etc.
14856        $dataset = Strassen::Dataset->new if !$dataset;
14857        my $res = BBBikeStats::calculate
14858    	    (Route->new_from_realcoords(\@realcoords), $dataset);
14859        BBBikeStats::tk_display_result
14860	    ($top,$res,-markcommand => sub {
14861		 my($realcoordsref) = @_;
14862
14863		 my @coordsref;
14864		 for (@$realcoordsref) {
14865		     push @coordsref, [ map { [transpose(split/,/,$_)] } @$_];
14866		 }
14867		 mark_street(-coords => \@coordsref,
14868			     -dont_center => 1);
14869	     },
14870	     -updatecommand => $update_statistics,
14871	     -reusewindow => 1,
14872	    );
14873    };
14874
14875    IncBusy($top);
14876    eval {
14877        require BBBikeStats;
14878        require Strassen::Dataset;
14879	$update_statistics->();
14880    };
14881    my $err = $@;
14882    DecBusy($top);
14883    if ($err) {
14884        return status_message(Mfmt("Fehler: %s", $err), "err");
14885    }
14886}
14887
14888### AutoLoad Sub
14889sub next_free_layer {
14890    my $max_i = 1;
14891    while($occupied_layer{"L$max_i"}) {
14892	$max_i++;
14893    }
14894    for my $type (\%str_draw, \%p_draw) {
14895	while(my($abk, $val) = each %$type) {
14896	    if ($val && $abk =~ /^L(\d+)/ && $1 >= $max_i) {
14897		$max_i = $1+1;
14898		while($occupied_layer{"L$max_i"}) {
14899		    $max_i++;
14900		}
14901	    }
14902	}
14903    }
14904    my $abk = "L$max_i";
14905    reset_free_layer($abk);
14906    $abk;
14907}
14908
14909### AutoLoad Sub
14910sub reset_free_layer {
14911    my $abk = shift;
14912    delete $no_overlap_label{$abk};
14913    delete $layer_active_color{$abk};
14914    delete $layer_pre_enter_command{$abk};
14915    delete $layer_post_enter_command{$abk};
14916    delete $layer_pre_leave_command{$abk};
14917    delete $layer_post_leave_command{$abk};
14918    delete $layer_line_width{$abk};
14919    delete $layer_line_length{$abk};
14920    delete $layer_category_line_arrow{$abk};
14921    delete $layer_line_arrow{$abk};
14922    delete $layer_stipple{$abk};
14923    delete $layer_line_dash{$abk};
14924    delete $layer_line_capstyle{$abk};
14925    delete $layer_category_size{$abk};
14926    delete $layer_category_color{$abk};
14927    delete $layer_category_line_width{$abk};
14928    delete $layer_category_image{$abk};
14929    delete $layer_category_stipple{$abk};
14930    delete $layer_category_line_dash{$abk};
14931    delete $layer_category_capstyle{$abk};
14932    delete $layer_category_line_shorten{$abk};
14933    delete $layer_line_shorten{$abk};
14934    delete $layer_category_line_shorten_end{$abk};
14935    delete $layer_line_shorten_end{$abk};
14936    delete $layer_name{$abk};
14937    delete $layer_icon{$abk};
14938    delete $p_name_draw{$abk};
14939    delete $str_name_draw{$abk};
14940    delete $no_overlap_label{$abk};
14941    delete $do_outline_text{$abk};
14942    remove_from_stack($abk);
14943}
14944
14945### AutoLoad Sub
14946sub set_coord_output_sub {
14947    my $_coord_output = shift;
14948    if (defined $_coord_output) {
14949	$coord_output = $_coord_output;
14950    }
14951    (my $undecorated_coord_output = $coord_output) =~ s{:.*}{};
14952    # XXX warum geht es mit keys, aber nicht mit each!!?!?!?!
14953    foreach my $k (keys %Karte::map) {
14954	#while(my($k,$v) = each %Karte::map) {
14955	my $v = $Karte::map{$k};
14956	#warn "$k => $v";
14957	if ($undecorated_coord_output eq $k) {
14958	    my $o = $Karte::map{$k};
14959	    if ($edit_mode) { # XXX find better conditional
14960		my $from_o = $Karte::map{'berlinmap'}; # XXX don't hardcode, each edit_mode has its own map-token
14961		if ($coord_output eq 'polar:dms') {
14962		    $coord_output_sub = sub {
14963			my(@c) = map { $_ / $scale } transpose(@_);
14964			@c = map { sprintf "%d�%02d'%05.2f\"", Karte::Polar::ddd2dms($_) } $from_o->can('map2map')->($from_o, $o, @c);
14965			@c;
14966		    };
14967		} else {
14968		    $coord_output_sub = sub {
14969			my(@c) = map { $_ / $scale } transpose(@_);
14970			@c = map { int } $from_o->can('map2map')->($from_o, $o, @c);
14971			@c;
14972		    };
14973		}
14974	    } else {
14975		if ($coord_output eq 'polar:dms') {
14976		    $coord_output_sub = sub {
14977			my(@c) = map { sprintf "%d�%02d'%05.2f\"", Karte::Polar::ddd2dms($_) } $o->can('standard2map')->($o, @_);
14978			@c;
14979		    };
14980		} elsif ($coord_output eq 'standard') {
14981		    $coord_output_sub = sub {
14982			# force int
14983			my(@c) = map { int } $o->can('standard2map')->($o, @_);
14984			@c;
14985		    };
14986		} else {
14987		    $coord_output_sub = sub {
14988			my(@c) = $o->trim_accuracy($o->can('standard2map')->($o, @_));
14989			@c;
14990		    };
14991		}
14992	    }
14993	    return;
14994	}
14995    }
14996
14997    if ($coord_output eq 'canvas') {
14998 	$coord_output_sub = sub {
14999	    my(@c) = transpose(@_);
15000	    map {
15001		my $x = $_;
15002		if ($without_zoom_factor) {
15003		    $x = $x / $scale;
15004		}
15005		if ($coord_output_int) {
15006		    $x = int $x;
15007		}
15008		$x;
15009	    } @c;
15010	};
15011    } elsif ($coord_output ne '') {
15012	die "Unknown value for coordout: $coord_output";
15013    }
15014}
15015
15016# F�gt interaktiv die angeklickte Stelle in die Route (�ber die
15017# Funktion addpoint_xy) ein, erneuert die Kilometerangaben.
15018sub addpoint_inter {
15019## DEBUG_BEGIN
15020#benchbegin();
15021## DEBUG_END
15022    my(@tags) = $c->gettags('current');
15023    return if !@tags;
15024    my $res;
15025    if ($tags[0] eq 'pp' or $tags[0] =~ /^lsa/) {
15026	$res = addpoint_xy(@{Strassen::to_koord1($tags[1])},
15027			   $c->coords('current'));
15028    } elsif ($tags[0] eq 'o') {
15029	$res = addpoint_xy(anti_transpose($c->coords('current')),
15030			   $c->coords('current'));
15031    }
15032    return if !defined $res;
15033    updatekm();
15034    set_flag('via');
15035    set_flag('ziel');
15036    # XXX only for slowcpu?
15037    if (!($edit_mode || $edit_normal_mode)) {
15038	# restack_delayed is very slow for many points, so disabled here...
15039	restack_delayed();
15040	update_route_strname();
15041    }
15042## DEBUG_BEGIN
15043#benchend();
15044## DEBUG_END
15045}
15046
15047sub addpoints_xy {
15048    my($realcoords_ref, %args) = @_;
15049    my $canvascoords_ref = delete $args{-canvascoords};
15050    my $power_cache = {};
15051    for(my $i = 0; $i <= $#$realcoords_ref; $i++) {
15052	my($cx,$cy);
15053	if ($canvascoords_ref) {
15054	    ($cx,$cy) = @{ $canvascoords_ref->[$i] };
15055	}
15056	addpoint_xy(@{$realcoords_ref->[$i]}, $cx, $cy, -powercache => $power_cache);
15057    }
15058}
15059
15060# Eingaben: $x und $y als realcoords, $xx und $yy als Canvas-Koords
15061sub addpoint_xy {
15062    my($x, $y, $xx, $yy, %args) = @_;
15063## DEBUG_BEGIN
15064#benchbegin();
15065## DEBUG_END
15066
15067    my $power_cache = delete $args{-powercache};
15068
15069    if (!defined $xx) {
15070	if ($coord_system ne 'standard') {
15071	    warn "NYI: non-standard map mode and not supplied $xx and $yy to addpoint_xy";
15072	} else {
15073	    ($xx, $yy) = transpose($x, $y);
15074	}
15075    }
15076
15077    my($deltax, $deltay, $etappe);
15078    if (@realcoords != 0) {
15079	($deltax, $deltay) = ($x - $realcoords[-1]->[0],
15080			      $y - $realcoords[-1]->[1]);
15081	$etappe = sqrt(sqr($deltax) + sqr($deltay));
15082	return undef if $etappe == 0; # keine leeren Etappen
15083
15084	# F�hrstrecken von der Gesamtstrecke ausschlie�en:
15085    CHECK_NO_FERRY: {
15086	    if ($net) {
15087		my $xy0 = join(",", @{$realcoords[-1]});
15088		my $xy1 = "$x,$y";
15089		my $name = ((exists $net->{Net2Name}{$xy0} && $net->{Net2Name}{$xy0}{$xy1}) ||
15090			    (exists $net->{Net2Name}{$xy1} && $net->{Net2Name}{$xy1}{$xy0}));
15091		if (defined $name && $name =~ /^F�hre /) {
15092		    last CHECK_NO_FERRY;
15093		}
15094	    }
15095	    $strecke += $etappe;
15096	}
15097    }
15098    my($prex, $prey);
15099    push(@coords, [$xx, $yy]);
15100    $nr++;
15101    push(@realcoords, [$x, $y]);
15102    if ($nr == 0) {
15103	($prex, $prey) = ($xx, $yy);
15104    } else {
15105	($prex, $prey) = @{$coords[-2]};
15106    }
15107    my $hw;
15108    $hw = BBBikeCalc::head_wind($deltax, $deltay) if $wind;
15109    my $curr_line = $c->createLine
15110	($prex, $prey, $xx, $yy,
15111	 -width => ($route_below ? int(get_line_width('HH')*2.5) : 5),
15112	 ($route_arrowed ? (-arrow => "last") : ()),
15113	 # -dash and -capstyle don't work well together
15114	 ($route_dashed ? (-dash => [4,5]) : (-capstyle => $capstyle_round)),
15115	 -tags => ['route', "route-$nr"]);
15116    if ($nr == 0) {
15117	set_flag('start');
15118    }
15119
15120    # XXX auch hier m�ssten F�hrstrecken ausgeschlossen werden... wie?
15121    my $v_rel;
15122    if ($bikepwr && $etappe) {
15123	my $wind; # Berechnung des Gegenwindes
15124	{
15125	    local $^W = 0;
15126	    if ($hw >= 2) {
15127		$wind = -$wind_v;
15128	    } elsif ($hw > 0) { # unsicher beim Crosswind
15129		$wind = -$wind_v*0.7;
15130	    } elsif ($hw > -2) {
15131		$wind = $wind_v*0.7;
15132	    } else {
15133		$wind = $wind_v;
15134	    }
15135	}
15136
15137	# Verh�ltnis zwischen der m�glichen Geschwindigkeit, die ohne
15138	# Gegenwind und Steigung erreicht werden kann, und der tats�chlich
15139	# erreichten
15140
15141	for(my $i = 0; $i <= $#power; $i++) {
15142
15143	    # In diesem Abschnitt wird versucht, eine Steigung zu finden.
15144	    # Wenn %hoehe nicht eingelesen wurde, passiert nichts.
15145	    # Wenn die H�hen von beiden Etappenpunkten definiert ist, kann
15146	    # die Steigung trivial errechnet werden. Wenn nur die H�he des
15147	    # Etappenzielpunktes bekannt ist, wird nachgeguckt, ob in den
15148	    # bisherigen Etappenstartpunkten die H�he bekannt ist, und
15149	    # bei Erfolg eine Durchschnittssteigung errechnet.
15150	    my($prev_x, $prev_y) = @{$realcoords[-2]};
15151	    my $grade;
15152	    my @grade_symbol_pos;
15153	    my $prev_hoehe = $hoehe{"$prev_x,$prev_y"};
15154	    my $this_hoehe = $hoehe{"$x,$y"};
15155	    my $grade_length = $etappe;
15156	    if ($use_hoehe && defined $this_hoehe) {
15157		if (defined $prev_hoehe) {
15158		    $grade = ($this_hoehe-$prev_hoehe)/$grade_length;
15159		    @grade_symbol_pos = (int(($xx-$prex)/2+$prex)+1,
15160					 int(($yy-$prey)/2+$prey)+1);
15161		} else {
15162		    for(my $j = $#{$bikepwr_all_time[$i]}; $j >= 0; $j--) {
15163			if (defined $bikepwr_all_time[$i]->[$j][3]) {
15164			    my @grade_line;
15165			    for(my $k = $j;
15166				$k <= $#{$bikepwr_all_time[$i]}; $k++) {
15167				$grade_length +=
15168				  $bikepwr_all_time[$i]->[$k][2];
15169				push @grade_line, @{$coords[$k]};
15170			    }
15171			    push @grade_line, $prex, $prey, $xx, $yy;
15172			    @grade_symbol_pos = get_polyline_center(@grade_line);
15173			    # XXX ist $etappe (und damit $grade_length)
15174			    # immer != 0?
15175			    $grade =
15176			      ($this_hoehe-$bikepwr_all_time[$i]->[$j][3])
15177				/ $grade_length;
15178			    for(my $k = $j;
15179				$k <= $#{$bikepwr_all_time[$i]}; $k++) {
15180				$bikepwr_all_time[$i]->[$k][4] = $grade;
15181			    }
15182			    last;
15183			}
15184		    }
15185		}
15186	    }
15187
15188	    # XXX m�glicherweise Performance-Killer bei reverse_route()?
15189	    # Caching verwenden?
15190	    my($current_v, $current_C) = bikepwr_get_v($wind, $i, $grade);
15191	    if ($coloring eq 'power' && $i == 0) {
15192		$v_rel = (bikepwr_get_v(0, $i, 0))[0] / $current_v;
15193	    }
15194	    my $bikepwr_time_etappe = $etappe / $current_v;
15195	    $bikepwr_time[$i] += $bikepwr_time_etappe;
15196	    my $bikepwr_cal_etappe = ($bikepwr_time_etappe
15197				      ? $current_C*($bikepwr_time_etappe/3600)
15198				      : 0);
15199	    $bikepwr_cal[$i] += $bikepwr_cal_etappe;
15200
15201	    if (keys %active_speed_power &&
15202		$active_speed_power{Type} eq "power" &&
15203		$i == $active_speed_power{Index}) {
15204		if (!$nr) {
15205		    $route_time[0] = 0;
15206		} else {
15207		    $route_time[$nr-1] = 0 if !defined $route_time[$nr-1];
15208		    $route_time[$nr]
15209		      = $route_time[$nr-1] + $bikepwr_time_etappe;
15210		}
15211		if (%ampeln && $ampeln{"$x,$y"}) {
15212		    $route_time[$nr] += $lost_time_per_ampel{X}; # XXX F...
15213		}
15214	    }
15215
15216	    my $grade_direction;
15217	    if ($show_grade && $i == 0) {
15218		if (!defined $grade) {
15219		    make_comments_net() if !$comments_net;
15220
15221		    if ($comments_net) {
15222			for my $cat (@{ $comments_net->{Net}{"$prev_x,$prev_y"}{"$x,$y"} }) {
15223			    if ($cat =~ /^(St|Gf)/) {
15224				$grade_direction = $1 eq 'St' ? +1 : -1;
15225				last;
15226			    }
15227			}
15228			if ($grade_direction) {
15229			    @grade_symbol_pos = get_polyline_center($prex, $prey, $xx, $yy);
15230			    my $r = $comments_net->get_street_record("$prev_x,$prev_y",
15231								     "$x,$y");
15232			    if ($r && $r->[Strassen::NAME] =~ /(\d+)%/) {
15233				$grade = $1 * $grade_direction;
15234			    }
15235			    $grade_length = Strassen::Util::strecke
15236				([$prev_x,$prev_y],[$x,$y]);
15237			}
15238		    }
15239		}
15240		if ((defined $grade &&
15241		     (($grade_length >= $grade_minimum_short_length && abs($grade) >= $grade_minimum) ||
15242		      ($grade_length < $grade_minimum_short_length && abs($grade) >= $grade_minimum_short))) ||
15243		    (!defined $grade && defined $grade_direction)) {
15244		    $c->createImage
15245			(@grade_symbol_pos,
15246			 -image => ((defined $grade_direction && $grade_direction > 0) || (defined $grade && $grade > 0) ? $steigung_photo : $gefaelle_photo),
15247			 -anchor => 's',
15248			 -tags => ['route', "route-$nr"],
15249			);
15250
15251		    if (defined $grade) {
15252			outline_text($c,
15253				     @grade_symbol_pos,
15254				     -font => $font{'small'},
15255				     -text => float_prec($grade*100, 1) . '%',
15256				     -tags => ['route', "route-$nr"],
15257				     -outlinewidth => 1,
15258				     -anchor => 'nw');
15259		    }
15260		}
15261	    }
15262
15263	    # Format einer Etappe von @bikepower_all_time
15264	    # 0: Zeit f�r die jeweilige Etappe
15265	    # 1: Gegenwindgeschwindigkeit (crosswind mit eingerechnet)
15266	    # 2: L�nge der Etappe
15267	    # 3: H�he des Etappenstartpunktes
15268	    # 4: Steigung der Etappe
15269	    # 5: Kalorienverbrauch
15270	    my @etappe_def = ($bikepwr_time_etappe, $wind, $etappe,
15271			      $prev_hoehe, $grade, $bikepwr_cal_etappe);
15272	    push(@{$bikepwr_all_time[$i]}, \@etappe_def);
15273	    # XXX bikepwr_all_time in dieser Form
15274	    # ist eigentlich ineffizient, da nur
15275	    # die Zeit f�r die verschiedenen "Power"s unterschiedlich ist,
15276	    # die anderen Daten aber alle gleich.
15277	}
15278    }
15279
15280    if (keys %active_speed_power &&
15281	$active_speed_power{Type} eq "speed") {
15282	my $i = $active_speed_power{Index};
15283	if (!$nr) {
15284	    $route_time[$nr] = 0;
15285	} else {
15286	    $route_time[$nr-1] = 0 if !defined $route_time[$nr-1];
15287	    $route_time[$nr]
15288	      = $route_time[$nr-1] + ($etappe / 1000) / $speed[$i] * 3600;
15289	}
15290	if (%ampeln && $ampeln{"$x,$y"}) {
15291	    $route_time[$nr] += $lost_time_per_ampel{X}; # XXX F ...
15292	}
15293    }
15294
15295    my $col;
15296    if ($coloring eq 'power' && defined $v_rel) {
15297	if    ($v_rel >= 2)    { $col = $wind_colors{-2}->[WIND_COLOR_NAME] }
15298	elsif ($v_rel >= 1.3)  { $col = $wind_colors{-1}->[WIND_COLOR_NAME] }
15299	elsif ($v_rel >= 0.77) { $col = $wind_colors{0}->[WIND_COLOR_NAME] }
15300	elsif ($v_rel >= 0.5)  { $col = $wind_colors{1}->[WIND_COLOR_NAME] }
15301	else                   { $col = $wind_colors{2}->[WIND_COLOR_NAME] }
15302    } elsif ($wind && $coloring eq 'wind') {
15303	$col = $wind_colors{$hw}->[WIND_COLOR_NAME];
15304    } elsif ($coloring =~ /^(wind|power)$/) {
15305	$col = 'red';
15306    } else {
15307	$col = $coloring; # red oder blue
15308    }
15309    $c->itemconfigure($curr_line, -fill => $col) if defined $col;
15310
15311    if (!$nr) {
15312	$route_distance[0] = 0;
15313    } else {
15314	$route_distance[$nr-1] = 0 if !defined $route_distance[$nr-1];
15315	$route_distance[$nr]   = $route_distance[$nr-1] + $etappe;
15316    }
15317
15318## DEBUG_BEGIN
15319#benchend();
15320## DEBUG_END
15321
15322    1;
15323}
15324
15325### AutoLoad Sub
15326sub get_route_color {
15327    my($value, $min_value, $max_value, $min_index, $max_index) = @_;
15328#    my $r = $wind_color{$min_value}
15329}
15330
15331### AutoLoad Sub
15332sub set_flag {
15333    my($type, $x, $y, $leaveold) = @_;
15334    $c->delete($type . 'flag') unless $leaveold;
15335    if ($do_flag{$type} && $flag_photo{$type}) {
15336	if ($type eq 'start' && !defined $x) {
15337	    ($x, $y) = @{$coords[0]};
15338	} elsif ($type eq 'ziel') {
15339	    return if (@coords < 2);
15340	    ($x, $y) = @{$coords[-1]};
15341	} elsif ($type eq 'via') {
15342	    require BBBikeVia; # XXX should not be necessary
15343	    BBBikeVia::show_via_flags();
15344	    return;
15345	}
15346	# XXX $nr may or may not be meaningful here
15347	$c->createImage($x, $y,
15348			-anchor => 'c',
15349			-image => $flag_photo{$type},
15350			-tags => ['route', "route-$nr", $type . 'flag']);
15351    }
15352}
15353
15354sub skalarprodukt {
15355    my($a1, $a2, $b1, $b2) = @_;
15356    $a1*$b1 + $a2*$b2;
15357}
15358
15359# Eingabe: Gerade mit zwei Endpunkten (Q und R) und Punkt P
15360# Ausgabe: Fu�punkt des Lotes vom Punkt auf die Gerade
15361sub fusspunkt {
15362    my($q1, $q2, $r1, $r2, $p1, $p2) = @_;
15363    my($a1, $a2) = ($r1-$q1, $r2-$q2); # Richtungsvektor berechnen
15364    my $a_sqr = skalarprodukt($a1, $a2, $a1, $a2);
15365    return undef if $a_sqr == 0;
15366    my $zaehler = skalarprodukt($p1-$q1, $p2-$q2, $a1, $a2);
15367    my $t_f = $zaehler / $a_sqr;
15368    ($q1 + $t_f * $a1, $q2 + $t_f * $a2);
15369}
15370
15371### AutoLoad Sub
15372sub recalc_bikepwr {
15373    my $power_cache = {};
15374    for(my $i = 0; $i <= $#power; $i++) {
15375	$bikepwr_time[$i] = 0;
15376	$bikepwr_cal[$i] = 0;
15377	foreach (@{$bikepwr_all_time[$i]}) {
15378	    my $wind  = $_->[1];
15379	    my $grade = $_->[4];
15380	    my($v, $C) = bikepwr_get_v($wind, $i, $grade, $power_cache);
15381	    my $bikepwr_time_etappe = ($_->[2] / $v);
15382	    $bikepwr_time[$i] += $bikepwr_time_etappe;
15383	    my $bikepwr_cal_etappe = ($bikepwr_time_etappe
15384				      ? $C*($bikepwr_time_etappe/3600)
15385				      : 0);
15386	    $bikepwr_cal[$i] += $bikepwr_cal_etappe;
15387	    $_->[0] = $bikepwr_time_etappe;
15388	    $_->[5] = $bikepwr_cal_etappe;
15389	}
15390    }
15391}
15392
15393### AutoLoad Sub
15394sub set_corresponding_power {
15395    @power = ();
15396    for(my $i = 0; $i<=$#speed; $i++) {
15397	my $bp_speed = new BikePower;
15398	$bp_speed->given('v');
15399	$bp_speed->velocity($speed[$i]/3.6);
15400	$bp_speed->calc;
15401	push @power, int($bp_speed->power);
15402    }
15403    if (!@power) {
15404	@power = (50, 100);
15405    }
15406}
15407
15408### AutoLoad Sub
15409sub redraw_path {
15410    destroy_delayed_restack();
15411    IncBusy($top);
15412    eval {
15413	my @oldcoords = @coords;
15414	my @oldrealcoords = @realcoords;
15415	my @oldsearchroutepoints = @search_route_points; # hack
15416	resetroute();
15417	addpoints_xy(\@oldrealcoords, -canvascoords => \@oldcoords);
15418	@search_route_points = @oldsearchroutepoints;
15419	set_flag('via');
15420	set_flag('ziel');
15421	updatekm();
15422	if (!defined $last_route_below || $last_route_below ne $route_below) {
15423	    if ($route_below) {
15424		# Hmmm, need to make sure it's over wasser+flaechen XXX
15425		#XXXspecial_lower("route", "delay-restack");
15426		set_in_stack('route', 'above', '*landuse*');
15427	    } else {
15428		set_in_stack('route', 'above', '*route*');
15429	    }
15430	    $last_route_below = $route_below;
15431	}
15432	restack_delayed();
15433    };
15434    DecBusy($top);
15435}
15436
15437# Einfaches Umdrehen der Route (kein echter R�ckweg!)
15438### AutoLoad Sub
15439sub reverse_route {
15440    destroy_delayed_restack();
15441    IncBusy($top);
15442    eval {
15443	my @newcoords = reverse @coords;
15444	my @newrealcoords = reverse @realcoords;
15445	@search_route_points = reverse @search_route_points;
15446	resetroute();
15447	addpoints_xy(\@newrealcoords, -canvascoords => \@newcoords);
15448	set_flag('via');
15449	set_flag('ziel');
15450	updatekm();
15451	if ($show_strlist) {
15452	    show_route_strname();
15453	}
15454	clear_undecided_temp_blockings();
15455	check_path_in_blockings_net(\@realcoords);
15456	restack_delayed();
15457    };
15458    warn __LINE__ . ": $@" if $@;
15459    DecBusy($top);
15460}
15461
15462# Echte Berechnung des R�ckwegs
15463### AutoLoad Sub
15464sub way_back {
15465    return if @search_route_points < 2;
15466    @search_route_points = reverse @search_route_points;
15467    for(my $i=$#search_route_points-1; $i >= 0; $i--) {
15468	$search_route_points[$i+1]->[SRP_TYPE] = $search_route_points[$i]->[SRP_TYPE];
15469    }
15470    $search_route_points[0]->[SRP_TYPE] = POINT_MANUELL;
15471    re_search(-undo => 0);
15472    update_route_strname();
15473}
15474
15475### AutoLoad Sub
15476sub way_back_gui {
15477    IncBusy($top);
15478    eval { way_back() };
15479    warn $@ if $@;
15480    DecBusy($top);
15481}
15482
15483sub delete_route {
15484    reset_button_command();
15485    if (@inslauf_selection || @ext_selection) {
15486	require BBBikeAdvanced;
15487	reset_selection();
15488    }
15489    update_route_strname(); # XXX => hook
15490    if ($map_mode =~ m{^(MM_VIA_MOVE
15491		       |MM_GOAL_MOVE
15492		       |MM_VIA_ADD
15493		       |MM_VIA_ADD_THEN_MOVE
15494		       |MM_VIA_DEL
15495		      )$}x) {
15496	set_map_mode(MM_SEARCH);
15497    }
15498
15499    hide_blockings_infobar();
15500
15501    Hooks::get_hooks("del_route")->execute;
15502}
15503
15504### XXX problems, see above
15505#  sub delete_route_gui_toggle {
15506#      my $menu_index = shift;
15507#      delete_route();
15508#      $top->Subwidget(PopupMenu)->entryconfigure
15509#  	($menu_index,
15510#  	 -label => M"Route wiederherstellen (Undo)",
15511#  	 -command => sub { get_undo_route_gui_toggle($menu_index) }
15512#  	);
15513#  }
15514
15515#  sub get_undo_route_gui_toggle {
15516#      my $menu_index = shift;
15517#      get_undo_route();
15518#      $top->Subwidget(PopupMenu)->entryconfigure
15519#  	($menu_index,
15520#  	 -label => M"Route l�schen",
15521#  	 -command => sub { delete_route_gui_toggle($menu_index) }
15522#  	);
15523#  }
15524
15525# Hierf�r nicht Autoload verwenden, weil es sonst *langsam* wird!
15526sub bikepwr_get_v { # Resultat in m/s
15527    my($wind, $i, $grade, $power_cache) = @_;
15528    if (!defined $bp_obj) {
15529	die "bp_obj ist nicht definiert";
15530    }
15531    $grade = 0 if !defined $grade;
15532    if (defined $power_cache and
15533	exists $power_cache->{$wind}{$i}{$grade}) {
15534	return @{ $power_cache->{$wind}{$i}{$grade} };
15535    }
15536    $bp_obj->grade($grade);
15537    $bp_obj->headwind($wind);
15538    $bp_obj->power($power[$i]);
15539    $bp_obj->calc();
15540    my $v = $bp_obj->velocity;
15541    my $C = $bp_obj->consumption;
15542    if (defined $power_cache) {
15543	$power_cache->{$wind}{$i}{$grade} = [$v, $C];
15544    }
15545    ($v, $C);
15546}
15547
15548# l�scht den letzten Punkt der Route aus @coords und Routenlinie
15549### AutoLoad Sub
15550sub dellast {
15551    my $no_update = shift;
15552    if (@realcoords) {
15553	if ($bikepwr) {
15554	    for(my $i=0; $i <= $#power; $i++) {
15555		my $etappe_def = pop(@{$bikepwr_all_time[$i]});
15556		if (ref $etappe_def eq 'ARRAY') {
15557		    $bikepwr_time[$i] -= $etappe_def->[0];
15558		    $bikepwr_cal[$i]  -= $etappe_def->[5];
15559		}
15560	    }
15561	    #for(my $i=0; $i <= $#speed; $i++) {
15562	    #XXX $bikepwr_cal_spd[$i]  -= $etappe_def->[6];
15563	    #}
15564	}
15565	@act_search_route = (); # XXX performance hit bei langen Strecken
15566	pop @coords;
15567	my $ref = pop @realcoords;
15568	my $x = $ref->[0];
15569	my $y = $ref->[1];
15570	my $xy = "$x,$y";
15571	if (@realcoords) {
15572	    # F�hrstrecken ausschlie�en
15573	CHECK_NO_FERRY: {
15574		if ($net) {
15575		    my $xy0 = join(",", @{$realcoords[-1]});
15576		    my $name = $net->{Net2Name}{$xy0}{$xy} ||
15577			       $net->{Net2Name}{$xy}{$xy0};
15578		    if (defined $name && $name =~ /^F�hre /) {
15579			last CHECK_NO_FERRY;
15580		    }
15581		}
15582
15583		$strecke -= sqrt(sqr($realcoords[-1]->[0] - $x) +
15584				 sqr($realcoords[-1]->[1] - $y));
15585	    }
15586	}
15587
15588	# Via l�schen, und zwar im aktuellen und im vorherigen Punkt ???
15589	if (@search_route_points) {
15590	    my $last_via = $search_route_points[-1]->[SRP_COORD];
15591	    if ($xy eq $last_via) {
15592		pop @search_route_points;
15593	    }
15594	}
15595
15596	$c->delete("route-$nr");
15597	$nr--;
15598	unless ($no_update) {
15599	    update_flags_and_route();
15600	}
15601	if ($map_mode eq MM_BUTTONPOINT) { # update also selection
15602	    if (@inslauf_selection) {
15603		pop @inslauf_selection;
15604		update_clipboard();
15605	    }
15606	}
15607	check_path_in_blockings_net(\@realcoords);
15608    }
15609}
15610
15611sub update_flags_and_route {
15612    set_flag('via');
15613    set_flag('ziel');
15614    updatekm();
15615    if ($map_mode eq MM_SEARCH && !@coords) {
15616	undef $search_route_flag;
15617	search_route_mouse(1);
15618    }
15619    update_route_strname();
15620}
15621
15622### AutoLoad Sub
15623sub update_clipboard {
15624    if ($use_clipboard) {
15625	$c->clipboardClear;
15626	# Use a leading space, to be consistent with rest of (lazy) clipboard
15627	# code.
15628	$c->clipboardAppend(" " . join(" ", @inslauf_selection));
15629    }
15630}
15631
15632# bis zum letzten Via l�schen
15633### AutoLoad Sub
15634sub deltovia {
15635    return if !@realcoords || !@search_route_points;
15636    # Zuerst wird �berpr�ft, ob der letzte Punkt ein Via-Punkt ist. In
15637    # diesem Fall wird diese Tatsache ignoriert und der Punkt wird
15638    # gel�scht.
15639    my $via = $search_route_points[-1]->[SRP_COORD];
15640    my($x, $y) = @{ $realcoords[-1] };
15641    my $xy = "$x,$y";
15642    if ($xy eq $via) {
15643	dellast();
15644    }
15645    goto CLEANUP if !@realcoords;
15646    goto CLEANUP if (!@search_route_points);
15647    $via = $search_route_points[-1]->[SRP_COORD];
15648    for(my $i = $#realcoords; $i >= 0; $i--) {
15649	my($x, $y) = @{ $realcoords[$i] };
15650	my $xy = "$x,$y";
15651	if ($xy eq $via) {
15652	    update_flags_and_route();
15653	    goto CLEANUP;
15654	} else {
15655	    dellast(1);
15656	}
15657    }
15658  CLEANUP:
15659    update_clipboard();
15660}
15661
15662# Ausgabe der aktuellen Routenl�nge
15663sub updatekm {
15664    return if !@realcoords;
15665
15666    my $lost_time_s;
15667    if (%ampeln) {
15668	my $ampel_count = 0;
15669	foreach (@realcoords) {
15670	    if ($ampeln{$_->[0].",".$_->[1]}) {
15671		$ampel_count++;
15672	    }
15673	}
15674	if ($ampel_count == 0) {
15675	    $ampelstatus_label_text = M"Keine Ampeln";
15676	} else {
15677	    $lost_time_s = $ampel_count*$lost_time_per_ampel{X}; # XXX F ...
15678	    $ampelstatus_label_text =
15679		"$ampel_count " .
15680		    ($ampel_count > 1 ? M"Ampeln" : M"Ampel") .
15681			" (-" . s2hm_or_s($lost_time_s) . ")";
15682	}
15683    } else {
15684	$ampelstatus_label_text = "";
15685    }
15686
15687    my $lost_time_tragen_s = 0;
15688    my $lost_time_narrowpassage_s = 0;
15689    if (%sperre_tragen || %sperre_narrowpassage) {
15690	my $tragen_count = 0;
15691	foreach (@realcoords) {
15692	    my $c = $_->[0].",".$_->[1];
15693	    if (exists $sperre_tragen{$c}) {
15694		$lost_time_tragen_s += $sperre_tragen{$c};
15695		$tragen_count++;
15696	    } elsif (exists $sperre_narrowpassage{$c}) {
15697		$lost_time_narrowpassage_s += $sperre_narrowpassage{$c};
15698		# XXX don't count
15699	    }
15700	}
15701	if ($lost_time_tragen_s) {
15702	    $ampelstatus_label_text .=
15703		"\n" .
15704		    Mfmt("%dx tragen", $tragen_count) .
15705			" (-" . s2hm_or_s($lost_time_tragen_s) . ")";
15706	}
15707    }
15708
15709    my @time_h;
15710    for(my $i = 0; $i <= $#speed; $i++) {
15711	# XXX implement something similar for "power", too!
15712	if ($kopfstein_count->{"speed"}[$i]) {
15713	    make_handicap_net();
15714	    make_qualitaet_net();
15715	    $time_h[$i] = 0;
15716	    if ($#realcoords > 0) {
15717		for(my $ii=0; $ii<$#realcoords; $ii++) {
15718		    my $s = Strassen::Util::strecke($realcoords[$ii],$realcoords[$ii+1]);
15719		    my @etappe_speeds = $speed[$i];
15720		    if ($qualitaet_s_net && (my $cat = $qualitaet_s_net->{Net}{join(",",@{$realcoords[$ii]})}{join(",",@{$realcoords[$ii+1]})})) {
15721			push @etappe_speeds, $qualitaet_s_speed{$cat}
15722			    if defined $qualitaet_s_speed{$cat};
15723		    }
15724		    if ($handicap_s_net && (my $cat = $handicap_s_net->{Net}{join(",",@{$realcoords[$ii]})}{join(",",@{$realcoords[$ii+1]})})) {
15725			push @etappe_speeds, $handicap_s_speed{$cat}
15726			    if defined $handicap_s_speed{$cat};
15727		    }
15728		    $time_h[$i] += ($s/1000)/min(@etappe_speeds);
15729		}
15730	    }
15731	} else {
15732	    $time_h[$i] = ($strecke / 1000) / $speed[$i];
15733	}
15734    }
15735    my $dir_strecke =
15736      sqrt(sqr($realcoords[0]->[0] - $realcoords[-1]->[0]) +
15737	   sqr($realcoords[0]->[1] - $realcoords[-1]->[1]));
15738    if ($unit_s eq 'm') {
15739	$act_value{Km} = sprintf "%d", $scale_coeff * $strecke;
15740    } elsif ($unit_s eq 'mi') {
15741	$act_value{Km} = float_prec($scale_coeff * $strecke/1609.344, 1);
15742    } else {
15743	$act_value{Km} = float_prec($scale_coeff * $strecke/1000, 1);
15744    }
15745    $act_value{Percent} = ($dir_strecke != 0
15746			   ? do {
15747			       my $p = int(($strecke/$dir_strecke)*100)-100;
15748			       # wenn 1000% erreicht sind, ist es sicher
15749			       # eine Rundfahrt, und da ist eine Prozent-
15750			       # angabe unsinnig
15751			       $p < 1000 ? $p : "";
15752			   }
15753			   : "");
15754    for(my $i = 0; $i <= $#speed; $i++) {
15755	my $time_h = $time_h[$i] +
15756	  (defined $lost_time_s && $ampel_count->{"speed"}[$i]
15757	   ? $lost_time_s/3600 : 0);
15758	$time_h += ($lost_time_tragen_s+$lost_time_narrowpassage_s)/3600;
15759	my $time_s = $time_h*3600;
15760	$act_value{TimeSeconds}->[$i] = $time_s;
15761	$act_value{Time}->[$i] = s2hm_or_s($time_s);
15762    }
15763
15764    if ($bikepwr) {
15765	for(my $i = 0; $i <= $#power; $i++) {
15766	    my $time = $bikepwr_time[$i] +
15767	      (defined $lost_time_s && $ampel_count->{"power"}[$i]
15768	       ? $lost_time_s : 0);
15769	    $time += ($lost_time_tragen_s+$lost_time_narrowpassage_s)/3600;
15770	    $act_value{PowerTimeSeconds}->[$i] = $time;
15771	    $act_value{PowerTime}->[$i] = s2hm_or_s($time);
15772	    if (!$edit_mode && !$edit_normal_mode) {
15773		$calories_power[$i] = float_prec($bikepwr_cal[$i], 1);
15774	    } else {
15775		$calories_power[$i] = undef;
15776	    }
15777	}
15778#XXX
15779# 	for(my $i = 0; $i <= $#speed; $i++) {
15780# 	    if (!$edit_mode && !$edit_normal_mode) {
15781# 		$calories_speed[$i] = float_prec($bikepwr_cal_spd[$i], 1);
15782# 	    } else {
15783# 		$calories_speed[$i] = undef;
15784# 	    }
15785# 	}
15786    }
15787
15788    # XXX hier?
15789    Hooks::get_hooks("new_route")->execute;
15790}
15791
15792# l�scht die Route (Liste, Linie, m�gliche tempor�re Blockings)
15793sub resetroute {
15794    $strecke = 0;
15795    $act_value{Km} = "";
15796    $act_value{Percent} = "";
15797    for(my $i = 0; $i <= $#speed; $i++) {
15798	$act_value{TimeSeconds}->[$i] = undef;
15799	$act_value{Time}->[$i] = "";
15800	#XXX $bikepwr_cal_spd[$i] = 0;
15801    }
15802    @realcoords = @coords = @search_route_points = ();
15803
15804    if ($bikepwr) {
15805	for(my $i = 0; $i <= $#power; $i++) {
15806	    @{$bikepwr_all_time[$i]} = ();
15807	    $bikepwr_time[$i] = 0;
15808	    $bikepwr_cal[$i] = 0;
15809	    $act_value{PowerTimeSeconds}->[$i] = undef;
15810	    $act_value{PowerTime}->[$i] = "";
15811	}
15812    }
15813
15814    $ampelstatus_label_text = "";
15815    $c->delete('route');
15816    $nr = -1;
15817    $next_is_undo = 0;
15818    @act_search_route = ();
15819    update_route_strname();
15820
15821    if (@inslauf_selection || @ext_selection) {
15822	require BBBikeAdvanced;
15823	reset_selection();
15824    }
15825}
15826
15827sub reset_undo_route {
15828    if (@realcoords) {
15829	save_route_to_register(0);
15830    }
15831
15832    resetroute();
15833}
15834
15835### AutoLoad Sub
15836sub get_undo_route {
15837    get_route_from_register(0);
15838}
15839
15840### AutoLoad Sub
15841sub save_route_to_register {
15842    my($register) = @_;
15843    my $r = {};
15844    $r->{RealCoords}        = [@realcoords];
15845    $r->{SearchRoutePoints} = [@search_route_points];
15846
15847    if ($bikepwr) {
15848	for(my $i = 0; $i <= $#power; $i++) {
15849	    if (defined $bikepwr_all_time[$i]) {
15850		@{ $r->{BikepwrAllTime}[$i] } = @{ $bikepwr_all_time[$i] }
15851	    }
15852	    $r->{BikepwrTime}[$i] = $bikepwr_time[$i];
15853	    $r->{BikepwrCal}[$i]  = $bikepwr_cal[$i];
15854	}
15855# 	for(my $i = 0; $i <= $#speed; $i++) {
15856# 	    $r->{BikepwrCalSpd}[$i]  = $bikepwr_cal_spd[$i];
15857# 	}
15858
15859    }
15860    $r->{Nr} = $nr;
15861
15862    $save_route{$register} = $r;
15863}
15864
15865# Return false if there is no route in this register.
15866### AutoLoad Sub
15867sub get_route_from_register {
15868    my($register) = @_;
15869    if (!$save_route{$register}) {
15870	return 0;
15871    }
15872    my $r = $save_route{$register};
15873
15874    @realcoords       = @{ $r->{RealCoords}     };
15875    realcoords2coords();
15876    @search_route_points = @{ $r->{SearchRoutePoints} };
15877    restore_search_route_points();
15878
15879    if ($bikepwr) {
15880	for(my $i = 0; $i <= $#power; $i++) {
15881	    if (defined $r->{BikepwrAllTime}[$i]) {
15882		@{ $bikepwr_all_time[$i] } = @{ $r->{BikepwrAllTime}[$i] }
15883	    }
15884	    $bikepwr_time[$i] = $r->{BikepwrTime}[$i];
15885	    $bikepwr_cal[$i]  = $r->{BikepwrCal}[$i];
15886	}
15887# 	for(my $i = 0; $i <= $#speed; $i++) {
15888# 	    $bikepwr_cal_spd[$i]  = $r->{BikepwrCalSpd}[$i];
15889# 	}
15890    }
15891    $nr = $r->{Nr};
15892
15893    redraw_path();
15894    update_route_strname();
15895
15896    1;
15897}
15898
15899sub restore_search_route_points {
15900    if ($net) {
15901	for (@search_route_points) {
15902	    add_new_point($net, $_->[SRP_COORD], -quiet => 1);
15903	}
15904    }
15905}
15906
15907sub set_canvas_scale {
15908    my $s = shift;
15909    $scale = $s;
15910    eval { set_canvas_scale_XS($s) };
15911    create_transpose_subs();
15912}
15913
15914### AutoLoad Sub
15915sub scalecanvas {
15916    my($c, $scalefactor, $x, $y, %args) = @_;
15917    my(@oldx) = $c->xview;
15918    my(@oldy) = $c->yview;
15919    my($xwidth) = $oldx[1]-$oldx[0];
15920    my($ywidth) = $oldy[1]-$oldy[0];
15921    my($sr_x0, $sr_y0, $sr_x1, $sr_y1) = ($Tk::VERSION == 800.017
15922					  ? $c->cget(-scrollregion)
15923					  : @{$c->cget(-scrollregion)});
15924    my($rx,$ry);
15925    if (defined $x && defined $y) {
15926	($rx, $ry) = ($c->rootx + $c->widgetx($x),
15927		      $c->rooty + $c->widgety($y));
15928    }
15929
15930    # Initialisieren (muss als erstes kommen)
15931    show_zoomrect() if $scalefactor < 1 and not $args{-fast};
15932
15933    IncBusy($top);
15934    eval {
15935	my $old_scale = $scale;
15936	set_canvas_scale($scale * $scalefactor);
15937	$c->scale('all', 0, 0, $scalefactor, $scalefactor);
15938	calc_mapscale();
15939	scale_width($c, $scale, $old_scale);
15940	change_category_visibility($c, $scale, $old_scale);
15941
15942	foreach (@scrollregion) { $_ *= $scalefactor }
15943	$c->configure(-scrollregion => \@scrollregion);
15944	foreach (@coords) {
15945	    $_->[0] *= $scalefactor;
15946	    $_->[1] *= $scalefactor;
15947	}
15948	foreach (@route_strnames) {
15949	    $_->[1] *= $scalefactor;
15950	    $_->[2] *= $scalefactor;
15951	}
15952
15953	scale_coords($c, $scale, $old_scale);
15954	scale_maps($scalefactor);
15955
15956	if (defined $x && defined $y) {
15957	    # preserve position under cursor
15958	    $c->scroll_canvasxy_to_rootxy($x*$scalefactor,$y*$scalefactor,$rx,$ry);
15959	} else {
15960	    # in die Mitte des vorherigen Ausschnitts positionieren
15961	    $c->xview('moveto' => $oldx[0]+($xwidth-$xwidth/$scalefactor)/2);
15962	    $c->yview('moveto' => $oldy[0]+($ywidth-$ywidth/$scalefactor)/2);
15963	}
15964
15965	overview_update();
15966    };
15967    warn $@ if $@;
15968    DecBusy($top);
15969
15970    # Zoomrect starten
15971    show_zoomrect(1) if $scalefactor < 1 and not $args{-fast};
15972
15973    Hooks::get_hooks("after_resize")->execute($scalefactor);
15974}
15975
15976sub scalecanvas_from_canvas_event {
15977    my($c, $scalefactor) = @_;
15978    my $e = $c->XEvent;
15979    return unless $e;
15980    my($x, $y) = ($c->canvasx($e->x),
15981		  $c->canvasy($e->y));
15982    scalecanvas($c, $scalefactor, $x, $y);
15983}
15984
15985### AutoLoad Sub
15986sub scale_width {
15987    my($c, $scale, $old_scale) = @_;
15988
15989# XXX scale obst (mehrere Icon-Gr��en)
15990    foreach my $type
15991	(qw(s-BAB sBAB-BAB s-HH s-B s-H s-NH s-N s-NN
15992	    SBAB-BAB-out s-HH-out s-B-out s-H-out s-NH-out s-N-out s-NN-out
15993	    rw
15994	    w-W w-W0 w-W1 w-W2 w-W-out w-W0-out w-W1-out w-W2-out wr
15995	    l l-out u b r pp p z g gP gD gBO fz
15996	    sperre0 sperre1 sperre1s sperre2)) {
15997	eval {
15998	CHANGE: {
15999		my $new_width = get_line_width($type, $scale);
16000		if (defined $old_scale) {
16001		    my $old_width = get_line_width($type, $old_scale);
16002		    last CHANGE if ($new_width == $old_width);
16003		}
16004		if ($type =~ /^(sperre|fz)/) {
16005		    # special handling to filter out images:
16006		    foreach my $item ($c->find("withtag", $type)) {
16007			$c->itemconfigure($item, -width => $new_width)
16008			    unless $c->type($item) eq 'image';
16009		    }
16010		} elsif ($type =~ /^w-.*-out$/) {
16011		    foreach my $item ($c->find("withtag", $type)) {
16012			$c->itemconfigure($item, -width => $new_width)
16013			    unless $c->type($item) eq 'polygon';
16014		    }
16015		} else {
16016		    $c->itemconfigure($type, -width => $new_width);
16017		}
16018	    }
16019	};
16020	if ($@) {
16021	    warn "Error while configuring $type in scale_width: $@";
16022	}
16023    }
16024    foreach my $sperre_type (qw(sperre1 sperre1s sperre2)) {
16025	my $new_width = get_line_width($sperre_type);
16026	my $old_width = get_line_width($sperre_type, $old_scale);
16027	if ($new_width != $old_width) {
16028	    foreach my $item ($c->find("withtag", $sperre_type)) {
16029		if ($c->type($item) ne 'image') {
16030		    $c->itemconfigure
16031			($item,
16032			 -fill => ($new_width == 0
16033				   ? undef : $category_color{$sperre_type}));
16034		}
16035	    }
16036	}
16037
16038	##XXX Works, but maybe it's better to put the code snippets of
16039	##plot_sperre into strings to be evaled, used in plot_sperre
16040	##and re-used here.
16041	# XXX adjust and move to scale_coords?
16042 	if ($sperre_type =~ /^sperre[12]/) {
16043 	    my $new_length = get_line_length($sperre_type);
16044	    my $old_length = get_line_length($sperre_type, $old_scale) * $scale/$old_scale;
16045	    if ($old_length) { # XXX when may $old_length be 0?
16046		my $f = $new_length / $old_length;
16047		foreach my $item ($c->find("withtag", $sperre_type)) {
16048		    if ($c->type($item) ne 'image') {
16049			my($x1,$y1,$x2,$y2) = $c->coords($item);
16050			my($xm,$ym) = (($x2+$x1)/2, ($y2+$y1)/2);
16051			my $xd1 = $x1-$xm;
16052			my $xd2 = $x2-$xm;
16053			my $yd1 = $y1-$ym;
16054			my $yd2 = $y2-$ym;
16055			$c->coords($item,
16056				   $xm+$xd1*$f, $ym+$yd1*$f,
16057				   $xm+$xd2*$f, $ym+$yd2*$f,
16058				  );
16059		    }
16060 		}
16061 	    }
16062	}
16063    }
16064
16065    foreach (qw(lsa-X lsa-B lsa-B0 lsa-F lsa-Zbr rest kn vf-Vf vf-Kz u b), ($XXX_use_old_R_symbol ? () : ('r'))) {
16066	$c->itemconfigure($_ . '-fg && !attrib-inwork', -image => get_symbol_scale($_, $scale));
16067    }
16068    foreach (qw(u-U0 u-UBau b-S0 b-SBau r-R0 r-RBau r-RP)) { # overwrite the previous settings of u,b,r
16069	$c->itemconfigure($_ . '-fg', -image => get_symbol_scale($_, $scale));
16070    }
16071    foreach (qw(attrib-inwork)) {
16072	$c->itemconfigure('attrib-inwork', -image => get_symbol_scale($_, $scale));
16073    }
16074    foreach (qw(e comm-tram nl)) {
16075	$c->itemconfigure($_ . '-img', -image => get_symbol_scale($_, $scale));
16076    }
16077
16078    if ($XXX_use_old_R_symbol) {
16079	# XXX ... nur �ndern, falls sich die Skalierung �ndert... (wie oben)
16080	# XXX arrowshape von sperre1 �ndern
16081	my %arg = get_symbol_scale('r');
16082	$c->itemconfigure('r-bg', -width => $arg{-width});
16083	$c->itemconfigure("r-fg",
16084			  -text => (defined $arg{-font} ? 'R' : ''),
16085			  (defined $arg{-font} ? (-font => $arg{-font}) : ()),
16086			 );
16087    }
16088    # rearrange outline_text
16089    # XXX performance is quite bad (about 0.6s for all U+S-Bahnh�fe)
16090## DEBUG_BEGIN
16091#benchbegin("Repositioning labels");
16092## DEBUG_END
16093    # XXX adjust and move to scale_coords?
16094    foreach my $item ($c->find(withtag => 'outlmaster')) {
16095	my($x,$y) = $c->coords($item);
16096	my $outline_width = 1;
16097	my $outl_i;
16098	for ($c->gettags($item)) {
16099	    if (/^outlmaster-width-(\d+)/) {
16100		$outline_width = $1;
16101	    } elsif (/^outlmaster-(\d+)/) {
16102		$outl_i = $1;
16103	    }
16104	}
16105	if (defined $outl_i) {
16106	    # XXX the second version is a hack, but faster
16107#	    foreach my $slave ($c->find(withtag => "outlslave-$outl_i")) {
16108	    foreach my $slave ($item-(4*$outline_width)..$item-1) {
16109		# assuming last tag is outldata_$x_$y tag
16110		my @outldata = split /_/, (($c->gettags($slave))[-1]);
16111		$c->coords($slave, $x+$outldata[1],$y+$outldata[2]);
16112	    }
16113	}
16114    }
16115## DEBUG_BEGIN
16116#benchend();
16117## DEBUG_END
16118
16119    # XXX adjust and move to scale_coords?
16120    foreach my $item ($c->find(withtag => 'strnr')) {
16121	my $master = ($c->gettags($item))[2];
16122	$master =~ s/^strnr-//;
16123	my(@bbox) = $c->bbox($master);
16124	if ($c->type($item) eq 'image') {
16125	    $c->coords($item, ($bbox[0]+$bbox[2])/2, ($bbox[1]+$bbox[3])/2); # XXX this is duplicated from draw_street_numbers!
16126	} else {
16127	    $c->coords($item, $bbox[0]-2,$bbox[1]-2,$bbox[2]+2,$bbox[3]+2);
16128	}
16129    }
16130
16131    for my $o_cat (MIN_ORT_CAT .. MAX_ORT_CAT) {
16132	my $font = get_orte_label_font($o_cat);
16133	#warn "$o_cat -> " . Dumper($top->fontActual($font)) . "\n";
16134	$c->itemconfigure("O$o_cat", -font => $font);
16135    }
16136
16137    while(my($name,$scalecommand) = each %scalecommand) {
16138	warn "Scale for $name...\n";
16139	$scalecommand->($name, $c, $scale, $old_scale);
16140    }
16141}
16142
16143### AutoLoad Sub
16144sub scale_coords {
16145    my($c, $scale, $old_scale) = @_;
16146
16147    {
16148	my $new_width = get_line_width('comm-scenic-View', $scale);
16149	my $old_width = get_line_width('comm-scenic-View', $old_scale);
16150	if ($new_width != $old_width) {
16151	    foreach my $item ($c->find(withtag => 'comm-scenic-View')) {
16152		my($cx,$cy) = Strassen::Util::middle($c->coords($item));
16153		$c->coords($item,$cx-$new_width,$cy-$new_width,$cx+$new_width,$cy+$new_width);
16154	    }
16155	}
16156    }
16157}
16158
16159### AutoLoad Sub
16160sub change_place_visibility {
16161    my($c, $new_scale) = @_;
16162
16163    # XXX genaue Version f�r dash patches rauskriegen
16164    return if $Tk::VERSION < 800.021;
16165
16166    $new_scale = $scale unless defined $new_scale;
16167
16168    if ($place_category eq 'auto') {
16169	my $eff_place_category;
16170	if      ($new_scale > 0.5) {
16171	    $eff_place_category = 0;
16172	} elsif ($new_scale > 0.25) {
16173	    $eff_place_category = 1;
16174	} elsif ($new_scale > 0.18) {
16175	    $eff_place_category = 2;
16176	} elsif ($new_scale > 0.125) {
16177	    $eff_place_category = 3;
16178	} elsif ($new_scale > 0.03125) {
16179	    $eff_place_category = 4;
16180	} else {
16181	    $eff_place_category = 5;
16182	}
16183
16184	if ($eff_place_category > 0) {
16185	    for my $cat (0 .. $eff_place_category-1) {
16186		$c->itemconfigure("O$cat", -state => "hidden");
16187		$c->itemconfigure("OP$cat", -state => "hidden");
16188	    }
16189	}
16190	for my $cat ($eff_place_category .. 5) {
16191	    $c->itemconfigure("O$cat", -state => "normal");
16192	    $c->itemconfigure("OP$cat", -state => "normal");
16193	}
16194    }
16195}
16196
16197### AutoLoad Sub
16198sub change_label_visibility {
16199    my($c, $new_scale, $old_scale, $restrict) = @_;
16200
16201    # XXX genaue Version f�r dash patches rauskriegen
16202    return if $Tk::VERSION < 800.021;
16203
16204    $new_scale = $scale unless defined $new_scale;
16205
16206    my %tags = ('r-label' => 0.35,
16207		'b-label' => 1.5,
16208		'u-label' => 1.5,
16209		'v-fg'    => 1.5,
16210	       );
16211
16212    if ($restrict) {
16213	my %new_tags;
16214	for (@$restrict) {
16215	    $new_tags{$_} = $tags{$_};
16216	}
16217	%tags = %new_tags;
16218    }
16219
16220    while(my($tag, $scale_limit) = each %tags) {
16221	if ((!defined $old_scale || $old_scale >= $scale_limit) && $new_scale <= $scale_limit) {
16222	    $c->itemconfigure($tag, -state => "hidden");
16223	} elsif ((!defined $old_scale || $old_scale < $scale_limit) && $new_scale >= $scale_limit) {
16224	    $c->itemconfigure($tag, -state => "normal");
16225	}
16226    }
16227}
16228
16229### AutoLoad Sub
16230sub change_category_visibility {
16231    my($c, $scale, $old_scale) = @_;
16232
16233    {
16234	my $new_width = get_line_width('sBAB-BAB', $scale);
16235	my $old_width = get_line_width('sBAB-BAB', $old_scale);
16236	if ($new_width != $old_width) {
16237	    if ($new_width < $sBAB_two_track_width && $old_width >= $sBAB_two_track_width) {
16238		$c->itemconfigure('sBAB-fg', -state => 'hidden');
16239	    } elsif ($new_width >= $sBAB_two_track_width && $old_width < $sBAB_two_track_width) {
16240		$c->itemconfigure('sBAB-fg', -state => 'normal');
16241	    }
16242	}
16243    }
16244
16245    change_place_visibility($c, $scale);
16246    change_label_visibility($c, $scale, $old_scale);
16247
16248return 1;
16249#XXXXXXXXXXXX enable
16250# use tag_invisible for plotstr/plotp
16251# insert a checkbutton fot auto_visible
16252# str_restrict: don't set restriction on StrassenNetz
16253    for my $tag (keys %tag_visibility) {
16254	my $old_def = $tag_invisible{$tag};
16255	if ($scale <= $tag_visibility{$tag}) {
16256	    $tag_invisible{$tag} = 1;
16257	} else {
16258	    $tag_invisible{$tag} = 0;
16259	}
16260	if (defined $old_def && $old_def != $tag_invisible{$tag}
16261	    && $auto_visible) {
16262	    if ($tag =~ /^([^-]+-[^-]+)/) {
16263		pending(1, "replot-$1");
16264	    }
16265	}
16266    }
16267}
16268
16269sub get_index_by_scale {
16270    my $myscale = shift;
16271    if ($myscale < 0.5) {
16272	0;
16273    } elsif ($myscale < 1) {
16274	1;
16275    } elsif ($myscale < 2) {
16276	2;
16277    } elsif ($myscale < 5) {
16278	3;
16279    } elsif ($myscale < 10) {
16280	4;
16281    } else {
16282	5;
16283    }
16284}
16285
16286sub get_line_width {
16287    my($tag, $myscale) = @_;
16288    $myscale = $scale if !defined $myscale;
16289
16290    my $is_outline = ($tag =~ /-out$/);
16291    my $add_outline = ($is_outline
16292		       ? 2 : ($tag eq 'pp' || $tag eq 'p' ? 1 : 0));
16293    my $index = get_index_by_scale($myscale);
16294    if ($is_outline && !exists $line_width{$tag}) {
16295	$tag =~ s/-out$//;
16296    }
16297    if ($tag =~ /^L\d+/ &&
16298	defined $default_line_width && $default_line_width == 1) {
16299	1;
16300    } else {
16301	$line_width{(exists $line_width{$tag} ? $tag : 'default')}->[$index]
16302	    + $add_outline;
16303    }
16304}
16305
16306sub get_line_length {
16307    my($tag, $myscale) = @_;
16308    $myscale = $scale if !defined $myscale;
16309
16310    my $index = get_index_by_scale($myscale);
16311    $line_length{(exists $line_length{$tag} ? $tag : 'default')}->[$index];
16312}
16313
16314sub get_symbol_scale {
16315    my($tag, $myscale) = @_;
16316    $myscale = $scale if !defined $myscale;
16317    my $mod = $small_icons ? 2 : 1;
16318    if ($tag eq 'lsa-X') {
16319	if ($myscale > 4*$mod) {
16320	    return $ampel_photo;
16321	} elsif ($scale >= 2*$mod) {
16322	    return $ampel_klein_photo;
16323	} elsif ($scale >= 0.5*$mod) {
16324	    return $ampel_klein2_photo;
16325	} else {
16326	    return undef;
16327	}
16328    } elsif ($tag eq 'lsa-F') {
16329	if ($myscale > 4*$mod) {
16330	    return $ampelf_photo;
16331	} elsif ($scale >= 2*$mod) {
16332	    return $ampelf_klein_photo;
16333	} elsif ($scale >= 0.5*$mod) {
16334	    return $ampelf_klein2_photo;
16335	} else {
16336	    return undef;
16337	}
16338    } elsif ($tag =~ m{^lsa-B$}) {
16339	if ($myscale > 4*$mod) {
16340	    return $andreaskr_photo;
16341	} elsif ($scale >= 2*$mod) {
16342	    return $andreaskr_klein_photo;
16343	} elsif ($scale >= 0.5*$mod) {
16344	    return $andreaskr_klein2_photo;
16345	} else {
16346	    return undef;
16347	}
16348    } elsif ($tag =~ m{^lsa-B0$}) {
16349	if ($myscale > 4*$mod) {
16350	    return $andreaskr_grey_photo;
16351	} elsif ($scale >= 2*$mod) {
16352	    return $andreaskr_grey_klein_photo;
16353	} elsif ($scale >= 0.5*$mod) {
16354	    return $andreaskr_grey_klein2_photo;
16355	} else {
16356	    return undef;
16357	}
16358    } elsif ($tag eq 'lsa-Zbr') {
16359	if ($myscale >= 4*$mod) {
16360	    return $zugbruecke_photo;
16361	} elsif ($scale >= 1*$mod) {
16362	    return $zugbruecke_klein_photo;
16363	} else {
16364	    return undef;
16365	}
16366    } elsif ($tag eq 'kn') {
16367	if ($myscale > 4*$mod) {
16368	    return $kneipen_photo;
16369	} elsif ($scale >= 1*$mod) {
16370	    return $kneipen_klein_photo;
16371	} else {
16372	    return undef;
16373	}
16374    } elsif ($tag eq 'e') {
16375	if ($myscale > 2*$mod) {
16376	    return $ferry_photo;
16377	} elsif ($scale >= 0.5*$mod) {
16378	    return $ferry_klein_photo;
16379	} elsif ($scale >= 0.2*$mod) {
16380	    return $ferry_mini_photo;
16381	} else {
16382	    return undef;
16383	}
16384    } elsif ($tag eq 'rest') {
16385	if ($myscale > 4*$mod) {
16386	    return $essen_photo;
16387	} elsif ($scale >= 1*$mod) {
16388	    return $essen_klein_photo;
16389	} else {
16390	    return undef;
16391	}
16392    } elsif ($XXX_use_old_R_symbol && $tag eq 'r') {
16393	if ($myscale > 4*$mod) {
16394	    return (-width => 20, -font => "Helvetica -18");
16395	} elsif ($myscale >= 1*$mod) {
16396	    return (-width => 14, -font => ($os eq 'win' ? "Helvetica -14 bold" : "6x13bold"));
16397	} elsif ($scale >= 0.5*$mod) {
16398	    return (-width => 10, -font => ($os eq 'win' ? "Helvetica -10 bold" : "5x7"));
16399	} elsif ($scale >= 0.2*$mod) {
16400	    return (-width => 6, -font => undef);
16401	} else {
16402	    return (-width => 3, -font => undef);
16403	}
16404    } elsif ($tag eq 'r-RP') {
16405	my $filename;
16406	if ($myscale >= 2*$mod) {
16407	    $filename = 'eisenbahn_klein';
16408	} elsif ($myscale >= 1*$mod) {
16409	    $filename = 'eisenbahn_mini';
16410	}
16411	if ($filename) {
16412	    return load_photo($top, $filename, -persistent => 1);
16413	} else {
16414	    return undef;
16415	}
16416    } elsif ($tag eq 'r' || $tag eq 'r-R0') {
16417	my $filename;
16418	if ($myscale > 2*$mod) {
16419	    $filename = 'eisenbahn'
16420	} elsif ($myscale >= 0.5*$mod) {
16421	    $filename = 'eisenbahn_klein';
16422	} elsif ($myscale >= 0.2*$mod) {
16423	    $filename = 'eisenbahn_mini';
16424	}
16425	my $photo;
16426	if ($filename) {
16427	    if ($tag eq 'r-R0') {
16428		$filename =~ s{(eisenbahn)}{$1_stillg};
16429	    }
16430	    $photo = load_photo($top, $filename, -persistent => 1);
16431	}
16432	return $photo;
16433    } elsif ($tag eq 'u' || $tag eq 'b' || $tag eq 'u-U0' || $tag eq 'b-S0' || $tag eq 'u-UBau' || $tag eq 'b-SBau') {
16434	my $photo;
16435	my $base = $tag =~ m{^b} ? "sbahn" : "ubahn";
16436	my $filename;
16437	if ($myscale > 2*$mod) {
16438	    $filename = $base;
16439	} elsif ($myscale >= 0.5*$mod) {
16440	    $filename = $base . "_klein";
16441	} elsif ($myscale >= 0.2*$mod) {
16442	    $filename = $base . "_mini";
16443	}
16444	if ($filename) {
16445	    if ($tag =~ m{^[ub]-[US](0|Bau)$}) {
16446		$photo = load_photo($top, $filename, -persistent => 1, -palette => 256, -gamma => 3);
16447	    } else {
16448		$photo = load_photo($top, $filename, -persistent => 1);
16449	    }
16450	}
16451	return $photo;
16452    } elsif ($tag eq 'vf-Vf') {
16453	if ($myscale > 2*$mod) {
16454	    $vorfahrt_photo = load_photo($top, 'vorfahrt') if !$vorfahrt_photo;
16455	    return $vorfahrt_photo;
16456	} elsif ($scale >= 0.5*$mod) {
16457	    $vorfahrt_klein_photo = load_photo($top, 'vorfahrt_klein') if !$vorfahrt_klein_photo;
16458	    return $vorfahrt_klein_photo;
16459	} else {
16460	    return undef;
16461	}
16462    } elsif ($tag eq 'vf-Kz') {
16463	if ($myscale > 2*$mod) {
16464	    $kreuzung_photo = load_photo($top, 'kreuzung') if !$kreuzung_photo;
16465	    return $kreuzung_photo;
16466	} elsif ($scale >= 0.5*$mod) {
16467	    $kreuzung_klein_photo = load_photo($top, 'kreuzung_klein') if !$kreuzung_klein_photo;
16468	    return $kreuzung_klein_photo;
16469	} else {
16470	    return undef;
16471	}
16472    } elsif ($tag eq 'comm-tram') {
16473	my $filename;
16474	if ($myscale > 4*$mod) {
16475	    $filename = 'strassenbahn'
16476	} elsif ($myscale >= 1*$mod) {
16477	    $filename = 'strassenbahn_klein';
16478	}
16479	my $photo;
16480	if ($filename) {
16481	    $photo = load_photo($top, $filename, -persistent => 1);
16482	}
16483	return $photo;
16484    } elsif ($tag eq 'nl') {
16485	my $photo;
16486	if ($myscale > 8*$mod) {
16487	    $photo = $night_photo;
16488	} elsif ($myscale >= 2*$mod) {
16489	    my $filename = 'night_klein';
16490	    $photo = load_photo($top, $filename, -persistent => 1);
16491	}
16492	return $photo;
16493    } elsif ($tag eq 'attrib-inwork') {
16494	my $photo;
16495	if ($myscale > 4*$mod) {
16496	    $photo = $inwork_photo;
16497	} elsif ($myscale >= 2*$mod) {
16498	    $photo = $inwork_klein_photo;
16499	}
16500	return $photo;
16501    }
16502}
16503
16504sub scale_maps {
16505    my $scalefactor = shift;
16506    if (defined $map_img || @map_surround_img) {
16507	my($width, $height);
16508	for my $img ($map_img, @map_surround_img) {
16509	    if (defined $img) {
16510		($width, $height) = ($img->width, $img->height);
16511		last;
16512	    }
16513	}
16514	if (defined $width) {
16515	    my @maps = $c->find(withtag => 'map');
16516	    for my $map_i (@maps) {
16517		my @map_coords = $c->coords($map_i);
16518		if ($c->type($map_i) eq 'image') {
16519		    eval {
16520			my $p = $c->itemcget($map_i, "-image");
16521			$p->delete;
16522		    }; warn $@ if $@;
16523		}
16524		$c->delete($map_i);
16525		@map_coords = ($map_coords[0]+$width*$scalefactor/2,
16526			       $map_coords[1]+$height*$scalefactor/2);
16527		# @map_coords zeigt jetzt auf die Mitte der Karte ...
16528		eval {
16529		    local $map_surround = 0;
16530		    getmap(@map_coords); # Karte neu zeichnen (richtig skaliert)
16531		}; warn $@ if $@;
16532	    }
16533	}
16534    }
16535}
16536
16537sub scrollregion_best {
16538    if ($city_obj->bbox) {
16539	require BBBikeAdvanced;
16540	set_scrollregion(@{ $city_obj->_bbox_standard_coordsys });
16541    }
16542}
16543
16544# Zentriert entweder auf eine Stra�e oder Koordinaten oder auf die Mitte
16545# Berlins.
16546### AutoLoad Sub
16547sub center_best {
16548    if (defined $city && $city eq 'Berlin') {
16549	if (defined $center_on_str && $center_on_str !~ /^\s*$/) {
16550	    choose_from_plz(-str   => $center_on_str);
16551	    return;
16552	} elsif (defined $center_on_coord && $center_on_coord !~ /^\s*$/) {
16553	    choose_from_plz(-coord => $center_on_coord);
16554	    return;
16555	}
16556    }
16557    if ($city_obj->_center_standard_coordsys) {
16558	$c->center_view(transpose(split /,/, $city_obj->_center_standard_coordsys));
16559    } elsif ($city_obj->center) {
16560	$c->center_view(transpose(split /,/, $city_obj->center));
16561    } else {
16562	$c->center_view;
16563    }
16564}
16565
16566# Zentriert auf den Anfang der aktuellen Route
16567### AutoLoad Sub
16568sub center_begin_of_route {
16569    $c->center_view($coords[0]->[0], $coords[0]->[1]);
16570}
16571
16572# Zentriert auf den Anfang der aktuellen Route und verschiebt zum
16573# letzten Punkt der Route hin,
16574### AutoLoad Sub
16575sub center_whole_route {
16576    $c->see($coords[0]->[0], $coords[0]->[1],
16577	    $coords[-1]->[0], $coords[-1]->[1],
16578	   );
16579}
16580
16581# Zoomt den Ausschnitt so, da� minx/miny und maxx/maxy in den Ecken stehen.
16582# Wenn keine Argumente angegeben sind, werden die Minimal/Maximalwerte der
16583# aktuellen Route genommen.
16584### AutoLoad Sub
16585sub zoom_view {
16586    my($minx, $miny, $maxx, $maxy);
16587    if (@_) {
16588	($minx, $miny, $maxx, $maxy) = @_;
16589    } elsif (!@coords) {
16590	return;
16591    } else {
16592	foreach (@coords) {
16593	    if (!defined $minx || $_->[0] < $minx) { $minx = $_->[0] }
16594	    if (!defined $maxx || $_->[0] > $maxx) { $maxx = $_->[0] }
16595	    if (!defined $miny || $_->[1] < $miny) { $miny = $_->[1] }
16596	    if (!defined $maxy || $_->[1] > $maxy) { $maxy = $_->[1] }
16597	}
16598    }
16599
16600    my(@corner) = $c->get_corners;
16601    my $c_w = ($corner[2]-$corner[0]);
16602    my $c_h = ($corner[3]-$corner[1]);
16603    my($r_w, $r_h) = ($maxx-$minx, $maxy-$miny);
16604    $c->center_view($r_w/2+$minx, $r_h/2+$miny);
16605    # XXX ls/pt-Version
16606    if ($r_w > 0 and $r_h > 0) {
16607	my $asp_x = $c_w/$r_w;
16608	my $asp_y = $c_h/$r_h;
16609	if ($asp_x < $asp_y) {
16610	    scalecanvas($c, $asp_x/1.1); # 10% Luft lassen
16611	} else {
16612	    scalecanvas($c, $asp_y/1.1);
16613	}
16614    }
16615}
16616
16617# XXX move to CanvasUtil.pm ???
16618sub Tk::Canvas::smooth_scroll {
16619    my($c, $tox, $toy, %args) = @_;
16620    if ($use_smooth_scroll && !$args{NoSmoothScroll}) {
16621	my($fromx, $fromy) = (($c->xview)[0], ($c->yview)[0]);
16622	my $step = 10;
16623	my($deltax, $deltay) = (($tox-$fromx)/$step,
16624				($toy-$fromy)/$step);
16625	for (1 .. $step) {
16626	    $c->xview('moveto' => $fromx + $deltax * $_);
16627	    $c->yview('moveto' => $fromy + $deltay * $_);
16628	    $c->idletasks;
16629	}
16630    } else {
16631	$c->xview('moveto' => $tox);
16632	$c->yview('moveto' => $toy);
16633    }
16634}
16635
16636# Diese Funktion geht von einer korrekten dpi-Einstellung f�r den
16637# Bildschirm und quadratischen Dots aus.
16638# R�ckgabewert: Der Teil hinter dem Doppelpunkt.
16639sub calc_mapscale_nenner {
16640    my($mx1) = transpose(0, 0);
16641    my($mx2) = transpose(1000, 1000);
16642    my $nenner = (($mx2-$mx1)/$top_dpmm/$scale_coeff);
16643    if ($nenner == 0) { $nenner = 0.00000001 }
16644    $nenner = abs(int(1_000_000 / $nenner));
16645    $nenner;
16646}
16647
16648# side-effect: this also sets $mapscale
16649sub calc_mapscale {
16650    my $nenner = calc_mapscale_nenner();
16651    $mapscale = "1:$nenner";
16652    $nenner;
16653}
16654
16655### AutoLoad Sub
16656sub show_zoomrect {
16657    my($i) = @_;
16658    if (!defined $i) {
16659	$c->delete('zoomrect');
16660	if (defined $zoomrect_after) {
16661	    $zoomrect_after->cancel;
16662	}
16663	my @c = $c->get_corners;
16664	$c->createLine(@c[0,1, 0,3, 2,3, 2,1, 0,1],
16665		       -tags => 'zoomrect',
16666		      );
16667    } elsif ($i > 3*2) {
16668	$c->delete('zoomrect');
16669	undef $zoomrect_after;
16670    } else {
16671	$c->itemconfigure('zoomrect',
16672			  -fill => ($i % 2 == 1 ? 'blue' : 'red'));
16673	$zoomrect_after = $c->after(300, sub { show_zoomrect($i+1) });
16674    }
16675}
16676
16677# Mark blinking is only implemented in the main canvas,
16678# not the overview canvas
16679### AutoLoad Sub
16680sub show_mark {
16681    my($i, %args) = @_;
16682    $i = 0 if !defined $i;
16683    if ($i == 0 and $showmark_after) {
16684	$showmark_after->cancel;
16685	undef $showmark_after;
16686    }
16687    my @stipple = ('gray12', 'gray25', 'gray50', 'gray75');
16688    my $col = $i/8; # color ...
16689    my $j   = $i%8; # stage ...
16690    if ($col > 5 && !$args{'-endlessmark'}) {
16691	$c->delete('show');
16692	undef $showmark_after;
16693    } else {
16694	$c->itemconfigure('show',
16695  			  -fill => ($col % 2 == 1 ? 'blue' : 'red'));
16696	if ($j < 4) {
16697	    $c->itemconfigure('show',
16698			      -stipple => $stipple[$j]);
16699	} elsif ($j == 4) {
16700	    $c->itemconfigure('show',
16701			      -stipple => undef);
16702	} else {
16703	    $c->itemconfigure('show',
16704			      -stipple => $stipple[8-$j]);
16705	}
16706	unless ($steady_mark) {
16707	    $showmark_after = $c->after(150, sub { show_mark($i+1, %args) });
16708	} else {
16709	    $c->itemconfigure('show',
16710			      -stipple => undef);
16711	}
16712    }
16713}
16714
16715## DEBUG_BEGIN
16716#BEGIN{mymstat("75% BEGIN");}
16717## DEBUG_END
16718
16719### AutoLoad Sub
16720sub show_overview {
16721    my $new    = shift;
16722
16723    my $overview_top = $toplevel{"overview"};
16724
16725    if ($overview_top && $overview_top->{CoordSystem} ne $coord_system) {
16726	$new = 1;
16727    }
16728    if (defined $overview_top and Tk::Exists($overview_top)) {
16729	if ($new) {
16730	    $overview_top->destroy;
16731	    delete $toplevel{"overview"};
16732	}
16733    }
16734
16735    if (defined $overview_top && Tk::Exists($overview_top)) {
16736	if (!$show_overview) {
16737	    $overview_top->withdraw;
16738	} else {
16739	    $overview_top->deiconify;
16740	    $overview_top->raise;
16741	}
16742	return;
16743    }
16744
16745    $overview_top = $top->Toplevel(-title => M"�bersicht",
16746				   -class => "Bbbike Overview",
16747				  );
16748    $overview_top->OnDestroy(sub { $show_overview = 0; });
16749    $toplevel{"overview"} = $overview_top;
16750    set_as_toolwindow($overview_top);
16751    $overview_top->{CoordSystem} = $coord_system;
16752    {
16753	# Try to set the overview to the right bottom corner of the main
16754	# window:
16755	my($w,$h) = (int($top->width/3), int($top->height/3));
16756	# restrict aspect to 4:3 --- a 16:9 overview window does not look good
16757	$w = min($w, int($h*4/3));
16758	my($x,$y) = ($sy->rootx - $w - 4*2, $sx->rooty - $h - 20 - 4);
16759	geometry($overview_top,$x,$y,$w,$h);
16760    }
16761    show_overview_populate($overview_top);
16762}
16763
16764sub show_overview_clean_and_populate {
16765    my $overview_top = shift;
16766    for ($overview_top->children) {
16767	$_->destroy;
16768    }
16769    show_overview_populate($overview_top);
16770}
16771
16772sub overview_draw_route {
16773    if (Tk::Exists($overview_canvas)) {
16774	my $transpose = $show_overview_mode eq 'region' ? \&transpose_small : \&transpose_medium;
16775	$overview_canvas->delete("route");
16776	return if !@realcoords;
16777	my @coords = map { $transpose->(@$_) } @realcoords;
16778	if (@coords == 2) {
16779	    push @coords, @coords;
16780	}
16781	$overview_canvas->createLine(@coords,
16782				     -fill => "red", -tags => "route");
16783    }
16784}
16785
16786sub overview_del_route {
16787    if (Tk::Exists($overview_canvas)) {
16788	$overview_canvas->delete("route");
16789    }
16790}
16791
16792sub _convert_transposed_to_overview_coord {
16793    my($tx,$ty) = @_;
16794    my $transpose = $show_overview_mode eq 'region' ? \&transpose_small : \&transpose_medium;
16795    $transpose->(anti_transpose($tx, $ty));
16796}
16797
16798sub _convert_overview_to_transposed_coord {
16799    my($x,$y) = @_;
16800    my $anti_transpose = $show_overview_mode eq 'region' ? \&anti_transpose_small : \&anti_transpose_medium;
16801    $anti_transpose->(transpose($x, $y));
16802}
16803
16804sub show_overview_populate {
16805    my $overview_top = shift;
16806    my $withdraw_sub = sub { $overview_top->withdraw;
16807			     $show_overview = 0 };
16808    $overview_top->protocol('WM_DELETE_WINDOW', $withdraw_sub);
16809
16810    # Canvas. Create scrollbars manually, so arrow_update can be called
16811    $overview_canvas = $overview_top->Canvas
16812	(-xscrollincrement => 15, # XXX check values
16813	 -yscrollincrement => 15,
16814	 -bg => $map_bg,
16815	);
16816
16817    Hooks::get_hooks("new_route")->add
16818	    (sub {
16819		 overview_draw_route();
16820	     }, "bbbike-overviewcanvas");
16821    Hooks::get_hooks("del_route")->add
16822	    (sub {
16823		 overview_del_route();
16824	     }, "bbbike-overviewcanvas");
16825    $overview_canvas->OnDestroy
16826	(sub {
16827	     for my $hook ("new_route", "del_route") {
16828		 Hooks::get_hooks($hook)->del("bbbike-overviewcanvas");
16829	     }
16830	 });
16831
16832    my $ov_transpose      = $show_overview_mode eq 'region' ? \&transpose_small : \&transpose_medium;
16833    my $ov_anti_transpose = $show_overview_mode eq 'region' ? \&anti_transpose_small : \&anti_transpose_medium;
16834    {
16835	my($x0,$y0,$x1,$y1) = @scrollregion;
16836	($x0,$y0) = $ov_transpose->(anti_transpose($x0,$y0));
16837	($x1,$y1) = $ov_transpose->(anti_transpose($x1,$y1));
16838	my @s = ($x0,$y0,$x1,$y1);
16839	$overview_canvas->configure(-scrollregion => [@s]);
16840    }
16841
16842    $overview_canvas->createLine(0,0,0,0,-fill => 'red', -tags => 'zoomrect');
16843    $overview_top->gridColumnconfigure(0, -weight => 1);
16844    $overview_top->gridRowconfigure(0, -weight => 1);
16845    $overview_canvas->grid(-row => 0, -column => 0, -sticky => 'eswn');
16846    my $sy = $overview_top->Scrollbar(-command => ["yview", $overview_canvas]);
16847    $sy->grid(-row => 0, -column => 1, -sticky => 'ns');
16848    my $sx = $overview_top->Scrollbar(-orient => 'horiz',
16849				      -command => ["xview", $overview_canvas]);
16850    $sx->grid(-row => 1, -column => 0, -sticky => 'ew');
16851
16852    my $center_coords;
16853    if ($city_obj->center) {
16854	$center_coords = [ split /,/, $city_obj->center ];
16855    } else {
16856	$center_coords = [8581,12243]; # Fallback: Brandenburger Tor
16857    }
16858
16859    my($ov_center_x,$ov_center_y) = $ov_transpose->(@$center_coords);
16860
16861    my $center_name;
16862    if ($city_obj->center_name) {
16863	$center_name = $city_obj->center_name;
16864    }
16865
16866    my $arrow_update;
16867    if ($center_name) {
16868	$arrow_update = sub {
16869	    $overview_canvas->delete('berlinarrow');
16870	    my($cx1,$cy1,$cx2,$cy2) = $overview_canvas->get_corners;
16871	    # Ersten Schnittpunkt (inneres Rechteck) ermitteln
16872	    my($ix1,$iy1) = VectorUtil::intersect_line_rectangle
16873		($cx1+($cx2-$cx1)/2, $cy1+($cy2-$cy1)/2, $ov_center_x,$ov_center_y,
16874		 $cx1+15,$cy1+15,$cx2-15,$cy2-15);
16875	    if (defined $ix1 and defined $iy1) {
16876		# zweiten Schnittpunkt ermitteln (aktuelle Canvasgrenze)
16877		my($ix2,$iy2) = VectorUtil::intersect_line_rectangle($ix1,$iy1,$ov_center_x,$ov_center_y,
16878								     $cx1,$cy1,$cx2,$cy2);
16879		if (defined $ix2 and defined $iy2) {
16880		    # Distance to center (in Berlin: Brandenburger Tor)
16881		    my $entf = Strassen::Util::strecke
16882			([$ov_anti_transpose->($ix1,$iy1)],
16883			 $center_coords);
16884		    $overview_canvas->createLine
16885			($ix1,$iy1,$ix2,$iy2,
16886			 -arrow => "last",
16887			 -width => 2,
16888			 -fill => "red",
16889			 -tags => 'berlinarrow');
16890		    $overview_canvas->createText
16891			($ix1, $iy1,
16892			 -anchor => BBBikeCalc::canvas_translation(BBBikeCalc::line_to_canvas_direction
16893								   ($ix1,$iy1,$ix2,$iy2)),
16894			 -text => "$center_name\n".sprintf("%d km", $entf/1000),
16895			 -fill => "red",
16896			 -font => $font{'small'},
16897			 -tags => ['berlinarrow','berlinarrowlabel']);
16898		}
16899	    }
16900	};
16901    } else {
16902	$arrow_update = sub {};
16903    }
16904
16905    $overview_canvas->configure(-yscrollcommand =>
16906				sub {
16907				    $sy->set(@_);
16908				    $arrow_update->();
16909				},
16910                                -xscrollcommand =>
16911				sub {
16912				    $sx->set(@_);
16913				    $arrow_update->();
16914				},
16915    );
16916
16917    # Zoom buttons
16918    my $button_x = 2;
16919    {
16920	my @zoom_button;
16921	my $set_disabled_buttons = sub {
16922	    if ($show_overview_mode eq 'city') {
16923		$zoom_button[0]->configure(-state => 'disabled');
16924		$zoom_button[1]->configure(-state => 'normal');
16925	    } else {
16926		$zoom_button[0]->configure(-state => 'normal');
16927		$zoom_button[1]->configure(-state => 'disabled');
16928	    }
16929	};
16930	for my $def (['+', 'city'],
16931		     ['-', 'region'],
16932		    ) {
16933	    my($label, $overview_mode_value) = @$def;
16934	    push @zoom_button, $overview_top->Button
16935		(-text => $label,
16936		 -font => $font{'reduced'},
16937		 -padx => 0, -pady => 0,
16938		 -highlightthickness => 0,
16939		 -takefocus => 0,
16940		 -command => sub {
16941		     my $this_button = shift;
16942		     $show_overview_mode = $overview_mode_value;
16943		     $overview_top->after(10, sub { show_overview_clean_and_populate($overview_top) });
16944		     $set_disabled_buttons->();
16945		 },
16946		);
16947	    $zoom_button[-1]->place("-x" => $button_x, "-y" => 2);
16948	    $button_x += $zoom_button[-1]->reqwidth+2;
16949	}
16950	$set_disabled_buttons->();
16951    }
16952
16953    my($km100_pixel) = ($ov_transpose->(100000,0))[0] - ($ov_transpose->(0,0))[0];
16954
16955    # Radar button
16956    if ($advanced && $devel_host) {
16957	my $radar_onoff = 0;
16958	my $radar_button;
16959	my $show_radar_image;
16960	$radar_button = $overview_top->Checkbutton
16961	    (-font => $font{'small'},
16962	     -indicatoron => 0,
16963	     -padx => 0,
16964	     -pady => ($os eq 'win' ? 0 : 1), # for Checkbuttons 1, for Buttons 0 (why?)
16965	     -highlightthickness => 0,
16966	     -takefocus => 0,
16967	     -text => 'Radar',
16968	     -variable => \$radar_onoff,
16969	     -command => sub {
16970		 $radar_button->after(50, $show_radar_image);
16971	     }
16972	    );
16973	$show_radar_image = sub {
16974	    if ($radar_image) {
16975		eval q{ $radar_image->delete };
16976	    }
16977	    $overview_canvas->delete('radarimage');
16978	    return if !$radar_onoff;
16979
16980	    IncBusy($top);
16981	    $progress->Init(-label => M"Radarschirm");
16982	    eval {
16983		require FURadar;
16984		$FURadar::progress = $progress;
16985		$FURadar::VERBOSE = $verbose;
16986		#	       $FURadar::use_map = ($show_overview_mode eq 'region'
16987		#				    ? 'FURadar2' : 'FURadar');
16988		$FURadar::use_map = 'FURadar2'; # the only left...
16989		# XXXX use fetch and cache routine
16990		my $origimgfile = FURadar::fetch();
16991		#XXX	    my $origimgfile = FURadar::latest_dwd();
16992		if ($origimgfile) {
16993		    my $time = (stat($origimgfile))[STAT_MODTIME];
16994		    my $imgfile = FURadar::interesting_parts
16995			($origimgfile,
16996			 -km100pixel => $km100_pixel);
16997		    if (-r $imgfile) {
16998			$radar_image = $overview_canvas->Photo(-file => $imgfile);
16999			my($xoff,$yoff) = ($show_overview_mode eq 'region'
17000					   ? (3,20)
17001					   : $ov_transpose->(0,0));
17002			$overview_canvas->createImage
17003			    ($xoff, $yoff,
17004			     -image => $radar_image,
17005			     -tags => 'radarimage');
17006			foreach my $raise (qw(g gP gD gBO O o)) { # XXX evtl. andere Tags auch raisen
17007			    $overview_canvas->raise($raise);
17008			}
17009		    }
17010		    if ($time) {
17011			$balloon->attach($radar_button,
17012					 -msg => scalar localtime $time);
17013		    }
17014		}
17015	    };
17016	    warn __LINE__ . ": $@" if $@;
17017	    $progress->Finish;
17018	    DecBusy($top);
17019	};
17020	$radar_button->configure(-selectcolor => $radar_button->cget(-background));
17021	$radar_button->place("-x" => $button_x+2, "-y" => 2);
17022    }
17023
17024    my @layer_errors;
17025
17026    # Zeichnen von Gew�ssern, S-Bahnen, Regionalbahnen, Stra�en
17027    # in der �bersichtskarte
17028    foreach my $abk (qw(w b s l r)) {
17029	eval {
17030	    local %str_outline   = %{ clone \%str_outline };
17031	    local %str_name_draw = %{ clone \%str_name_draw };
17032	    local $wasserumland  = $wasserumland;
17033	    local $wasserstadt   = $wasserstadt;
17034	    local %str_far_away  = %{ clone \%str_far_away };
17035	    local %str_restrict  = %{ clone \%str_restrict };
17036	    local %p_draw        = %{ clone \%p_draw };
17037	    if ($overview_draw{$abk} || ($abk eq 'l' && $overview_draw{'s'})) {
17038		$str_outline{$abk} = 0;
17039		$p_draw{'pp'} = 0;
17040		my %args;
17041		if ($abk eq 'w') {
17042		    my $ws_low = eval { Strassen->new("wasserstrassen-lowres") };
17043		    if ($ws_low) {
17044			$args{-object} = $ws_low;
17045		    } else {
17046			for my $cat (qw(W1 W2 F:W F:I)) {
17047			    $str_restrict{$abk}->{$cat} = 1;
17048			}
17049			for my $cat (qw(W0 W)) {
17050			    $str_restrict{$abk}->{$cat} = 0;
17051			}
17052			$wasserumland = $wasserstadt = 1;
17053			$str_far_away{$abk} = 1;
17054		    }
17055		    $str_name_draw{$abk} = 0;
17056		} elsif ($abk eq 's' || $abk eq 'l') {
17057		    $str_restrict{$abk} = {qw(HH 1 B 1 H 0)}; # XXX bad bad hack. The H=>0 is necessary too trigger $all_set=0 elsewhere XXX
17058		}
17059
17060		plot('str',$abk,
17061		     Canvas => $overview_canvas,
17062		     Width  => 1,
17063		     %args,
17064		    );
17065
17066		if ($abk eq 'w') {
17067		    # Hack: need to display islands over water
17068		    $overview_canvas->raise('i-I');
17069		}
17070	    }
17071	};
17072	if ($@) {
17073	    push @layer_errors, "Der Layer <$abk> kann nicht gezeichnet werden: $@";
17074	}
17075    }
17076
17077    overview_draw_route();
17078
17079    $progress->InitGroup;
17080    for my $abk (qw(g gD)) {
17081	eval {
17082	    plot('str',$abk,
17083		 Canvas => $overview_canvas,
17084		 ($abk eq 'g' && $coord_system ne 'standard' ? (Filename => "plz-orig") : ()),
17085		 Width => 3,
17086		);
17087	};
17088	if ($@) {
17089	    push @layer_errors, "Der Layer mit den Grenzen <$abk> kann nicht gezeichnet werden: $@";
17090	}
17091    }
17092
17093    eval {
17094	# local does not work here, segfault on Win98+perl5.6.1/perl5.8.0+Tk800.0xx
17095	my $orte_far_away_orig = $p_far_away{'o'};
17096	$p_far_away{'o'} = 1;
17097	my $no_overlap_label_orig = $no_overlap_label{'o'};
17098	$no_overlap_label{'o'} = 1; # XXX Kein Effekt - warum?
17099	my $orte_label_size_orig = $orte_label_size;
17100	$orte_label_size = 1;
17101	if ($city_obj->is_osm_source) {
17102	    # The PlaceCategory=2/3 limit works good for Dalmatia,
17103	    # but is a little bit slow and to dense for Hessen and Sachsen
17104	    plotorte(Canvas => $overview_canvas,
17105		     PlaceCategory => $show_overview_mode eq 'city' ? 2 : 3,
17106		     Shortname => 1,
17107		     NoOverlapLabel => 'drop_non_fitting',
17108		    );
17109	} else {
17110	    # the old procedure for Berlin data
17111	    plotorte(Canvas        => $overview_canvas,
17112		     PlaceCategory => 4,
17113		     Shortname     => 1,
17114		     NoOverlapLabel => 0,
17115		    );
17116	    if ($show_overview_mode eq 'city') {
17117		plotorte(Canvas        => $overview_canvas,
17118			 PlaceCategory => 0,
17119			 Shortname     => 1,
17120			 NameDraw      => 1,
17121			 -municipality => 1,
17122			 -type         => 'oo'
17123		    );
17124	    }
17125	}
17126
17127	$p_far_away{'o'} = $orte_far_away_orig;
17128	$orte_label_size = $orte_label_size_orig;
17129	$no_overlap_label{'o'} = $no_overlap_label_orig;
17130    };
17131    if ($@) {
17132	push @layer_errors, "Der Orte-Layer kann nicht gezeichnet werden: $@";
17133    }
17134
17135    $progress->FinishGroup;
17136
17137    if (@layer_errors) {
17138	status_message(join("\n", @layer_errors), "warn");
17139    }
17140
17141    $overview_canvas->raise("zoomrect");
17142    $overview_top->bind('<q>' => $withdraw_sub);
17143    $overview_top->bind('<Q>' => sub { &$withdraw_sub;
17144				       $overview_top->destroy
17145				   });
17146    my $real_canvas = $overview_canvas;
17147    my $scroll_lock;
17148    my $set_scroll_lock = sub {
17149	$scroll_lock = $overview_canvas->after(100,
17150					       sub { undef $scroll_lock });
17151    };
17152    my $button_pressed;
17153    my $refresh_sub;
17154    my($delta_x_fraction, $delta_y_fraction) = (0.5, 0.5);
17155    $refresh_sub =
17156      sub {
17157	  my($w, $initial) = @_;
17158	  my $e = $w->XEvent;
17159	  if (!defined $button_pressed) {
17160	      $button_pressed = $overview_canvas->repeat
17161		(100, sub { $refresh_sub->($w, 0); });
17162	  }
17163	  return if $scroll_lock;
17164	  my($x, $y) = ($e->x, $e->y);
17165	  my($xx, $yy) = ($overview_canvas->canvasx($x),
17166			  $overview_canvas->canvasy($y));
17167	  if ($initial) {
17168	      my(@c) = $overview_canvas->bbox('zoomrect');
17169	      if ($xx >= $c[0] && $xx <= $c[2] &&
17170		  $yy >= $c[1] && $yy <= $c[3]) {
17171		  # Click in rect, record initial position.
17172		  # This code is necessary to avoid jumps on initial click.
17173		  $delta_x_fraction = ($xx-$c[0])/($c[2]-$c[0]);
17174		  $delta_y_fraction = ($yy-$c[1])/($c[3]-$c[1]);
17175	      }
17176	  }
17177	  my $real_canvas_width  = $real_canvas->width;
17178	  my $real_canvas_height = $real_canvas->height;
17179	  # XXX ist noch etwas ruckartig ... kleinere units,
17180	  # intelligenteres Handling!
17181	  my $pad = 10;
17182	  if ($x < $pad) {
17183	      $overview_canvas->xview(scroll => -1, 'units');
17184	      $set_scroll_lock->();
17185	  }
17186	  if ($y < $pad) {
17187	      $overview_canvas->yview(scroll => -1, 'units');
17188	      $set_scroll_lock->();
17189	  }
17190	  if ($x > $real_canvas_width-$pad) {
17191	      $overview_canvas->xview(scroll => +1, 'units');
17192	      $set_scroll_lock->();
17193	  }
17194	  if ($y > $real_canvas_height-$pad) {
17195	      $overview_canvas->yview(scroll => +1, 'units');
17196	      $set_scroll_lock->();
17197	  }
17198	  my(@oldx) = $c->xview;
17199	  my(@oldy) = $c->yview;
17200	  my($xwidth) = $oldx[1]-$oldx[0];
17201	  my($ywidth) = $oldy[1]-$oldy[0];
17202
17203	  ($xx, $yy) = ($show_overview_mode eq 'region'
17204			? anti_transpose_small($xx, $yy)
17205			: anti_transpose_medium($xx, $yy)
17206		       );
17207	  ($xx, $yy) = transpose($xx, $yy);
17208	  $c->center_view($xx,$yy);
17209      };
17210
17211    $real_canvas->Tk::bind('<ButtonPress-1>'  => sub {
17212			       my $w = shift;
17213			       $refresh_sub->($w, 1, @_)
17214			   });
17215    $real_canvas->Tk::bind('<B1-Motion>' => sub {
17216			       my $w = shift;
17217			       $refresh_sub->($w, 0, @_)
17218			   });
17219    $real_canvas->Tk::bind
17220      ('<ButtonRelease-1>'
17221       => sub {
17222	   if (defined $button_pressed) {
17223	       $button_pressed->cancel();
17224	       undef $button_pressed;
17225	   }
17226	   ($delta_x_fraction, $delta_y_fraction) = (0.5, 0.5);
17227       });
17228
17229    {
17230	my $gain = 1;
17231	$real_canvas->CanvasBind('<2>',
17232				 [sub {
17233				      my($w,$x,$y) = @_;
17234				      $w->scan('mark',$x,$y);
17235				  },Tk::Ev('x'),Tk::Ev('y')]);
17236	$real_canvas->CanvasBind('<B2-Motion>',
17237				 [sub {
17238				      my($w,$x,$y) = @_;
17239				      $w->scan('dragto',$x,$y,$gain);
17240				  },Tk::Ev('x'),Tk::Ev('y')]);
17241    }
17242
17243    # Scrolling korrigieren (auf Mitte setzen)
17244    my(@oldx) = $overview_canvas->xview;
17245    my(@oldy) = $overview_canvas->yview;
17246    my($xwidth) = $oldx[1]-$oldx[0];
17247    my($ywidth) = $oldy[1]-$oldy[0];
17248    $overview_canvas->xview('moveto' => (1-$xwidth)/2);
17249    $overview_canvas->yview('moveto' => (1-$ywidth)/2);
17250
17251    overview_update();
17252
17253    # Scrollbar-Navigation per Cursortasten
17254    $overview_top->bind
17255      ('<Up>'    => sub { $real_canvas->yview(scroll => -1, 'units') });
17256    $overview_top->bind
17257      ('<Down>'  => sub { $real_canvas->yview(scroll => 1, 'units') });
17258    $overview_top->bind
17259      ('<Left>'  => sub { $real_canvas->xview(scroll => -1, 'units') });
17260    $overview_top->bind
17261      ('<Right>' => sub { $real_canvas->xview(scroll => 1, 'units') });
17262}
17263
17264### AutoLoad Sub
17265sub delete_overview {
17266    my $overview_top = $toplevel{"overview"};
17267    if (defined $overview_top && Tk::Exists($overview_top)) {
17268	$overview_top->destroy;
17269    }
17270    eval q{ $radar_image->delete };
17271
17272    delete $toplevel{"overview"};
17273    # Done already in OnDestroy: $show_overview = 0;
17274}
17275
17276### AutoLoad Sub
17277sub overview_update {
17278    return if !$overview_canvas || !Tk::Exists($overview_canvas);
17279    my @a = $c->get_corners;
17280    my @c;
17281    my $i;
17282    my $ts = ($show_overview_mode eq 'region'
17283	      ? \&transpose_small
17284	      : \&transpose_medium);
17285    for($i = 0; $i < $#a; $i+=2) {
17286	push @c, $ts->(anti_transpose($a[$i], $a[$i+1]));
17287    }
17288    $overview_canvas->coords('zoomrect', @c[0,1, 0,3, 2,3, 2,1, 0,1]);
17289    my($midx, $midy) = (($c[2]-$c[0])/2+$c[0],
17290			($c[3]-$c[1])/2+$c[1]);
17291
17292    if (!$overview_canvas->is_visible($midx, $midy)) {
17293	$overview_canvas->center_view($midx, $midy);
17294    }
17295}
17296
17297##### Suche #####################################################
17298sub search_route {
17299    my($start, $ziel, $via_arr, $continue, %args) = @_;
17300    return if $in_search;
17301    $in_search++;
17302    my @via; @via = @$via_arr if defined $via_arr;
17303
17304    destroy_delayed_restack();
17305
17306    IncBusy($top, %busy_watch_args);
17307    eval {
17308	status_message("");
17309	my @res = do_search($start, $ziel, \@via, %args);
17310
17311	if (!@res) {
17312	    die M"Keine Strecke gefunden.\n";
17313	}
17314
17315	my @path = @{ $res[StrassenNetz::RES_PATH] };
17316	if (!$continue) {
17317	    clear_undecided_temp_blockings();
17318	}
17319	check_path_in_blockings_net(\@path);
17320 	my $old_nr;
17321 	if ($continue) {
17322 	    save_route_to_register('cont'); # if $max_list > 0;
17323 	    $old_nr = $#coords;
17324 	} else {
17325	    # XXX shouldn't be necessary!!!
17326	    my($save_start) = $search_route_points[0]; # XXX used to be [SRP_COORD]?!
17327 	    if (!exists $args{-undo} || $args{-undo}) {
17328		reset_undo_route();
17329	    } else {
17330		resetroute();
17331	    }
17332	    push @search_route_points, $save_start;
17333 	}
17334
17335	addpoints_xy(\@path);
17336	updatekm();
17337	# continue with best route (but do not continue if the route was deleted before and @act_search_route is empty)
17338	if ($continue && @act_search_route) {
17339	    push @act_search_route,
17340		$net->route_to_name([@path], -startindex => $old_nr); # XXX is wrong (?): +1);
17341	} else {
17342	    # Use @realcoords instead of @path, in case it is continued,
17343	    # but with an empty @act_search_route before
17344	    @act_search_route = $net->route_to_name([@realcoords], -startindex=>0);
17345	}
17346	if (@path) {
17347	    push @search_route_points, [join(",", @{ $path[-1] }),
17348					POINT_SEARCH];
17349	}
17350	print "Route: ", join(", ", map { $_->[0] } @act_search_route), "\n"
17351	  if $verbose;
17352	if (exists $args{-caller} && $args{-caller} eq 'chooseort') {
17353	    zoom_view() if ($zoom_new_route_chooseort);
17354	} else {
17355	    zoom_view() if ($zoom_new_route);
17356	}
17357	if ($auto_show_list) {
17358	    $show_strlist = 1;
17359	    show_route_strname();
17360	}
17361	if ($edit_mode_flag) {
17362	    require BBBikeAdvanced;
17363	    path_to_selection();
17364	}
17365	set_flag('via');
17366	set_flag('ziel');
17367	restack_delayed();
17368    };
17369    my $err = $@;
17370    $in_search = 0;
17371    DecBusy($top);
17372    status_message($err, 'err') if ($err);
17373}
17374
17375# Low-level search
17376sub do_search {
17377    my($start, $ziel, $via_ref) = @_;
17378
17379    # XXX $str_draw{'r'} ueberprfen, wenn im RB-Mode
17380    if (!$net and ($str_draw{'s'} || $str_draw{'l'})) { make_net() }
17381    print STDERR "Suche von <$start> bis <$ziel>" . (@$via_ref ? " via <@$via_ref>" : "") . "\n"
17382	if $verbose;
17383    my %extra_args;
17384    $extra_args{Via} = $via_ref;
17385    if (keys %ampeln) {
17386	if ($ampel_optimierung) {
17387	    $extra_args{Ampeln} =
17388		{Net     => \%ampeln,
17389		 Penalty => $lost_strecke_per_ampel};
17390	} elsif ($optprefs{'Ampeln'}) {
17391	    $extra_args{Ampeln} =
17392		{Net     => \%ampeln,
17393		 Penalty => optprefs2penalty($optprefs{'Ampeln'})*100};
17394	} # XXX
17395	if ($abbiege_optimierung) {
17396	    $extra_args{Abbiegen} = {Penalty => $abbiege_penalty,
17397				     Order   => {'NN' => 0,
17398						 'N' => 1,
17399						 'NH' => 1,
17400						 'H' => 2,
17401						 'HH' => 3,
17402						 'BAB' => 3, # XXX
17403						 'B' => 4}};
17404	}
17405	# XXX optprefs
17406    }
17407
17408    # Qualit�t, Handicap und tempor�re Handicaps
17409    foreach my $def ({OptSwitch	 => \$qualitaet_s_optimierung,
17410		      OptName	 => 'Qualit�t',
17411		      Speed          => \%qualitaet_s_speed,
17412		      MakeNet	 => \&make_qualitaet_net,
17413		      CatPrefix	 => 'Q',
17414		      ExtraArgsName	 => 'Qualitaet',
17415		     },
17416		     {OptSwitch	 => \$handicap_s_optimierung,
17417		      OptName	 => 'Sonstige Beeintr�chtigungen',
17418		      Speed          => \%handicap_s_speed,
17419		      MakeNet	 => \&make_handicap_net,
17420		      CatPrefix	 => 'q',
17421		      ExtraArgsName	 => 'Handicap',
17422		     },
17423		    ) {
17424	my $opt = $ {$def->{OptSwitch}};
17425	my $optname = $def->{OptName};
17426	if ($opt || (defined $optname && $optprefs{$optname})) {
17427	    my $speed = $def->{Speed};
17428	    my $makenet = $def->{MakeNet};
17429	    my $catprefix = $def->{CatPrefix};
17430	    my $net = $makenet->();
17431	    my $penalty;
17432	    if ($opt) {
17433		foreach (0 .. 4) {
17434		    next if !defined $speed->{$catprefix . "$_"};
17435		    $penalty->{$catprefix . "$_"} =
17436			max_speed($speed->{$catprefix . "$_"});
17437		}
17438	    } else {
17439		foreach (0 .. 4) {
17440		    next if !defined $penalty->{$catprefix . "$_"};
17441		    # XXX
17442		    $penalty->{$catprefix . "$_"} =
17443			optprefs2penalty($optprefs{$def->{OptName}}) * $_;
17444		}
17445	    }
17446	    $extra_args{$def->{ExtraArgsName}} =
17447		{Net => $net,
17448		 Penalty => $penalty,
17449		};
17450	}
17451    }
17452
17453    if ($strcat_optimierung || $optprefs{'Kategorie'}) {
17454	# XXX wenn L zugeschaltet wird, mu� strcat_net aktualisiert werden
17455	if (!$strcat_net) {
17456	    if ($multistrassen) {
17457		$strcat_net = new StrassenNetz $multistrassen;
17458	    } elsif ($str_obj{'s'}) {
17459		$strcat_net = new StrassenNetz $str_obj{'s'};
17460	    }
17461	    if ($strcat_net) {
17462		$strcat_net->make_net_cat;
17463	    }
17464	}
17465	if ($strcat_net) {
17466	    my $penalty;
17467	    if ($strcat_optimierung) {
17468		foreach (keys %strcat_speed) {
17469		    $penalty->{$_} = max_speed($strcat_speed{$_});
17470		}
17471	    } else {
17472# 		my %strcat_def = (B  => HH => 100,
17473#    H  => 100,
17474#    N  => 100,
17475#    NN => 100);
17476# 		foreach (keys %strcat_speed) {
17477# 		    # XXX
17478# 		    $penalty->{"Q$_"} = optprefs2penalty($optprefs{'Kategorie'})* $_;
17479# 		}
17480	    }
17481	    $extra_args{Strcat} =
17482		{Net => $strcat_net,
17483		 Penalty => $penalty,
17484		};
17485	}
17486    }
17487    if ($radwege_optimierung) {
17488	if (!$radwege_net) {
17489	    my $radwege_exact = new Strassen "radwege_exact";
17490	    $radwege_net = new StrassenNetz $radwege_exact;
17491	    $radwege_net->make_net_cat(-obeydir => 1);
17492	    # add all other streets do not have cycle paths ...
17493	    while (my($p1,$hash) = each %{ $net->{Net} }) {
17494		while (my($p2,$entf) = each %$hash) {
17495		    if (!exists $radwege_net->{Net}{$p1}{$p2}) {
17496			$radwege_net->{Net}{$p1}{$p2} = "RW0";
17497			$radwege_net->{Net}{$p2}{$p1} = "RW0";
17498		    }
17499		}
17500	    }
17501	}
17502	my $penalty;
17503	foreach (keys %radwege_speed) {
17504	    $penalty->{$_} = max_speed($radwege_speed{$_});
17505	}
17506
17507	$extra_args{Radwege} =
17508	    {Net => $radwege_net,
17509	     Penalty => $penalty,
17510	    };
17511    }
17512
17513    if ($N_RW_optimization || $N_RW1_optimization) {
17514	# XXX check if $N_RW_net is up-to-date with respect to its
17515	# sources, or whether a new $N_RW_net should be build
17516	if (!$N_RW_net) {
17517	    my $s = $multistrassen ? $multistrassen : $str_obj{'s'};
17518	    if (!$s) {
17519		warn "Can't get streets object, ignore N_RW optimization";
17520	    } else {
17521		$N_RW_net = new StrassenNetz $s;
17522		$N_RW_net->make_net_cyclepath(Strassen->new("radwege_exact"), 'N_RW');
17523	    }
17524	}
17525	if ($N_RW_net) {
17526	    my $penalty = { "H"     => 4,
17527			    "H_Bus" => ($N_RW1_optimization ? 4 : 1),
17528			    "H_RW"  => 1,
17529			    "N"     => 1,
17530			    "N_Bus" => 1,
17531			    "N_RW"  => 1 };
17532	    $extra_args{RadwegeStrcat} =
17533		{Net => $N_RW_net,
17534		 Penalty => $penalty,
17535		};
17536	}
17537    }
17538
17539    if ($tram_optimization) {
17540	if (!$tram_net) {
17541	    $tram_net = StrassenNetz->new(Strassen->new('comments_tram')); # XXX -orig?
17542	    $tram_net->make_net_cat;
17543	}
17544	if ($tram_net) {
17545	    my $penalty = { "CS"   => 4 }; # XXX about 20km/h -> 5km/h
17546	    $extra_args{Tram} =
17547		{Net => $tram_net,
17548		 Penalty => $penalty,
17549		};
17550	}
17551    }
17552
17553    if ($green_optimization) {
17554	# XXX check if $green_net is up-to-date with respect to its
17555	# sources, or whether a new $green_net should be build
17556	if (!$green_net) {
17557	    $green_net = new StrassenNetz(Strassen->new("green"));
17558	    $green_net->make_net_cat;
17559	}
17560	my $penalty = ($green_optimization == 2
17561		       ? { "green0" => 3,
17562			   "green1" => 2,
17563			   "green2" => 1,
17564			 }
17565		       : { "green0" => 2,
17566			       "green1" => 1.5,
17567				   "green2" => 1,
17568			       }
17569		      );
17570	$extra_args{Green} =
17571	    {Net => $green_net,
17572	     Penalty => $penalty,
17573	    };
17574    }
17575
17576    if ($unlit_streets_optimization) {
17577	if (!$unlit_streets_net) {
17578	    $unlit_streets_net = new StrassenNetz(Strassen->new("nolighting"));
17579	    $unlit_streets_net->make_net_cat;
17580	}
17581	my $penalty = { "NL" => 4,
17582		      };
17583	$extra_args{UnlitStreets} =
17584	    {Net => $unlit_streets_net,
17585	     Penalty => $penalty,
17586	    };
17587    }
17588
17589    if ($steigung_optimierung) {
17590	if (!$steigung_net) {
17591	    $steigung_net = new StrassenNetz Strassen->new;
17592	    $steigung_net->make_net_steigung($net, \%hoehe);
17593	}
17594	my $penalty;
17595	my $act_power;
17596	if ($active_speed_power{Type} eq 'power') {
17597	    $act_power = $power[$active_speed_power{Index}];
17598	} else {
17599	    $act_power = speed2power($speed[$active_speed_power{Index}]);
17600	}
17601	if (!defined $steigung_penalty_env{ActPower} ||
17602	    $steigung_penalty_env{ActPower} != $act_power) {
17603	    $steigung_penalty = {};
17604	}
17605	$steigung_penalty_env{ActPower} = $act_power;
17606	$extra_args{Steigung} =
17607	    {Net => $steigung_net,
17608	     Penalty => $steigung_penalty,
17609	     PenaltySub => sub { steigung_penalty($_[0], $act_power) },
17610	    };
17611    }
17612    if (!$sperre{'tragen'}) {
17613	$extra_args{Tragen} = 1;
17614    }
17615    $extra_args{Velocity} = get_active_speed()/3.6; # should be m/s
17616    # XXX Bislang noch keine M�glichkeit au�er /tmp/add.pl, um
17617    # $aufschlag zu setzen.
17618    # Der Alternativ-Strecken-Code braucht noch viel Arbeit. Als
17619    # erstes sollte ein Start/Ziel-Punkt, der zwischen zwei
17620    # Kreuzungen/Kurvenpunkten liegt, h�chstens einmal! durchfahren
17621    # werden.
17622    if ($aufschlag != 0 && $aufschlag != 1) {
17623	$extra_args{Aufschlag} = $aufschlag;
17624	$extra_args{All}       = 1;
17625    }
17626    # XXX weitere m�gliche Optimierungen:
17627    # (benutzungspflichtige) Radwege
17628    # verkehrsberuhigte Zonen => 6 .. 20 km/h
17629    # Fu�g�ngerampeln: Abbremsen auf 10 km/h und gleich wieder hoch
17630    # Kreuzungen (Neben/Haupt, Haupt/Haupt ohne Ampel)
17631    # Berufsverkehr (Stau auf gro�en Stra�en => 15 .. 20 km/h)
17632    if ($search_stat) {
17633	$extra_args{Stat} = 1;
17634    }
17635    if ($search_visual) {
17636	$extra_args{'VisualSearch'} = {'Canvas' => $c,
17637				       'Transpose' => \&transpose,
17638				       'Delay' => 0.1,
17639				      };
17640    }
17641    if (%global_search_args) {
17642	while (my($k,$v) = each %global_search_args) {
17643	    $extra_args{$k} = $v;
17644	}
17645    }
17646    if (keys %penalty_subs) {
17647	# Note: the %penalty_subs should only multiply $p, not add to
17648	# if there are more than one penalty sub!
17649	$extra_args{UserDefPenaltySub} = sub {
17650	    my($p, $next_node, $last_node) = @_;
17651	    while (my($k,$v) = each %penalty_subs) {
17652		$p = $v->($p, $next_node, $last_node);
17653	    }
17654	    $p;
17655	};
17656    }
17657
17658    make_net() if (!$net);
17659    foreach my $ref (\$start, \$ziel) {
17660	if (!$net->reachable($$ref)) {
17661	    add_new_point($net, $$ref); # XXX ja?
17662	}
17663    }
17664    my(@res) = $net->search($start, $ziel, %extra_args);
17665
17666    @res;
17667}
17668
17669# Wiederholung der Suche (evtl. mit neuen Parametern)
17670### AutoLoad Sub
17671sub re_search {
17672    my(%args) = @_;
17673    return if @search_route_points < 2;
17674    IncBusy($top, %busy_watch_args);
17675    eval {
17676	my(@old_search_route_points) = @search_route_points;
17677	@search_route_points = $old_search_route_points[SRP_COORD];
17678	for(my $i=0; $i<$#old_search_route_points; $i++) {
17679	    my $p1 = $old_search_route_points[$i];
17680	    my $p2 = $old_search_route_points[$i+1];
17681	    if ($p2->[SRP_TYPE] eq POINT_MANUELL) {
17682		addpoint_xy(split(/,/, $p2->[SRP_COORD]));
17683		push @search_route_points, [@$p2];
17684	    } else {
17685		search_route
17686		    ($p1->[SRP_COORD], $p2->[SRP_COORD],
17687		     undef, ($i == 0 ? '' : 'cont'),
17688		     (exists $args{-undo} ? (-undo => $args{-undo}) : ()),
17689		    );
17690	    }
17691	}
17692    };
17693    my $err = $@;
17694    DecBusy($top);
17695    die $err if $err;
17696}
17697
17698sub re_search_gui {
17699    re_search(@_);
17700    update_route_strname();
17701}
17702
17703sub add_via_to_current_search {
17704    my($before, $via, $after) = @_;
17705
17706    destroy_delayed_restack();
17707    IncBusy($top, %busy_watch_args);
17708    eval {
17709	status_message("");
17710
17711	my $insert_index;
17712	for my $i (0 .. $#search_route_points-1) {
17713	    # We assume that the same before/after combination
17714	    # exists exactly once in the route. This is a
17715	    # rather pragmatic assumption.
17716	    if ($search_route_points[$i]->[SRP_COORD] eq $before &&
17717		$search_route_points[$i+1]->[SRP_COORD] eq $after) {
17718		$insert_index = $i;
17719		last;
17720	    }
17721	}
17722	if (!defined $insert_index) {
17723	    # Should never happen, no translation necessary:
17724	    die "Cannot insert via point (no insertion index found using $before - $after)";
17725	}
17726
17727	# Neither $before nor $after should be used multiple times. This
17728	# is still pragmatic, but less likely as the above assumption.
17729	my($before_index_in_route, $after_index_in_route);
17730	my $stage = 0; # 0: search for before, 1: search for after
17731	for my $i (0 .. $#realcoords) {
17732	    if ($stage == 0) {
17733		if (join(",",@{$realcoords[$i]}) eq $before) {
17734		    $before_index_in_route = $i;
17735		    $stage = 1;
17736		}
17737	    } else {
17738		if (join(",",@{$realcoords[$i]}) eq $after) {
17739		    $after_index_in_route = $i;
17740		    last;
17741		}
17742	    }
17743	}
17744	if (!defined $before_index_in_route ||
17745	    !defined $after_index_in_route) {
17746	    # Should never happen, no translation necessary:
17747	    die "Cannot find either $before or $after in realcoords";
17748	}
17749
17750	$via = add_new_point($net, $via); # may die if via is not insertable
17751
17752	my @res = do_search($before, $after, [$via]);
17753	if (!@res) {
17754	    die M"Keine Strecke gefunden.\n";
17755	}
17756	my @path = @{ $res[StrassenNetz::RES_PATH] };
17757	check_path_in_blockings_net(\@path);
17758
17759	# XXX what about register/undo/... stuff?
17760
17761	splice @search_route_points, $insert_index+1, 0, [$via, POINT_SEARCH];
17762	splice @realcoords, $before_index_in_route, ($after_index_in_route-$before_index_in_route)+1, @path;
17763
17764	# XXX Too much duplication with other route handling functions:
17765	# search_route, redraw_path, reverse_route ...
17766	my @oldrealcoords = @realcoords;
17767	my @oldsearchroutepoints = @search_route_points; # hack
17768	resetroute();
17769	addpoints_xy(\@oldrealcoords);
17770	# XXX as a side effect, @realcoords and @coords are set to the new route
17771	@search_route_points = @oldsearchroutepoints;
17772	set_flag('via');
17773	set_flag('ziel');
17774	updatekm();
17775	restack_delayed();
17776    };
17777    my $err = $@;
17778    DecBusy($top);
17779    status_message($err, 'err') if ($err);
17780}
17781
17782# Steigung mu� als Tausendfaches angegeben werden.
17783### AutoLoad Sub
17784sub steigung_penalty {
17785    my($steigung, $act_power) = @_;
17786    my $frac = ($steigung/1000+0.08)/(0.08*2);
17787    max_speed(power2speed($act_power, -grade => $steigung/1000));
17788}
17789
17790### AutoLoad Sub
17791sub route_strname_on_map {
17792    my $xadd_anchor = $xadd_anchor_type->{'route'};
17793    my $yadd_anchor = $yadd_anchor_type->{'route'};
17794
17795    require Tk::StippleLine;
17796
17797    foreach my $def (@route_strnames) {
17798	my($str, $x, $y, $inx, $entf) = @$def;
17799	$str = $str .= " ($entf)" if defined $entf and $do_route_strnames_km;
17800	my(@tags) = ('route',
17801		     "route-" . $inx,
17802		     'routename');
17803    TRY: {
17804	    for my $check_against (['route', 'routename'],
17805				   ['routename'],
17806				  ) {
17807		my $returnanchor;
17808		if (draw_text_intelligent
17809		    ($c, $x, $y,
17810		     -text => $str,
17811		     -tags => [@tags],
17812		     -abk => $check_against,
17813		     -checktagindex => 'all',
17814		     -xadd => $xadd_anchor,
17815		     -yadd => $yadd_anchor,
17816		     -returnanchor => \$returnanchor,
17817		    )) {
17818		    Tk::StippleLine::create
17819			    ($c, $x, $y,
17820			     $x+$xadd_anchor->{$returnanchor},
17821			     $y+$yadd_anchor->{$returnanchor},
17822			     -fill => 'black',
17823			     -width => 2,
17824			     -tags => [@tags]);
17825		    last TRY;
17826		}
17827	    }
17828	    $c->createText($x, $y, -text => $str,
17829			   -anchor => 'w',
17830			   -tags => [@tags]);
17831	}
17832    }
17833}
17834
17835### AutoLoad Sub
17836sub get_act_search_route {
17837    my @search_route;
17838    if (!@act_search_route) {
17839	if (@realcoords) {
17840	    make_net() if !$net;
17841	    @search_route = $net->route_to_name([@realcoords],-startindex=>0);
17842	}
17843    } else {
17844	@search_route = @act_search_route;
17845    }
17846    \@search_route;
17847}
17848
17849### AutoLoad Sub
17850sub show_route_strname {
17851    require Tk::HList;
17852
17853    my $t;
17854    my $withdraw_sub;
17855    if (defined $toplevel{strlist} && Tk::Exists($toplevel{strlist})) {
17856	if (!$show_strlist) {
17857	    $toplevel{strlist}->withdraw;
17858	} else {
17859	    my $was_withdrawn = $toplevel{strlist}->state ne "normal";
17860	    #XXX maybe combine with code below
17861	    if ($was_withdrawn) {
17862		if (eval {require Tk::Placement; 1; }) {
17863		    # XXX use placer also for other toplevels --- replace
17864		    # all Popup(@popup_style) calls?
17865		    warn "Use Tk::Placement, yet experimental..." if $devel_host;
17866		    Tk::Placement::placer($toplevel{strlist}, -screen => $c,
17867					  -addx => 20, -addy => 25, # XXX for fvwm
17868					  );
17869		}
17870		$toplevel{strlist}->deiconify;
17871		# raise nur ausf�hren, wenn es wirklich was zu sehen gibt
17872		#$toplevel{strlist}->raise;
17873	    }
17874
17875	}
17876    } else {
17877	$toplevel{strlist} = $top->Toplevel(-title => M"Aktuelle Route",
17878				      -class => "Bbbike Routeinfo");
17879	set_as_toolwindow($toplevel{strlist});
17880	$withdraw_sub = sub { $toplevel{strlist}->withdraw;
17881			      $show_strlist = 0 };
17882	$toplevel{strlist}->protocol('WM_DELETE_WINDOW', $withdraw_sub);
17883	$t = $toplevel{strlist};
17884    }
17885
17886    undef @route_info;
17887    if (defined $t) {
17888	$t->SelectionOwn;
17889	# XXX maxbytes beachten
17890	$t->SelectionHandle(sub {
17891				my($offset, $maxbytes) = @_;
17892				my $res = route_info_to_text();
17893				return undef if $offset > length($res);
17894				$res;
17895			    });
17896    }
17897
17898    my($bf, $f1);
17899    if (defined $t) {
17900	$bf = $t->Frame->pack(-fill => 'x', -side => "bottom");
17901	$f1 = $t->Frame->pack(-fill => 'x', -side => "bottom");
17902	$t->Label(-textvariable => \$ampelstatus_label_text,
17903		  -anchor => 'w',
17904		  -justify => "left")->pack(-fill => 'x', -side => 'bottom');
17905    }
17906
17907    if (!Tk::Exists($route_strname_lbox)) {
17908	if (!defined $t) {
17909	    die "No route_strname_lbox?!";
17910	}
17911	$route_strname_lbox = $t->Scrolled
17912	  ('HList',
17913	   -header => 1,
17914	   -columns => 5,
17915	   -selectmode => 'extended',
17916	   -scrollbars => 'osoe',
17917	   -width => 68, # XXX
17918	  )->pack(-expand => 1, -fill => 'both');
17919	$route_strname_lbox->header('create', 0, -text => M"L�nge");
17920	$route_strname_lbox->header('create', 1, -text => M"Gesamt");
17921	$route_strname_lbox->header('create', 2, -text => M"Richtung");
17922	$route_strname_lbox->header('create', 3, -text => M"Stra�e");
17923	$route_strname_lbox->header('create', 4, -text => "");
17924#	$route_strname_lbox->header('create', 5, -text => M"Zeit");
17925    } else {
17926	$route_strname_lbox->delete('all');
17927    }
17928
17929    if ($do_route_strnames_comments && !$do_route_strnames_compact) {
17930	$route_strname_lbox->header('configure', 4, -text => M"Kommentar");
17931    } else {
17932	$route_strname_lbox->header('configure', 4, -text => M"");
17933    }
17934
17935    undef $show_route_start;
17936    undef $show_route_ziel;
17937    undef @route_strnames;
17938    my(@search_route) = @{ get_act_search_route() };
17939
17940    if (@search_route) {
17941
17942	if ($do_route_strnames_orte) {
17943	    if (!$nearest_orte) {
17944		$nearest_orte = new_from_strassen Kreuzungen
17945		                                  Strassen => _get_orte_obj();
17946		$nearest_orte->make_grid;
17947	    }
17948	}
17949
17950	if ($do_route_strnames_comments) {
17951	    if (!$comments_net) {
17952		make_comments_net();
17953	    }
17954	}
17955
17956	$route_strname_lbox->configure
17957	  (-command => sub {
17958	       my $i = shift;
17959	       if (defined $search_route[$i][4] and
17960		   ref $search_route[$i][4] eq 'ARRAY') {
17961		   my @line_coords;
17962		   foreach my $nr ($search_route[$i][4][0]+1 ..
17963				   $search_route[$i][4][1]+1) {
17964		       my @coords = $c->coords("route-$nr");
17965		       push @line_coords, [ @coords ] if @coords;
17966		   }
17967		   mark_street(-coords => \@line_coords,
17968			       -clever_center => 1,
17969			      ) if @line_coords;
17970	       }
17971	   });
17972
17973	# max angle meaning straight forward
17974	use constant ROUTE_STRAIGHT_ANGLE => 30;
17975
17976	if ($do_route_strnames_compact) {
17977	    @search_route = $net->compact_route(\@search_route,
17978						-routestraightangle => ROUTE_STRAIGHT_ANGLE,
17979					       );
17980	}
17981
17982	my $ges_entf = 0;
17983	my($next_entf, $ges_entf_s, $next_winkel, $next_richtung, $next_extra)
17984	  = ("", "", undef, "");
17985	my $last_str;
17986	my %seen_comments;
17987	for(my $i = 0; $i <= $#search_route; $i++) {
17988	    my($str, $index_arr);
17989	    my($entf, $winkel, $richtung, $extra)
17990	      = ($next_entf, $next_winkel, $next_richtung, $next_extra);
17991	    my $entf_s;
17992	    ($str, $next_entf, $next_winkel, $next_richtung, $index_arr, $next_extra)
17993	      = @{$search_route[$i]};
17994	    my $route_strnames_index;
17995	    if ($str ne '...' &&
17996		(!defined $last_str || $last_str ne $str)) {
17997		$last_str = $str;
17998		$str = Strassen::strip_bezirk($str);
17999		if (!defined $show_route_start) {
18000		    $show_route_start = $str;
18001		}
18002		$show_route_ziel = $str;
18003		if (ref $index_arr eq 'ARRAY' &&
18004		    defined $index_arr->[0] &&
18005		    defined $coords[$index_arr->[0]] &&
18006		    defined $coords[$index_arr->[0]+1]) {
18007		    my($x, $y) = ($coords[$index_arr->[0]]->[0],
18008				  $coords[$index_arr->[0]]->[1]);
18009		    push @route_strnames, [$str, $x, $y, $index_arr->[0]];
18010		    $route_strnames_index = $#route_strnames;
18011		}
18012	    }
18013
18014	    if ($i > 0) {
18015		if (!$winkel) { $winkel = 0 }
18016		$winkel = int($winkel/10)*10;
18017		if ($winkel < ROUTE_STRAIGHT_ANGLE && (!$extra || !$extra->{ImportantAngle})) {
18018		    $richtung = "";
18019		} else {
18020		    my $artikel = (!defined $Msg::lang || $Msg::lang =~ /^(|de)$/
18021				   ? Strasse::de_artikel($str)
18022				   : "=>");
18023		    $richtung =
18024		      ($winkel <= 45 ? M"halb" : '') .
18025			($richtung eq 'l' ? M"links" : M"rechts") . " " .
18026			  "($winkel�) " . $artikel;
18027		}
18028
18029		if ($do_route_strnames_orte) {
18030		    my($nearest_ort_xy) =
18031			$nearest_orte->nearest_loop
18032			    ($realcoords[$index_arr->[0]]->[0],
18033			     $realcoords[$index_arr->[0]]->[1],
18034			     IncludeDistance => 1);
18035		    if ($nearest_ort_xy) {
18036			my $ort = $nearest_orte->get_first($nearest_ort_xy->[0]);
18037			# XXX evtl. Ort-Kat f�r 1000 beachten
18038			my $in_bei = ($nearest_ort_xy->[1] <= 1000
18039				      ? M"in" : M"bei");
18040			$richtung = "$in_bei " .
18041			            (Strassen::split_ort($ort))[0] .
18042				    ": $richtung";
18043		    }
18044		}
18045
18046		$ges_entf += $entf;
18047		$ges_entf_s = "(" . m2km($ges_entf) . ")";
18048		$entf_s = M("nach")." ".m2km($entf, 3, 2);
18049		if (defined $route_strnames_index) {
18050		    $route_strnames[$route_strnames_index]->[4]
18051		      = m2km($ges_entf);
18052		}
18053	    } elsif (@coords > 1) {
18054		my $compass = uc(BBBikeCalc::canvas_translation(BBBikeCalc::line_to_canvas_direction
18055								(@{ $coords[0] }, @{ $coords[1] })));
18056		if (defined $Msg::lang && $Msg::lang !~ /^de/) {
18057		    $compass =~ s/([NESW])/{N => M("nord"),
18058					    E => M("ost"),
18059					    S => M("s�d"),
18060					    W => M("west")}->{$1}/gei;
18061		    $richtung = $compass . M("w�rts");
18062		} else {
18063		    $richtung = M("nach")." ".$compass;
18064		}
18065	    }
18066
18067	    $route_strname_lbox->add($i, -text => $entf_s);
18068	    $route_strname_lbox->itemCreate($i, 1, -text => $ges_entf_s);
18069	    $route_strname_lbox->itemCreate($i, 2, -text => $richtung);
18070	    $route_strname_lbox->itemCreate($i, 3, -text => $str);
18071
18072	    my $etappe_comment = "";
18073	    if ($do_route_strnames_comments && $comments_net &&
18074		!$do_route_strnames_compact) {
18075		my @comments;
18076		for my $i ($index_arr->[0] .. $index_arr->[1]) {
18077		    my($etappe_comment_obj) = $comments_net->get_point_comment([@realcoords], $i, \%seen_comments, AsObj => 1);
18078		    if (defined $etappe_comment_obj &&
18079			# Ignore data from comments_kfzverkehr:
18080			$etappe_comment_obj->[Strassen::CAT()] !~ m{^[+-][12]$}
18081		       ) {
18082			my $name = $etappe_comment_obj->[Strassen::NAME()];
18083			$name =~ s{.*:\s+}{}; # strip street part
18084## The following is not needed if the comments are specific enough, i.e.
18085## "An der Ampel Voltairestr. die Gehwegseite wechseln" instead of
18086## "An der Ampel die Gehwegseite wechseln". As most comments are in this
18087## form already I will try to be consistent and have everything like
18088## this (of course, with osm data this would be another story, but
18089## currently CP/CP2/PI comments are not created by osm2bbd)
18090#			# If the special comment is not at beginning
18091#			# of an etappe, then it is useful to have the
18092#			# exact crossing displayed.
18093#			if ($i != $index_arr->[0] && $etappe_comment_obj->[Strassen::CAT()] =~ m{^(CP|CP2|PI)(;|$)?}) {
18094#			    my $crossings = all_crossings();
18095#			    my $c = join ',', @{ $realcoords[$i] };
18096#			    if ($crossings && exists $crossings->{$c}) {
18097#				# XXX strip also "current" street
18098#				my $cr_name = join '/', map { Strassen::strip_bezirk($_) } @{ $crossings->{$c} };
18099#				$name .= " (Kreuzung $cr_name)";
18100#			    }
18101#			}
18102			push @comments, $name;
18103		    }
18104		}
18105		$etappe_comment = join("; ", @comments) if @comments;
18106	    }
18107	    $route_strname_lbox->itemCreate($i, 4, -text => $etappe_comment);
18108	    push @route_info, [($entf_s||""), ($ges_entf_s||""),
18109			       ($richtung||""), ($str || "")];
18110	}
18111	$ges_entf_s = "(" . m2km($ges_entf+$next_entf) . ")";
18112	my $i = $#search_route + 1;
18113	$route_strname_lbox->add($i, -text => M("nach")." ".m2km($next_entf, 3, 2));
18114	$route_strname_lbox->itemCreate($i, 1, -text => "$ges_entf_s");
18115	$route_strname_lbox->itemCreate($i, 2, -text => M"angekommen!");
18116	push @route_info, [M("nach")." ".m2km($next_entf, 3, 2),
18117			   $ges_entf_s, M"angekommen!", ""];
18118
18119	my(@children) = $route_strname_lbox->info('children');
18120	my $last_i = $children[-1];
18121	for(my $j = $i+1; $j<=$last_i; $j++) {
18122	    $route_strname_lbox->delete($j);
18123	}
18124	if ($do_route_strnames) {
18125	    $c->delete("routename");
18126	    route_strname_on_map(\@route_strnames);
18127	}
18128	$toplevel{strlist}->raise;
18129    } else {
18130	$route_strname_lbox->add(0, -text => M"Keine Route");
18131    }
18132
18133    return if !defined $t;
18134
18135    my $do_route_strnames_sub = sub {
18136	$c->delete("routename");
18137	if ($do_route_strnames) {
18138	    route_strname_on_map(\@route_strnames);
18139	}
18140    };
18141    my $cb1 = $f1->Checkbutton(-text => M"Stra�ennamen an der Route",
18142			       -variable => \$do_route_strnames,
18143			       -font => $font{'small'},
18144			      )->pack(-side => 'left');
18145    my $cb2 = $f1->Checkbutton(-text => M"km-Angaben",
18146			       -variable => \$do_route_strnames_km,
18147			       -command => $do_route_strnames_sub,
18148			       -font => $font{'small'},
18149			      )->pack(-side => 'left');
18150    my $cb2_enabler = sub {
18151	$cb2->configure(-state => $do_route_strnames ? "normal" : "disabled");
18152    };
18153    $cb2_enabler->();
18154    $cb1->configure(-command => sub {
18155			$cb2_enabler->();
18156			$do_route_strnames_sub->();
18157		    });
18158
18159    $f1->Checkbutton(-text => M"Kompakt",
18160		     -variable => \$do_route_strnames_compact,
18161		     -command => sub { show_route_strname() },
18162		     -font => $font{'small'},
18163		    )->pack(-side => 'left');
18164    if ($advanced) { # XXX funktioniert noch nicht so schoen intuitiv...
18165	$f1->Checkbutton(-text => M"Orte einbinden",
18166			 -variable => \$do_route_strnames_orte,
18167			 -command => sub { show_route_strname() },
18168			 -font => $font{'small'},
18169			)->pack(-side => 'left');
18170    }
18171    $f1->Checkbutton(-text => M"Kommentare",
18172		     -variable => \$do_route_strnames_comments,
18173		     -command => sub { show_route_strname() },
18174		     -font => $font{'small'},
18175		    )->pack(-side => 'left');
18176
18177    my @bfb;
18178    my $endb = $bf->Button(Name => 'end',
18179			   -command => $withdraw_sub,
18180			  );
18181    $t->bind('<Escape>' => sub { $endb->invoke });
18182    push @bfb, $endb;
18183    push @bfb, $bf->Button
18184      (-text => M"Sichern (Text)",
18185       -command => sub {
18186	   my($file) = $bf->getSaveFile
18187	       (($os eq 'win' ? (-defaultextension => '.TXT') : ()),
18188		-title => M"Route sichern",
18189		-initialdir => $home,
18190	       );
18191	   return if !defined $file;
18192	   if ($os eq 'win' and $file !~ /\.txt$/i) {
18193	       $file .= '.TXT';
18194	   }
18195	   make_backup($file);
18196	   if (open(ROUTE, ">$file")) {
18197	       print ROUTE route_info_to_text();
18198	       close ROUTE;
18199	   } else {
18200	       status_message
18201		   (Mfmt("Schreiben auf <%s> nicht m�glich: %s", $file, $!),
18202		    'err');
18203	   }
18204       },
18205      );
18206    push @bfb, $bf->Button
18207      (-text => M"Sichern (GPX)",
18208       -command => sub { save_route_as_optimized_gpx() },
18209      );
18210    push @bfb, $bf->Button
18211      (-text => M("GPS (Garmin)"),
18212       -command => sub { send_route_to_gps() },
18213      );
18214    $t->bind('<Control-g>' => sub { send_route_to_gps() }); # XXX re-use toplevel binding?
18215    # If there is a txt => palm converter and a palm transfer program,
18216    # then show this button:
18217    require BBBikePalm;
18218    if (can_create_and_transfer_palm_docs()) {
18219	push @bfb, create_palm_button($bf);
18220    }
18221    my $print_text_sub = sub {
18222	my $font = shift;
18223	if (!$show_route_start) { $show_route_start = "???" }
18224	if (!$show_route_ziel)  { $show_route_ziel = "???" }
18225	my $header = Mfmt("Route von %s bis %s",
18226			  $show_route_start, $show_route_ziel);
18227	if ($^O eq 'MSWin32' && defined &Win32Util::start_txt_print) {
18228	    # Make a nice filename as it's visible on the hardcopy:
18229	    my $start = $show_route_start;
18230	    my $ziel  = $show_route_ziel;
18231	    for ($start, $ziel) {
18232		s{[^A-Za-z0-9_-]}{_}g;
18233	    }
18234	    my $base = "Route_" . $start . "_" . $ziel;
18235	    $base = substr($base, 0, 28) if length($base) > 28;
18236	    $base .= ".txt";
18237
18238	    print_text_windows
18239		(-header   => $header,
18240		 -text     => route_info_to_text(),
18241		 -basename => $base,
18242		);
18243	} else { # try pdflatex, then postscript, on Windows first Route::PDF
18244	    my @try_order = qw(pdflatex postscript routepdf);
18245	    if ($os eq 'win') {
18246		@try_order = qw(routepdf pdflatex postscript);
18247	    }
18248	TRY: {
18249		for my $try (@try_order) {
18250		    if ($try eq 'pdflatex') {
18251			last TRY if print_text_pdflatex(route_info_to_latex());
18252		    } elsif ($try eq 'postscript') {
18253			print_text_postscript
18254			    (route_info_to_text(),
18255			     -columns => 1,
18256			     -header => $header,
18257			     -font => $font,
18258			    );
18259		    } elsif ($try eq 'routepdf') {
18260			print_route_pdf();
18261		    }
18262		}
18263	    }
18264	}
18265    };
18266    push @bfb, $bf->Button
18267      (-text => M"Drucken",
18268       -command => sub { $print_text_sub->($ps_fixed_font||"Courier7") },
18269      );
18270    if (_can_send_mail()) {
18271	push @bfb, $bf->Button
18272	    (-text => M"Mail",
18273	     -command => sub {
18274		 if (@route_info) {
18275		     $show_route_start = "???" unless $show_route_start;
18276		     $show_route_ziel  = "???" unless $show_route_ziel;
18277		     enter_send_mail
18278			 (Mfmt("BBBike-Route von %s bis %s",
18279			       $show_route_start, $show_route_ziel),
18280			  -data => route_info_to_text());
18281		 }
18282	     });
18283    }
18284    $t->bind('<Up>'   => sub { $route_strname_lbox->yview(scroll => -1,
18285							  'units') });
18286    $t->bind("<Down>" => sub { $route_strname_lbox->yview(scroll => 1,
18287							  'units') });
18288    pack_buttonframe($bf, \@bfb);
18289    $endb->focus;
18290    #$t->Popup(@popup_style);
18291
18292    my $was_withdrawn = $t->state ne "normal";
18293    if ($was_withdrawn) {
18294	if (eval {require Tk::Placement; 1; }) {
18295	    # XXX use placer also for other toplevels --- replace
18296	    # all Popup(@popup_style) calls?
18297	    warn "Use Tk::Placement, yet experimental...";
18298	    Tk::Placement::placer($t, -screen => $c,
18299				  -addx => 20, -addy => 25, # XXX for fvwm
18300				 );
18301	} else {
18302	    $t->withdraw;
18303	    my($x,$y) = ($top->rootx+$top->width-10, $top->rooty+$top->height-30);
18304	    $t->idletasks;
18305	    $x -= $t->reqwidth;
18306	    $y -= $t->reqheight;
18307	    $x = 0 if ($x < 0);
18308	    $y = 0 if ($y < 0);
18309	    $t->geometry("+$x+$y");
18310	    $t->deiconify;
18311	}
18312    }
18313}
18314
18315sub route_info_to_text {
18316    my $text = sprintf("%-14s %-10s %-26s %s\n",
18317		       M"L�nge", M"Gesamt", M"Richtung", M"Stra�e");
18318    $text .= "-" x 70 . "\n";
18319    $text .= join "", map { sprintf("%-14s %-10s %-26s %s\n", @$_) } @route_info;
18320    $text;
18321}
18322
18323sub _get_route_title {
18324    my $route_name = "BBBike-Route";
18325    if (defined $show_route_start and
18326	defined $show_route_ziel) {
18327	my $start = Strasse::short(Strassen::strip_bezirk($show_route_start), 3); # Start besser abk�rzen --- ist meist immer der Gleiche
18328	my $ziel  = Strasse::short(Strassen::strip_bezirk($show_route_ziel), 2);
18329	$route_name = "BBBike: $start-$ziel";
18330    }
18331    $route_name;
18332}
18333
18334sub route_info_to_html {
18335    my $html_route_name = _get_route_title();
18336    eval {
18337	require HTML::Entities;
18338	HTML::Entities::encode_entities($html_route_name);
18339    };
18340    warn $@ if $@;
18341    my $html = "<html><head><title>$html_route_name</title></head><body>";
18342    $html .= join "", map { sprintf(" %s %s<br>\n%s <b>%s</b><br><br>\n", @$_) } @route_info;
18343    $html .= "</body></html>";
18344    $html;
18345}
18346
18347# More tweaking could be done (other font face/size, real wide margins...)
18348sub route_info_to_latex {
18349    require BBBikeLaTeX;
18350    BBBikeLaTeX::route_info_to_latex(-routetitle => _get_route_title(),
18351				     -routeinfo => \@route_info,
18352				    );
18353}
18354
18355sub update_route_strname {
18356    if (defined $toplevel{strlist} && Tk::Exists($toplevel{strlist})) {
18357	show_route_strname();
18358    }
18359}
18360
18361sub add_custom_layers_to_net {
18362    my($net_source, $net_source_abk) = @_;
18363    while(my($abk,$val) = each %custom_net_str) {
18364	if ($val) { # XXX del? && $abk =~ /^L\d/) {
18365	    eval {
18366		if (!$str_obj{$abk}) {
18367		    my $s = Strassen->new($str_file{$abk});
18368		    if ($abk eq 'fz') {
18369			$s = $s->grepstreets(sub { $_->[Strassen::CAT] !~ m{(?:projected|inwork)} });
18370		    }
18371		    $str_obj{$abk} = $s;
18372		}
18373		push @$net_source, $str_obj{$abk};
18374		push @$net_source_abk, $abk;
18375	    };
18376	    warn "Cannot get Strassen for $abk: $@" if $@;
18377	}
18378    }
18379}
18380
18381sub make_plz {
18382    require PLZ;
18383    my $plz;
18384    if (defined $city && $city eq 'Berlin') {
18385	require PLZ::Multi;
18386	my @objs = ("Berlin.coords.data",
18387		    "Potsdam.coords.data",
18388		   );
18389	eval {
18390	    # XXX why?
18391	    my $plaetze = Strassen->new("plaetze");
18392	    push @objs, $plaetze if $plaetze;
18393	}; warn $@ if $@;
18394
18395	$plz = PLZ::Multi->new(@objs, -cache => 1);
18396    } else {
18397	$plz = PLZ->new;
18398    }
18399    $plz;
18400}
18401
18402sub make_net {
18403    my(%args) = @_;
18404    IncBusy($top);
18405    $progress->Init(-label => M("Berechnen des Stra�ennetzes")."...",
18406		    -dependents => $c,
18407		    -visible => 1,
18408		   );
18409
18410    my $user_dels;
18411    if ($net && $net->{_Deleted}) { # remember user dels
18412	require Data::Dumper;
18413	# clone:
18414	$user_dels = eval substr(Data::Dumper::Dumper($net->{_Deleted}), 7);
18415    }
18416
18417    undef $qualitaet_s_net;
18418    undef $handicap_s_net;
18419    undef $strcat_net;
18420    undef $radwege_net;
18421    undef $N_RW_net;
18422    undef $green_net;
18423    undef $unlit_streets_net;
18424    undef $steigung_net;
18425    undef $crossings;
18426
18427    eval {
18428	my $add_temp_blockings;
18429	my(@net_source, @net_source_abk);
18430	if ($net_type eq "r") {
18431	    if (!$str_obj{'r'}) {
18432		$str_obj{'r'} = new Strassen $str_file{'r'};
18433	    }
18434	    push @net_source,     $str_obj{'r'};
18435	    push @net_source_abk, 'r';
18436	} elsif ($net_type eq "us" || $net_type eq 'rus') {
18437	    my @abk = ($net_type eq 'us' ? qw(u b) : qw(u b r));
18438	    foreach (@abk) {
18439		if (!$str_obj{$_}) {
18440		    $str_obj{$_} = new Strassen $str_file{$_};
18441		}
18442		push @net_source,     $str_obj{$_};
18443		push @net_source_abk, $_;
18444	    }
18445	} elsif ($net_type eq 'wr') {
18446	    if (!$str_obj{'wr'}) {
18447		$str_obj{'wr'} = Strassen->new($str_file{'wr'});
18448	    }
18449	    push @net_source, $str_obj{'wr'};
18450	    push @net_source_abk, 'wr';
18451	} elsif ($net_type eq 'custom') {
18452	    add_custom_layers_to_net(\@net_source, \@net_source_abk);
18453	} else {
18454	    if ($str_obj{'l'}) {
18455		push @net_source,     $str_obj{'l'};
18456		push @net_source_abk, 'l';
18457	    }
18458	    if ($str_obj{'s'}) {
18459		my %s_restrict = %{$str_restrict{'s'}};
18460		if ($net_type eq 's-car') {
18461		    $str_obj{'sBAB'} = Strassen->new($str_file{'sBAB'})
18462			if !$str_obj{'sBAB'};
18463		    push @net_source,     $str_obj{'sBAB'};
18464		    push @net_source_abk, 'sBAB';
18465		    $s_restrict{'NN'} = 0;
18466		}
18467		my $is_restricted = 0;
18468#XXX use new_copy_restricted
18469		foreach (keys %s_restrict) {
18470		    if ($s_restrict{$_} == 0 &&
18471			$s_restrict{$_} ne 'P') { # Pl�tze
18472			$is_restricted = 1;
18473			last;
18474		    }
18475		}
18476		if ($is_restricted) {
18477		    my $restr_str = Strassen->new;
18478		    # XXX Copy at least the map global directive
18479		    if ($str_obj{'s'}->{GlobalDirectives}{map}) {
18480			@{ $restr_str->{GlobalDirectives}{map} } = @{ $str_obj{'s'}->{GlobalDirectives}{map} };
18481		    }
18482		    $str_obj{'s'}->init;
18483		    while(1) {
18484			my $ret = $str_obj{'s'}->next;
18485			last if !@{$ret->[Strassen::COORDS]};
18486			my($cat) = $ret->[Strassen::CAT] =~ m{^([^:]+)}; # strip attributes
18487			next if !$s_restrict{$cat};
18488			$restr_str->push($ret);
18489		    }
18490		    $restr_str->{File} = $str_obj{'s'}->file;
18491		    $restr_str->{Id}   = $str_obj{'s'}->id . "_restr_" . join("_", grep { $s_restrict{$_} } keys %s_restrict);
18492		    push @net_source,     $restr_str;
18493		    push @net_source_abk, 's';
18494		} else {
18495		    if ($str_obj{'s'}) {
18496			push @net_source,     $str_obj{'s'};
18497			push @net_source_abk, 's';
18498		    }
18499		}
18500	    }
18501	    while(my($token, $bool) = each %add_net) {
18502		next if !$bool;
18503		if ($token eq 'custom') {
18504		    add_custom_layers_to_net(\@net_source, \@net_source_abk);
18505		} else {
18506		    $str_obj{$token} = Strassen->new($str_file{$token})
18507			if !$str_obj{$token};
18508		    push @net_source, $str_obj{$token};
18509		    push @net_source_abk, $token;
18510		}
18511	    }
18512	    if (!@net_source) { # XXX n�
18513		my(@str_types) = ('s');
18514		if ($args{'-l_add'}) {
18515		    push @str_types, 'l';
18516		}
18517		foreach my $str_type (@str_types) {
18518		    cache_decider_init();
18519		    my $str = new Strassen $str_file{$str_type};
18520		    if (cache_decider() && $coord_system eq 'standard') {
18521			$str_obj{$str_type} = $str;
18522		    }
18523		    push @net_source,     $str;
18524		    push @net_source_abk, $str_type;
18525		}
18526	    }
18527
18528	    if ($show_active_temp_blockings && $current_temp_blockings_ms) {
18529		$add_temp_blockings = 1;
18530	    }
18531	}
18532
18533	if (@net_source == 0) {
18534	    die "Netz kann nicht berechnet werden, keine Sourcen";
18535	} elsif (@net_source == 1) {
18536	    $net = new StrassenNetz $net_source[0];
18537	} else {
18538	    $multistrassen = new MultiStrassen @net_source;
18539	    $net = new StrassenNetz $multistrassen;
18540	}
18541
18542	$net->set_source(@net_source);
18543	$net->set_source_abk(@net_source_abk);
18544
18545	my $make_net_all = sub {
18546	    if (defined $global_search_args{Algorithm} &&
18547		$global_search_args{Algorithm} =~ /^C-A\*-2/) {
18548		$net->use_data_format($StrassenNetz::FMT_MMAP);
18549	    } else {
18550		$net->use_data_format($StrassenNetz::FMT_HASH);
18551	    }
18552	    $net->make_net(Progress => $progress,
18553			   UseCache => 0,
18554			  );
18555
18556	    if ($net_type eq 's' || $net_type eq 's-car') {
18557		my @sperre_type;
18558		foreach ('einbahn', 'einbahn-strict', 'sperre', 'tragen', 'wegfuehrung') {
18559		    push @sperre_type, $_ if $sperre{$_};
18560		}
18561		if (@sperre_type) {
18562		    eval {
18563			$net->make_sperre($sperre_file,
18564					  Type => \@sperre_type,
18565					  SpecialVehicle => get_special_vehicle(),
18566					 );
18567		    }; warn $@ if $@;
18568		    if ($net_type eq 's-car') {
18569			eval {
18570			    $net->make_sperre("$datadir/gesperrt_car",
18571					      Type => \@sperre_type,
18572					      # no SpecialVehicle defined for vars
18573					     );
18574			}; warn $@ if $@;
18575		    }
18576		}
18577		if ($sperre{'Q3'}) {
18578		    eval {
18579			$net->make_sperre("qualitaet_s", Type => ['Q3']);
18580			if ($str_obj{'l'}) {
18581			    $net->make_sperre("qualitaet_l", Type => ['Q3']);
18582			}
18583		    }; warn $@ if $@;
18584		}
18585		if ($use_faehre) {
18586		    $net->add_faehre($str_file{'e'});
18587		}
18588	    } elsif ($net_type eq 'us' || $net_type eq 'rus') {
18589		my @abk = ($net_type eq 'us' ? qw(u b) : qw(u b r));
18590
18591		my $sperre_s = MultiStrassen->new(map { $p_file{"sperre_$_"} } @abk);
18592		$net->make_sperre($sperre_s, Type => "sperre");
18593
18594		my @bhf_source;
18595		foreach (@abk) {
18596		    if (!$p_obj{$_}) {
18597			$p_obj{$_} = new Strassen $p_file{$_};
18598		    }
18599		    push @bhf_source, $p_obj{$_};
18600		}
18601		my $bhf_obj = new MultiStrassen @bhf_source;
18602		$handicap_s_net = StrassenNetz->new(Strassen->new);
18603		my $h_net = $handicap_s_net->{Net} = {};
18604		$net->add_umsteigebahnhoefe
18605		    ($bhf_obj, -addmapfile => 'umsteigebhf',
18606		     -cb => sub {
18607			 my($self, $p1, $p2, $entf, $name) = @_;
18608			 $h_net->{$p1}{$p2} = "q4"; # XXX just a hack to see some results... A best solution is to use the forthcoming penalty solution for the Marathon
18609		     });
18610	    } elsif ($net_type eq 'wr') {
18611		# nothing special here...
18612	    }
18613	};
18614
18615	if ($use_mldbm) {
18616	    eval {
18617		warn "Trying MLDBM cache...\n";
18618		$net->load_net_mldbm;
18619		warn "OK!\n";
18620	    };
18621	    if ($@) {
18622		$make_net_all->();
18623		eval {
18624		    warn "Saving MLDBM cache...\n";
18625		    $net->save_net_mldbm;
18626		    warn "OK!\n";
18627		};
18628		warn __LINE__ . ": $@" if $@;
18629	    }
18630	} else {
18631	    $make_net_all->();
18632	}
18633
18634	if ($add_temp_blockings) {
18635	    add_temp_blockings_to_net();
18636	}
18637
18638	if ($verbose) {
18639	    warn $net->statistics;
18640	}
18641	status_message("");
18642	delete $pending{'recalc-net'};
18643    };
18644    status_message($@, 'err') if ($@);
18645
18646    if ($user_dels) {
18647	restore_user_dels($net, $user_dels);
18648    }
18649
18650    $progress->Finish;
18651    DecBusy($top);
18652}
18653
18654sub make_qualitaet_net {
18655    if (!$qualitaet_s_net) {
18656	# XXX hmmm, fails fataly if any of the layers is missing
18657	eval {
18658	    $qualitaet_s_net = StrassenNetz->new
18659		(MultiStrassen->new(Strassen->new("qualitaet_s"),
18660				    Strassen->new("qualitaet_l")));
18661	    $qualitaet_s_net->make_net_cat;
18662	};
18663	if ($@ && !$no_original_datadir) {
18664	    status_message($@, "info");
18665	}
18666    }
18667    $qualitaet_s_net;
18668}
18669
18670sub make_handicap_net {
18671    if (!$handicap_s_net) {
18672	# XXX hmmm, fails fataly if any of the layers is missing
18673	eval {
18674	    my @s = (Strassen->new("handicap_s"),
18675		     Strassen->new("handicap_l"),
18676		    );
18677	    $handicap_s_net = StrassenNetz->new(MultiStrassen->new(@s));
18678	    $handicap_s_net->make_net_cat;
18679	};
18680	if ($@ && !$no_original_datadir) {
18681	    status_message($@, "info");
18682	}
18683    }
18684    $handicap_s_net;
18685}
18686
18687sub make_comments_net {
18688    if (!$str_obj{"comm"}) {
18689	$str_obj{'comm'} = _get_comments_obj();
18690    }
18691    if ($str_obj{"comm"}) {
18692	$comments_net = new StrassenNetz $str_obj{"comm"};
18693	$comments_net->make_net_cat(-net2name => 1,
18694				    -multiple => 1,
18695				    -obeydir => 1);
18696    }
18697}
18698
18699# Erzeugt einen Hash aller Kreuzungen
18700### AutoLoad Sub
18701sub all_crossings {
18702    if (!$crossings || !%$crossings) {
18703	my $s = $multistrassen ? $multistrassen : $str_obj{'s'};
18704	return if !$s;
18705	$crossings = $s->all_crossings(RetType => 'hash',
18706				       UseCache => 1);
18707    }
18708    $crossings;
18709}
18710
18711# User definable blockings
18712sub load_user_dels {
18713    my $file = shift || "$bbbike_configdir/userdels.bbd";
18714    $net->load_user_deletions
18715	($file,
18716	 -oncallback  => sub { set_usercross_image(@_) }, #XXX do not duplicate
18717	 -offcallback => sub { # XXX do not duplicate
18718	     my($xy1,$xy2) = @_;
18719	     $c->delete("delnet-$xy1-$xy2");
18720	     $c->delete("delnet-$xy2-$xy1");
18721	 },
18722	);
18723    restore_cursor();
18724}
18725
18726sub _save_umask (&) {
18727    my $code = shift;
18728    my $old_umask;
18729    eval {
18730	$old_umask = umask;
18731    };
18732    eval {
18733	$code->();
18734    };
18735    my $err = $@;
18736    if (defined $old_umask) {
18737	umask $old_umask;
18738    }
18739    die $err if $err;
18740}
18741
18742sub save_user_dels {
18743    my $file = shift || "$bbbike_configdir/userdels.bbd";
18744    my(%args) = @_;
18745    _save_umask {
18746	umask 022;
18747	$net->save_user_deletions($file, %args) if $net;
18748    };
18749}
18750
18751sub restore_user_dels {
18752    my($net, $user_dels) = @_;
18753    # restore user deletions
18754    while(my($k1,$v1) = each %$user_dels) {
18755	while(my($k2,$v2) = each %$v1) {
18756	    my $ok;
18757	    if (exists $net->{Net}{$k1}{$k2}) {
18758		$net->{_Deleted}{$k1}{$k2} = $net->{Net}{$k1}{$k2};
18759		$ok++;
18760	    }
18761	    if (exists $net->{_Deleted}{$k1}{$k2}) {
18762		$ok++;
18763	    }
18764	    if (exists $net->{Net}{$k2}{$k1}) {
18765		$net->{_Deleted}{$k2}{$k1} = $net->{Net}{$k2}{$k1};
18766		$ok++;
18767	    }
18768	    if (exists $net->{_Deleted}{$k2}{$k1}) {
18769		$ok++;
18770	    }
18771	    if ($ok) {
18772		$net->del_net($k1, $k2, 2);
18773		# image still exists (well it should)
18774	    } else {
18775		$c->delete("delnet-$k1-$k2");
18776		$c->delete("delnet-$k2-$k1");
18777	    }
18778	}
18779    }
18780}
18781
18782# -force => 1: be quiet and do not ask or warn
18783sub delete_user_dels {
18784    my(%args) = @_;
18785
18786    my($any_delnet_tag) = $c->find("withtag", "delnet");
18787    if (!defined $any_delnet_tag) {
18788	if (!$args{-force}) {
18789	    $top->messageBox(-message => M"Keine benutzerdefinierten Sperrungen vorhanden.");
18790	}
18791	return;
18792    }
18793
18794    if ($args{-force} ||
18795	$top->messageBox(-message => M"Alle benutzerdefinierten Sperrungen l�schen?",
18796			 -type => "YesNo",
18797			 -icon => "question") =~ /^yes/i) {
18798	$net->remove_all_from_deleted(sub {
18799					  my($xy1,$xy2) = @_;
18800					  $c->delete("delnet-$xy1-$xy2");
18801					  $c->delete("delnet-$xy2-$xy1");
18802				      });
18803	restore_cursor();
18804    }
18805}
18806
18807# Return "x,y"
18808sub set_coords_str {
18809    my($c, @tags) = @_;
18810    @tags = $c->gettags('current') if !@tags;
18811    return if !@tags;
18812    if ($tags[0] eq 'p' or $tags[0] eq 'pp' or $tags[0] =~ /^lsa/) {
18813	$tags[1];
18814    } elsif ($tags[0] =~ /^[sSlL]$/ ||
18815	     $add_net{fz} && $tags[0] eq 'fz' ||
18816	     ($net_type eq 's-car' && $tags[0] eq 'sBAB')
18817	     # XXX weitere Ausnahmen f�r $add_net{is} etc. definieren
18818	    ) {
18819	my($pos, @points) = nearest_line_points_mouse($c, @tags);
18820	make_net() if !$net;
18821	if ($net->can("adjust_to_nearest")) {
18822	    $points[0] = [ split /,/,
18823			   $net->adjust_to_nearest(join ",", @{$points[0]})
18824			 ];
18825	} else {
18826	    $net->add_net($pos, @points);
18827	}
18828	my($x, $y) = @{$points[0]};
18829	Route::_coord_as_string([$x,$y]);
18830    } else {
18831	my @accept_tags = qw(s l p pp lsa);
18832	if ($net_type eq 's-car') { push @accept_tags, 'sBAB' }
18833	my($item, @tags) = find_below($c, @accept_tags);
18834	return if !defined $item;
18835	set_coords($c, @tags); # hoffentlich keine Endlosrekursion...
18836	#die "Tag [@tags] ist weder p, pp, s noch l sollte nicht vorkommen!";
18837    }
18838}
18839
18840### AutoLoad Sub
18841sub set_coords_rbahn {
18842    my($c, @tags) = @_;
18843    @tags = $c->gettags('current') if !@tags;
18844    return if !@tags;
18845    if ($tags[0] =~ /^r-[bf]g/) {
18846	$tags[1];
18847    } else {
18848	my($item, @tags) = find_below($c, qw/r-bg r-fg/);
18849	return if !defined $item;
18850	set_coords($c, @tags); # hoffentlich keine Endlosrekursion...
18851	#die "Tag [@tags] ist weder p, pp, o, s noch l sollte nicht vorkommen!";
18852    }
18853}
18854
18855### AutoLoad Sub
18856sub set_coords_usbahn {
18857    my($c, @tags) = @_;
18858    @tags = $c->gettags('current') if !@tags;
18859    return if !@tags;
18860    if ($tags[0] =~ /^[ub]-[bf]g/) {
18861	$tags[1];
18862    } else {
18863	my($item, @tags) = find_below($c, qw/u-bg b-bg u-fg b-fg/);
18864	return if !defined $item;
18865	set_coords($c, @tags); # hoffentlich keine Endlosrekursion...
18866	#die "Tag [@tags] ist weder p, pp, o, s noch l sollte nicht vorkommen!";
18867    }
18868}
18869
18870### AutoLoad Sub
18871sub set_coords_bahn {
18872    my($c, @tags) = @_;
18873    @tags = $c->gettags('current') if !@tags;
18874    return if !@tags;
18875    if ($tags[0] =~ /^[ubr]-[bf]g/) {
18876	$tags[1];
18877    } else {
18878	my($item, @tags) = find_below($c, qw/u-bg b-bg u-fg b-fg r-bg r-fg/);
18879	return if !defined $item;
18880	set_coords($c, @tags); # hoffentlich keine Endlosrekursion...
18881	#die "Tag [@tags] ist weder p, pp, o, s noch l sollte nicht vorkommen!";
18882    }
18883}
18884
18885### AutoLoad Sub
18886sub set_coords_wasserrouten {
18887    my($c, @tags) = @_;
18888    if ($tags[0] eq 'wr') {
18889	my($pos, @points) = nearest_line_points_mouse($c, @tags);
18890	make_net() if !$net;
18891	if ($net->can("adjust_to_nearest")) {
18892	    $points[0] = [ split /,/,
18893			   $net->adjust_to_nearest(join ",", @{$points[0]})
18894			 ];
18895	} else {
18896	    $net->add_net($pos, @points);
18897	}
18898	my($x, $y) = @{$points[0]};
18899	Route::_coord_as_string([$x,$y]);
18900    } else {
18901	my($item, @tags) = find_below($c, qw/wr/);
18902	return if !defined $item;
18903	set_coords($c, @tags); # hoffentlich keine Endlosrekursion...
18904	#die "Tag [@tags] ist weder p, pp, s noch l sollte nicht vorkommen!";
18905    }
18906}
18907
18908# Return "x,y"
18909### AutoLoad Sub
18910sub set_coords_custom {
18911    my($c, @tags) = @_;
18912    @tags = $c->gettags('current') if !@tags;
18913    return if !@tags;
18914    if ($tags[0] =~ /^L\d$/) {
18915	my($pos, @points) = nearest_line_points_mouse($c, @tags);
18916	make_net() if !$net;
18917	if ($net->can("adjust_to_nearest")) {
18918	    $points[0] = [ split /,/,
18919			   $net->adjust_to_nearest(join ",", @{$points[0]})
18920			 ];
18921	} else {
18922	    $net->add_net($pos, @points);
18923	}
18924	my($x, $y) = @{$points[0]};
18925	Route::_coord_as_string([$x,$y]);
18926    } else {
18927	my($item, @tags) = find_below_rx($c, ['^L\d'], [0]);
18928	return if !defined $item;
18929	set_coords($c, @tags); # hoffentlich keine Endlosrekursion...
18930    }
18931}
18932
18933### AutoLoad Sub
18934sub user_edit_street {
18935    if (!$net) {
18936	make_net();
18937    }
18938    status_message("Can't make net", "die") if !$net;
18939    my(@click_items) = ($net_type eq 's' || $net_type eq 's-car'
18940			? qw(s l fz)
18941			: ($net_type =~ /^(r|us|rus)$/
18942			   ? map { $_ eq 's' ? 'b' : $_ } split //, $net_type
18943			   : ($net_type eq 'wr'
18944			      ? qw(wr)
18945			      : warn "Unhandled net type $net_type"
18946			     )
18947			  )
18948		       );
18949    if (($net_type eq 's' || $net_type eq 's-car') && $use_faehre) {
18950	push @click_items, "e";
18951    }
18952    if ($net_type eq 's-car') {
18953	push @click_items, 'sBAB'; # XXX check!
18954    }
18955    my($item, @tags) = find_below($c, @click_items);
18956    if (defined $item) {
18957	my($pos, @points) = nearest_line_points_mouse($c, @tags);
18958	my($xy1,$xy2) = (join(",",@{$points[1]}), join(",",@{$points[2]}));
18959	$net->toggle_deleted_line
18960	    ($xy1,$xy2,
18961	     sub {
18962		 my($xy1,$xy2) = @_;
18963		 set_usercross_image($xy1,$xy2)
18964	     },
18965	     sub {
18966		 my($xy1,$xy2) = @_;
18967		 $c->delete("delnet-$xy1-$xy2");
18968		 $c->delete("delnet-$xy2-$xy1");
18969		 restore_cursor();
18970	     });
18971    }
18972}
18973
18974### AutoLoad Sub
18975sub set_usercross_image {
18976    my($xy1,$xy2) = @_;
18977    if (!$usercross_photo) {
18978	$usercross_photo =
18979	    load_photo($top, 'usercross');
18980    }
18981    my($x1,$y1,$x2,$y2) = (split(/,/,$xy1), split(/,/,$xy2));
18982    my($midx,$midy) = (int(($x2-$x1)/2+$x1), int(($y2-$y1)/2+$y1));
18983    ($midx,$midy) = transpose($midx, $midy);
18984    $c->createImage($midx+2,$midy-1,
18985		    -image => $usercross_photo,
18986		    -tags => ["delnet", "delnet-$xy1-$xy2"]);
18987}
18988
18989### AutoLoad Sub
18990sub save_cursor {
18991    $c->{SavedCursor} = $c->get_cursor;
18992    if (!defined $c->{SavedCursor}) {
18993	$c->{SavedCursor} = "__DEFAULT__";
18994    }
18995}
18996
18997### AutoLoad Sub
18998sub restore_cursor {
18999    if ($c->{SavedCursor}) {
19000	if ($c->{SavedCursor} eq '__DEFAULT__') {
19001	    $c->set_cursor(undef);
19002	} else {
19003	    $c->set_cursor($c->{SavedCursor});
19004	}
19005	undef $c->{SavedCursor};
19006    }
19007}
19008
19009sub set_cursor {
19010    my($type, $fallback) = @_;
19011    if (!defined $fallback && defined $type) {
19012	if ($type eq 'ziel') {
19013	    $fallback = 'right_ptr';
19014	}
19015    }
19016    if (!defined $type) {
19017	#$c->configure(-cursor => undef);
19018	$c->set_cursor(undef);
19019	status_message('');
19020    } elsif (exists $cursor{$type}) {
19021	if (exists $cursor_mask{$type}) {
19022	    #$c->configure(-cursor =>
19023	    $c->set_cursor(['@' . $cursor{$type},
19024			    $cursor_mask{$type},
19025			    'black', 'white']);
19026	} else {
19027	    #$c->configure(-cursor =>
19028	    $c->set_cursor(['@' . $cursor{$type}, 'black']);
19029	}
19030    } elsif (defined $fallback) {
19031	$c->set_cursor($fallback);
19032    } else {
19033	#$c->configure(-cursor => undef);
19034	$c->set_cursor(undef);
19035    }
19036    if (defined $type && $type eq 'start') {
19037	status_message(M"Start ausw�hlen");
19038    } elsif (defined $type && $type eq 'ziel') {
19039	status_message(M"Ziel ausw�hlen");
19040    }
19041}
19042
19043### AutoLoad Sub
19044sub set_cursor_data {
19045    my($data, $persistent, $cur_data) = @_;
19046    my $tmpfile = "$tmpdir/cursor.$<-$$";
19047    if ($persistent) {
19048	$tmpfile .= "_" . $persistent;
19049    }
19050    if ($os eq 'win') {
19051	if ($cur_data) {
19052	    $tmpfile .= ".cur";
19053	} else {
19054	    $c->set_cursor(undef);
19055	    return;
19056	}
19057    } else {
19058	$tmpfile .= ".xbm";
19059    }
19060    if (open(C, ">$tmpfile")) {
19061        print C ($os eq 'win' ? $cur_data : $data);
19062	close C;
19063	#$c->configure(-cursor => ['@' . $tmpfile, 'black']);
19064	if ($os eq 'win') {
19065	    $c->set_cursor(['@' . $tmpfile]);
19066	} else {
19067	    $c->set_cursor(['@' . $tmpfile, 'black']);
19068	}
19069	if (!$persistent) {
19070	    unlink $tmpfile;
19071	} else {
19072	    $tmpfiles{$tmpfile}++;
19073	}
19074    } else {
19075	warn "Can't set cursor data with file $tmpfile: $!";
19076	#$c->configure(-cursor => undef);
19077	$c->set_cursor(undef);
19078    }
19079}
19080
19081### AutoLoad Sub
19082sub set_route_start_street {
19083    my $street = shift;
19084    my $coord = choose_from_plz(-str => $street,
19085				-noshow => 0);
19086    set_route_start($coord) if $coord;
19087}
19088
19089### AutoLoad Sub
19090sub set_route_ziel_street {
19091    my $street = shift;
19092    my $coord = choose_from_plz(-str => $street,
19093				-noshow => 1);
19094    set_route_ziel($coord) if $coord;
19095}
19096
19097# Setzt den Start-Punkt der Route
19098# Eingabe ist "$x,$y" (realcoords)
19099# XXX viel Redundanz mit search_route_mouse!
19100### AutoLoad Sub
19101sub set_route_start {
19102    my $xy = shift;
19103    return if !defined $xy;
19104    my $search_route_start = $xy;
19105
19106    if (!$net) { make_net() }
19107
19108    if (!$net->reachable($search_route_start)) {
19109	my $new_search_route_start = $net->fix_coords($search_route_start);
19110	if (!$new_search_route_start) {
19111	    $top->bell;
19112	    status_message(M"Der Startort ist nicht erreichbar", 'warn');
19113	    undef $search_route_start;
19114	    return; #goto CLEANUP;
19115	} else {
19116	    $search_route_start = $new_search_route_start;
19117	}
19118    }
19119
19120    resetroute();
19121
19122    # XXX vielleicht sollte man das unabh�ngige Setzen von Start/Ziel
19123    # erm�glichen (z.B. zuerst Ziel, dann Start ausw�hlen). Z.Zt.
19124    # mu� $search_route_ziel undefiniert werden.
19125    #XXXundef $search_route_ziel;
19126    $search_route_flag = 'ziel';
19127    my($x, $y) = transpose(split(/,/, $search_route_start));
19128    set_flag('start', $x, $y);
19129    set_cursor('ziel');
19130
19131    @search_route_points = [$search_route_start, POINT_MANUELL];
19132
19133    return;
19134}
19135
19136# Setzt den Ziel-Punkt der Route
19137# Eingabe ist "$x,$y"
19138# XXX viel Redundanz mit search_route_mouse_cont!
19139### AutoLoad Sub
19140sub set_route_ziel {
19141    my $xy = shift;
19142    my(%args) = @_;
19143    return if !defined $xy;
19144
19145#XXX dieser Teil ist halbnotwendig, falls der Startpunkt manuell
19146# gesetzt wurde und nearest_line_points aufgerufen werden muss.
19147# Allerdings funktioniert nearest_line_points anscheinend nicht ohne
19148# gemaltes Stra�ennetz, wohingegen die Telefonbuch-Stra�en-Auswahl
19149# ganz gut ohne gemaltes Stra�ennetz funktioniert.
19150# Deshalb vorerst disabled.
19151#
19152#     if (@realcoords) {
19153# 	if ($net->reachable
19154# 	    (Route::_coord_as_string($realcoords[$#realcoords]))) {
19155# 	    $search_route_start
19156# 	      = Route::_coord_as_string($realcoords[$#realcoords]);
19157# 	}
19158# 	my($tx, $ty) = transpose(@{$realcoords[$#realcoords]});
19159# 	my($pos, @points) = nearest_line_points_xy($tx, $ty);
19160# 	if (@points) { # XXX wirklich?
19161# 	    $net->add_net($pos, @points);
19162# 	    $search_route_start = Route::_coord_as_string($points[0]);
19163# 	} else {
19164# 	    addpoint_inter();
19165# 	    return;
19166# 	    #		$search_route_start = $search_route_ziel;
19167# 	}
19168#     }
19169
19170#     my $this_search_route_start = $search_route_ziel;
19171#     if (!defined $this_search_route_start) {
19172# 	$this_search_route_start = $search_route_start;
19173# 	if (!defined $this_search_route_start) {
19174# 	    return;
19175# 	}
19176#     }
19177
19178    my $this_search_route_start = $search_route_points[-1]->[SRP_COORD];
19179    return if (!defined $this_search_route_start);
19180    my $search_route_ziel = $xy;
19181
19182    if (!$net) { make_net() }
19183
19184    if (!$net->reachable($search_route_ziel)) {
19185	my $new_search_route_ziel = $net->fix_coords($search_route_ziel);
19186	if (!$new_search_route_ziel) {
19187	    $top->bell;
19188	    status_message(M"Der Zielort ist nicht erreichbar", 'warn');
19189	    undef $search_route_ziel;
19190	    return; #goto CLEANUP;
19191	} else {
19192	    $search_route_ziel = $new_search_route_ziel;
19193	}
19194    }
19195    # XXX nicht n�tig? my($x, $y) = transpose(split(/,/, $search_route_ziel));
19196    search_route($this_search_route_start, $search_route_ziel,
19197		 undef, 'cont', %args);
19198    update_route_strname();
19199}
19200
19201sub search_route_mouse {
19202    my $by_button = shift;
19203    $map_mode = MM_SEARCH;
19204    if (!$search_route_flag) {
19205	$search_route_flag = 'start';
19206
19207	if (!$lowmem) {
19208	    if ($net_type eq 's' || $net_type eq 's-car') {
19209		if (!$net and ($str_draw{'s'} || $str_draw{'l'})) {
19210		    make_net();
19211		}
19212	    }
19213	    # XXX $str_draw{'r'} ueberprfen, wenn im RB-Mode
19214	    $net->reset if ($net);
19215	} else {
19216	    print STDERR M"`Stra�ennetz neu berechnen' vor Suche anklicken!\n";
19217	}
19218
19219	set_cursor('start');
19220	return;
19221    } elsif ($search_route_flag eq 'start') {
19222	if ($by_button) {
19223	    undef $search_route_flag;
19224	    goto CLEANUP;
19225	}
19226	my $search_route_start = set_coords($c);
19227	return if !defined $search_route_start;
19228
19229	make_net() if !$net;
19230	if (!$net->reachable($search_route_start)) {
19231	    $top->bell;
19232	    status_message(M"Der Startort ist nicht erreichbar", 'warn');
19233	    undef $search_route_start;
19234	    return; #goto CLEANUP;
19235	}
19236	$search_route_flag = 'ziel';
19237	my($x, $y) = transpose(split(/,/, $search_route_start));
19238	set_flag('start', $x, $y);
19239	set_cursor('ziel');
19240	@search_route_points = [$search_route_start, POINT_MANUELL];
19241	return;
19242    } else { # ziel
19243	if ($by_button) {
19244	    undef $search_route_flag;
19245	    goto CLEANUP;
19246	}
19247	my $search_route_ziel = set_coords($c);
19248	return if !defined $search_route_ziel;
19249	if (!$net->reachable($search_route_ziel)) {
19250	    $top->bell;
19251	    status_message(M"Der Zielort ist nicht erreichbar", 'warn');
19252	    undef $search_route_ziel;
19253	    return; #goto CLEANUP;
19254	}
19255	status_message('');
19256	my $this_search_route_start = $search_route_points[-1]->[SRP_COORD];
19257	return if !defined $this_search_route_start;
19258	search_route($this_search_route_start, $search_route_ziel);
19259
19260	# XXX duplicate code (see above)
19261	undef $search_route_flag;
19262	update_route_strname();
19263	search_route_mouse_cont();
19264	return;
19265    }
19266
19267  CLEANUP:
19268    undef $search_route_flag;
19269    set_cursor(undef);
19270}
19271
19272# Setzt das Suchen einer Route vom bisherigen Endpunkt fort.
19273# Der neue Zielpunkt wurde gerade per Maus angeklickt.
19274sub search_route_mouse_cont {
19275    if (!$search_route_flag) {
19276	# ??? Es existiert noch kein Startpunkt.
19277	$search_route_flag = 'ziel_cont';
19278	set_cursor('ziel');
19279	return;
19280    } else {
19281	my $this_search_route_start;
19282	if (!$net) { make_net() } # Netz wird neu berechnet
19283	if (@realcoords) { # Es existieren bereits Punkte in der Route.
19284	    if ($net->reachable
19285		(Route::_coord_as_string($realcoords[-1]))) {
19286		# Der vorherige Zielpunkt ist direkt erreichbar (Punkt
19287		# existiert in der Datenbank)
19288		$this_search_route_start
19289		    = Route::_coord_as_string($realcoords[-1]);
19290	    } else {
19291		# Wann tritt dieser Fall auf?
19292		warn "In search_route_mouse_cont, 2nd case";
19293		my($tx, $ty) = transpose(@{$realcoords[-1]});
19294		my($pos, @points) = nearest_line_points_xy($tx, $ty);
19295		if (@points) { # XXX wirklich?
19296		    $net->add_net($pos, @points);
19297		    $this_search_route_start = Route::_coord_as_string($points[0]);
19298		    @{$realcoords[-1]} = @{$points[0]}; # XXXX workaround
19299		    # der aber nicht stimmt, wenn der letzte Punkt �ber
19300		    # freehand eingegeben wurde ...
19301		    # sigh, der ganze search_route_mouse_cont-Kram braucht eine
19302		    # kr�ftige �berarbeitung ... :-(
19303		} else {
19304		    addpoint_inter();
19305		    return;
19306		}
19307	    }
19308	}
19309	my $search_route_ziel = set_coords($c);
19310	return if !defined $search_route_ziel;
19311	if (!$net->reachable($search_route_ziel)) {
19312	    $top->bell;
19313	    status_message(M"Der Zielort ist nicht erreichbar", 'warn');
19314	    #$search_route_ziel = $this_search_route_start;
19315	    #undef $search_route_start;
19316	    return; #goto CLEANUP;
19317	}
19318	status_message('');
19319	search_route($this_search_route_start, $search_route_ziel,
19320		     undef, 'cont');
19321
19322	update_route_strname();
19323    }
19324  CLEANUP:
19325}
19326
19327sub plugin_menu {
19328    my $opbm = shift;
19329    $opbm->command(-label => M"Plugin laden",
19330		   -command => sub {
19331		       my($file) = $top->getOpenFile
19332			   (-title => M("Plugin laden"),
19333			    -filetypes => [[M"Perl-Module" => '.pm'],
19334					   [M"Alle Dateien" => '*']],
19335			    -initialdir => "$FindBin::RealBin/plugins",
19336			   );
19337		       if (defined $file) {
19338			   load_plugin($file);
19339		       }
19340		   });
19341    if (0) { # XXX The old Plugin lister could be removed completely some day
19342	$opbm->command(-label => M"Alle Plugins zeigen (alt)",
19343		       -command => sub {
19344			   require BBBikePlugin;
19345			   BBBikePlugin::find_all_plugins($FindBin::RealBin, $top);
19346		       });
19347    } else {
19348	$opbm->command(-label => M"Alle Plugins zeigen",
19349		       -command => sub {
19350			   require BBBikePluginLister;
19351			   BBBikePluginLister::plugin_lister($top, $FindBin::RealBin);
19352		       });
19353    }
19354}
19355
19356sub menu_entry_up_down {
19357    my($menu, $tag_group) = @_;
19358    my(@tags) = @$tag_group;
19359    $menu->separator;
19360    my $x; # dummy
19361    $menu->radiobutton(-label => M"oben zeichnen",
19362		       -variable => \$x,
19363		       -command => sub {
19364			   foreach (@tags) { special_raise($_, 0) }
19365			   restack();
19366		       });
19367    $menu->radiobutton(-label => M"normal",
19368		       -variable => \$x,
19369		       -command => sub {
19370			   foreach (@tags) { special_normal($_, 0) }
19371			   restack();
19372		       });
19373    $menu->radiobutton(-label => M"unten zeichnen",
19374		       -variable => \$x,
19375		       -command => sub {
19376			   foreach (reverse @tags) { special_lower($_, 0) }
19377			   restack();
19378		       });
19379}
19380
19381sub menu_entry_choose_ort {
19382    my($menu, $abk, %args) = @_;
19383    if (exists $str_attrib{$abk}) {
19384	$menu->checkbutton(-label => $str_attrib{$abk}->[ATTRIB_PLURAL],
19385			   -variable => \$str_draw{$abk},
19386			   -command => sub { plot('str',$abk); },
19387			   (defined $args{'-accelerator'} ?
19388			    (-accelerator => $args{'-accelerator'}) :
19389			    (),
19390			   ),
19391			  );
19392	my %str_args;
19393	if (exists $args{'-strchooseortargs'}) {
19394	    %str_args = %{$args{'-strchooseortargs'}};
19395	}
19396	$menu->command(-label => Mfmt("%s ausw�hlen", $str_attrib{$abk}->[ATTRIB_SINGULAR]),
19397		       -command => sub { choose_ort('s', $abk, %str_args) });
19398	if ($args{'-strextrachoosemenuaction'}) {
19399	    $args{'-strextrachoosemenuaction'}->();
19400	}
19401	if (0) { # XXX Habe ich schon seit Jahren nicht genutzt!
19402	    $menu->command
19403	      (-label => Mfmt("Liste der %s neu erstellen",
19404			      $str_attrib{$abk}->[ATTRIB_PLURAL]),
19405	       -command => sub { choose_ort('s', $abk, -rebuild => 1,
19406					    %str_args) });
19407	    $menu->command
19408	      (-label => Mfmt("Update der %s", $str_attrib{$abk}->[ATTRIB_PLURAL]),
19409	       -command => sub { undef $str_obj{$abk};
19410				 plot('str',$abk);
19411			     });
19412	    $menu->command
19413	      (-label => Mfmt("Schnelles Update der %s",
19414			      $str_attrib{$abk}->[ATTRIB_PLURAL]),
19415	       -command => sub { plot('str',$abk, FastUpdate => 1); });
19416	}
19417	if ($advanced) {
19418	    $menu->command
19419	      (-label => "Lazy drawing",
19420	       -command => sub {
19421		   $str_draw{$abk} = 1 - $str_draw{$abk};
19422		   plot('str',$abk, -lazy => 1);
19423	       });
19424	}
19425	if ($args{'-strblockings'}) {
19426	    my $sperre_abk = 'sperre_'.$abk;
19427	    $menu->checkbutton
19428		(-label => M"gesperrte Strecken",
19429		 -variable => \$p_draw{$sperre_abk},
19430		 -command => sub {
19431		     plot_sperre($p_file{$sperre_abk},
19432				 -abk => $sperre_abk);
19433		 },
19434		);
19435	}
19436    }
19437
19438    if (exists $p_attrib{$abk} && exists $str_attrib{$abk}) {
19439	$menu->separator;
19440    }
19441
19442    if (exists $p_attrib{$abk}) {
19443	$menu->checkbutton(-label => $p_attrib{$abk}->[ATTRIB_PLURAL],
19444			   -variable => \$p_draw{$abk},
19445			   -command => sub { plot('p',$abk) },
19446			   (defined $args{'-accelerator_p'} ?
19447			    (-accelerator => $args{'-accelerator_p'}) :
19448			    (),
19449			   ),
19450			  );
19451	my %p_args;
19452	if (exists $args{'-pchooseortargs'}) {
19453	    %p_args = %{$args{'-pchooseortargs'}};
19454	}
19455	$menu->command(-label => Mfmt("%s ausw�hlen", $p_attrib{$abk}->[ATTRIB_SINGULAR]),
19456		       -command => sub { choose_ort('p', $abk, %p_args) });
19457	if ($args{'-pextrachoosemenuaction'}) {
19458	    $args{'-pextrachoosemenuaction'}->();
19459	}
19460	if (0) { # XXX Habe ich schon seit Jahren nicht genutzt!
19461	    $menu->command
19462	      (-label => Mfmt("Liste der %s neu erstellen", $p_attrib{$abk}->[ATTRIB_PLURAL]),
19463	       -command => sub { choose_ort('p', $abk, -rebuild => 1) });
19464	    $menu->command
19465	      (-label => Mfmt("Update der %s", $p_attrib{$abk}->[ATTRIB_PLURAL]),
19466	       -command => sub { undef $p_obj{$abk};
19467				 plot_point($abk);
19468			     });
19469	    $menu->command
19470	      (-label => Mfmt("Schnelles Update der %s",
19471			      $p_attrib{$abk}->[ATTRIB_PLURAL]),
19472	       -command => sub { plot('p',$abk, FastUpdate => 1); });
19473	}
19474	if ($advanced) {
19475	    $menu->command
19476	      (-label => "Lazy drawing",
19477	       -command => sub {
19478		   $p_draw{$abk} = 1 - $p_draw{$abk};
19479		   plot('p',$abk, -lazy => 1);
19480	       });
19481	}
19482    }
19483}
19484
19485# bindet ein Men� an die rechte Taste
19486sub menuright {
19487    my($b, $menu) = @_;
19488    $b->bind('<ButtonPress-3>' => sub {
19489		 if (0) { # old code XXX
19490		     $menu->Popup(-popover => $b,
19491				  -popanchor => 'n',
19492				  -overanchor => 's',
19493				 );
19494		 } else {
19495		     my $e = $b->XEvent;
19496		     my $X = $e->X;
19497		     my $Y = $e->Y;
19498		     $menu->Post($X,$Y);
19499		 }
19500	     }
19501	    );
19502}
19503
19504sub menuarrow {
19505    my($b, $menu, $col, %args) = @_;
19506    return if !menuarrow_unmanaged($b, $menu, %args);
19507    if (defined $col) {
19508	$b->grid(-row => $curr_row+1, -column => $col, -sticky => 'nesw');
19509    } else {
19510	my(@packargs) = (exists $args{'-pack'} ? @{$args{'-pack'}} : ());
19511	$b->pack(@packargs);
19512    }
19513}
19514
19515sub menuarrow_unmanaged {
19516    my($b, $menu, %args) = @_;
19517    return 0 if !$menuarrow_photo;
19518    $b->configure(-menu => $menu);
19519    $b->configure
19520      (-image => $menuarrow_photo,
19521       -takefocus => 1,
19522       -highlightthickness => 1,
19523       -indicatoron => 0,
19524       -bd => ($small_icons ? 0 : 2),
19525       -padx => 0,
19526       -pady => 0,
19527      );
19528
19529    my $menulabel;
19530    if (defined $args{'-menulabel'}) {
19531	$menulabel = $args{'-menulabel'};
19532    } else {
19533	for my $inx (0 .. $menu->index('last')) {
19534	    if ($menu->type($inx) !~ /^(separator|tearoff)$/) {
19535		$menulabel = eval q{$menu->entrycget($inx, -label)};
19536		last if defined $menulabel;
19537	    }
19538	}
19539    }
19540    if (defined $menulabel and $menulabel ne '') {
19541	(my $balloonlabel = $menulabel) =~ s/~//;
19542	$balloon->attach($b, -msg => M("Men�")." $balloonlabel...");
19543	# No balloon for actual menu:
19544	$balloon->attach($menu, -msg => []);
19545    }
19546    $menu->{BBBike_Menulabel} = $menulabel if !defined $menu->{BBBike_Menulabel};
19547    $menu->{BBBike_Special}   = $args{-special};
19548    $b->bind('<ButtonPress-3>' => sub { $b->ButtonDown });
19549    1;
19550}
19551
19552# error categories:
19553#  info: never pops up a dialog: either writes to stderr or to the
19554#        status bar if available
19555#  infodlg: info with a dialog
19556#  infoauto: info with auto-popped down toplevel
19557#  warn: warn with a dialog
19558#  err:  error with a dialog
19559#  die:  error with a dialog and die afterwards
19560sub status_message {
19561    my($msg, $err) = @_;
19562    if (!defined $err || ($err =~ /^info/ && $err ne "infodlg" && $err ne "infoauto") || !$use_dialog) {
19563	if (!defined $progress) {
19564	    if (defined $err && $err eq 'info-stack-trace') {
19565		require Carp;
19566		Carp::cluck($msg);
19567	    } else {
19568		print STDERR "$msg\n";
19569	    }
19570	} else {
19571	    $msg =~ s/\n+\z//;
19572	    $status_label->configure(-text => $msg);
19573	    if ($msg =~ /\n/) {
19574		set_status_button
19575		    (-text => "OK",
19576		     -command => sub {
19577			 status_message("", "info");
19578		     });
19579	    } else {
19580		remove_status_button();
19581	    }
19582	}
19583    } elsif ($err eq 'infoauto') {
19584	my $l;
19585	if ($status_message_toplevel && Tk::Exists($status_message_toplevel)) {
19586	    $status_message_toplevel->deiconify;
19587	    $status_message_toplevel->raise;
19588	    $l = $status_message_toplevel->Subwidget("Text");
19589	} else {
19590	    $status_message_toplevel = $top->Toplevel;
19591	    set_as_toolwindow($status_message_toplevel);
19592	    $status_message_toplevel->geometry('+30+30'); # XXX better geometry
19593	}
19594	if (!$l || !Tk::Exists($l)) {
19595	    $l = $status_message_toplevel->Component("Label" => "Text",
19596						     -background => Tk::NORMAL_BG,
19597						    )->pack(qw(-fill both -expand 1));
19598	}
19599	$l->configure(-text => $msg);
19600    } else {
19601	# warn or error
19602	if (!$top) {
19603	    print STDERR "$msg\n";
19604	} else {
19605	    my %args = (-title  => ($err eq 'warn' ? 'Warnung' : $err eq 'infodlg' ? 'Info' : 'Fehler'),
19606			-text   => $msg,
19607			-bitmap => ($err eq 'warn' ? 'warning' : $err eq 'infodlg' ? 'info' : 'error'),
19608			-background => Tk::NORMAL_BG,
19609			-highlightbackground => Tk::NORMAL_BG,
19610		       );
19611	    $splash_screen->Destroy if $splash_screen; undef $splash_screen;
19612	    if ($status_message_dialog && Tk::Exists($status_message_dialog)) {
19613		## Do not reconfigure existing dialog because of the
19614		## (still!) two-seconds hang
19615		#$status_message_dialog->configure(%args);
19616		$status_message_dialog->destroy;
19617	    }
19618
19619	    my $Dialog = LongOrNormalDialog();
19620	    $status_message_dialog = $top->$Dialog(%args);
19621	    # KDE's window manager seems to have a bug (?)
19622	    # that the dialog might be behind other
19623	    # transients. Fix the situation by forcing the dialog
19624	    # on top.
19625	    $kde->keep_on_top($status_message_dialog) if $kde;
19626	    $status_message_dialog->Show;
19627	}
19628    }
19629    if (defined $err && $err eq 'die') { # also die
19630	require Carp;
19631	Carp::confess($msg);
19632    }
19633}
19634
19635sub info_auto_popdown {
19636    if ($status_message_toplevel && Tk::Exists($status_message_toplevel)) {
19637	$status_message_toplevel->withdraw;
19638    }
19639}
19640
19641sub LongOrNormalDialog {
19642    my $Dialog = "Dialog";
19643    if (eval { require Tk::LongDialog; 1 }) {
19644	$Dialog = "LongDialog";
19645    } else {
19646	require Tk::Dialog;
19647    }
19648    $Dialog;
19649}
19650
19651sub _blockings_infobar_exists {
19652    return if $blockings_infobar && Tk::Exists($blockings_infobar);
19653    my %stdcolor = (-bg => 'yellow');
19654    $blockings_infobar = $c->Frame(Name => "blockingsinfobar", %stdcolor, -relief => 'raised', -borderwidth => 1);
19655    $blockings_infobar->Label(-text => M"M�gliche tempor�re Sperrungen auf der Route", %stdcolor)->pack(-side => "left");
19656    $blockings_infobar->Button(-padx => 1, -pady => 1, -borderwidth => 1,
19657			       -text => M"Anzeigen",
19658			       -command => sub { show_blockings() },
19659			      )->pack(-side => "left", -padx => 10);
19660    $blockings_infobar->idletasks; # to force -reqheight to be set
19661}
19662
19663sub show_blockings_infobar {
19664    require Tk::SmoothShow;
19665    _blockings_infobar_exists();
19666    Tk::SmoothShow::show($blockings_infobar);
19667}
19668
19669sub hide_blockings_infobar {
19670    if ($blockings_infobar && Tk::Exists($blockings_infobar)) {
19671	require Tk::SmoothShow;
19672	Tk::SmoothShow::hide($blockings_infobar);
19673    }
19674}
19675
19676sub set_status_button {
19677    my(%args) = @_;
19678    $status_button->grid(-column => $status_button_column,
19679			 -row => 0);
19680    if (!$args{-command}) {
19681	die "-command missing";
19682    }
19683    my $cmd = $args{-command};
19684    $args{-command} = sub {
19685	$cmd->();
19686	remove_status_button();
19687    };
19688    $status_button->configure(%args);
19689}
19690
19691sub remove_status_button {
19692    if ($status_button->manager) {
19693	$status_button->configure(-text => "", -command => \&Tk::NoOp);
19694	$status_button->gridForget;
19695    }
19696}
19697
19698sub add_new_point {
19699    my $net   = shift;
19700    my $point = shift;
19701    my(%args) = @_;
19702    my($rx, $ry) = split(/,/, $point);
19703    my($tx, $ty) = transpose($rx, $ry);
19704    my($pos, @points) = nearest_line_points_xy($tx, $ty);
19705    # Korrektur des mittleren Punktes (-> index=0 !!!)
19706    $points[0] = [$rx, $ry];
19707    if (@points) {
19708	$net->add_net($pos, @points);
19709    }
19710    unless ($args{'-quiet'}) {
19711	if (!$net->reachable($point)) {
19712	    status_message(Mfmt("Der Punkt <%s> existiert im Netz nicht und kann auch nicht erzeugt werden", $point), "die");
19713	}
19714    }
19715    join(",", @{ $points[0] });
19716}
19717
19718sub nearest_line_points_xy {
19719    my($x, $y) = @_;
19720    my $start;
19721    my %seen;
19722    my $stage = 'closest';
19723    my @find;
19724    my $find_i;
19725my $safe_loop = 0; #XXX
19726    while (1) {
19727die "too many loops, please report, line " . __LINE__ if ($safe_loop++ > 100);
19728	my $find;
19729	if ($stage eq 'closest') {
19730	    ($find) = $c->find('closest', $x, $y, 0, $start);
19731	    if (defined $find and $find ne '') {
19732		if (exists $seen{$find}) {
19733		    $stage = 'overlapping';
19734		    next;
19735		}
19736	    }
19737	} elsif ($stage eq 'overlapping') {
19738	    if (!@find) {
19739		@find = $c->find('overlapping', $x-2, $y-2, $x+2, $y+2);
19740		$find_i = 0;
19741	    }
19742	    return undef if $find_i > $#find;
19743	    $find = $find[$find_i];
19744	    $find_i++;
19745	}
19746	my(@tags) = $c->gettags($find);
19747	my $item_type_by_tag = $tags[0];
19748	if (grep { $item_type_by_tag eq $_ } $net->get_source_abk) {
19749	    return nearest_line_points($x, $y, @tags);
19750	}
19751
19752#XXX del:
19753#        if ($net_type eq "r") {
19754#	    if ($tags[0] eq 'r') {
19755#		return nearest_line_points($x, $y, @tags); # XXX
19756#	    }
19757#	} elsif ($net_type eq "us") {
19758#	    if ($tags[0] =~ /^[ub]$/) {
19759#		return nearest_line_points($x, $y, @tags); # XXX
19760#	    }
19761#	} elsif ($net_type eq "rus") {
19762#	    if ($tags[0] =~ /^[ubr]$/) {
19763#		return nearest_line_points($x, $y, @tags); # XXX
19764#	    }
19765#	} elsif ($net_type eq 'wr') {
19766#	    if ($tags[0] eq 'wr') {
19767#		return nearest_line_points($x, $y, @tags); # XXX
19768#	    }
19769#	} elsif ($net_type eq 's-car') {
19770#	    if (($tags[0] =~ /^[sSlL]$/ || $tags[0] eq 'sBAB') && !grep { /^[sSlL]-label/ || /^sBAB-label/ } @tags) {
19771#		return nearest_line_points($x, $y, @tags); # XXX
19772#	    }
19773#	} else {
19774#	    if ($tags[0] =~ /^[sSlL]$/ && !grep { /^[sSlL]-label/ } @tags) {
19775#		return nearest_line_points($x, $y, @tags); # XXX
19776#	    }
19777#	}
19778	if ($stage eq 'closest') {
19779	    $start = $find;
19780	    $seen{$find}++;
19781	}
19782    }
19783}
19784
19785sub nearest_line_points_mouse {
19786    my($c, @tags) = @_;
19787    my $e = $c->XEvent;
19788    my($x, $y) = ($c->canvasx($e->x), $c->canvasy($e->y));
19789    @tags = $c->gettags('current') if !@tags;
19790    @tags = grep { $_ ne 'current' } @tags;
19791
19792    my @forbidden_tags_rxs = ('^show$', '^route$', '-label'); # ignore labels and show marker etc.
19793    my $forbidden_tags_qr = join("|", @forbidden_tags_rxs);
19794    $forbidden_tags_qr = qr{$forbidden_tags_qr};
19795    if (grep { $_ =~ $forbidden_tags_qr } @tags) {
19796	(undef, @tags) = find_below_rx($c, [q{.}], undef, \@forbidden_tags_rxs);
19797    }
19798
19799    my($pos, @points);
19800    eval {
19801	($pos, @points) = nearest_line_points($x, $y, @tags);
19802    };
19803    if ($@) {
19804	# 2nd try: restrict to just s and l types
19805	(undef, @tags) = find_below($c, 's', 'l');
19806	($pos, @points) = nearest_line_points($x, $y, @tags);
19807    }
19808    ($pos, @points);
19809}
19810
19811# Input arguments:
19812#   x/y: current canvas coordinates
19813#   tags: tags of the current canvas item
19814# Output:
19815#   ($index, middlepoint(new), firstpoint, secondpoint)
19816#   points are real coordinates
19817sub nearest_line_points {
19818    my($x, $y, @tags) = @_;
19819    my(@realcoords, @coords);
19820    if (defined $tags[3] && $tags[3] =~ /^(.+)-(\d+)$/) {
19821	my($type, $index) = ($1, $2);
19822	my $s;
19823	$s = $str_obj{$type};
19824	if (!defined $s) {
19825	    if (exists $str_file{$type}) {
19826		# XXX better: create a function type_to_filename
19827		my $filename = get_strassen_file($str_file{$type});
19828		$str_obj{$type} = new Strassen $filename;
19829		$s = $str_obj{$type};
19830	    }
19831	    if (!defined $s) {
19832		die "Streets not defined for type $type, Filename is $str_file{$type} XXX";
19833	    }
19834	} else {
19835	    $s->reload;
19836	}
19837	my $ret = $s->get($index);
19838	if ($ret and @{$ret->[Strassen::COORDS]}) {
19839	    # Erste Methode. $str_width wird von 2 bis 4 inkrementiert
19840	    # (h�ngt von der Breite der Stra�en ab).
19841	    for my $str_width (2 .. 4) {
19842		my $i;
19843		my($lastxx, $lastyy, $lastrx, $lastry);
19844		for($i = 0; $i <= $#{$ret->[Strassen::COORDS]}; $i++) {
19845		    if ($ret->[Strassen::COORDS][$i] =~ /^(?:[A-Z])?(-?\d+(?:\.\d*)?),(-?\d+(?:\.\d*)?)$/) {
19846			my($rx, $ry) = ($1, $2);
19847			my($xx, $yy) = transpose($rx, $ry);
19848			push @realcoords, $rx, $ry;
19849			push @coords, transpose($xx, $yy);
19850			if (defined $lastxx &&
19851			    (($x >= $lastxx-$str_width &&
19852			      $x <= $xx+$str_width) ||
19853			     ($x >= $xx-$str_width     &&
19854			      $x <= $lastxx+$str_width)) &&
19855			    (($y >= $lastyy-$str_width &&
19856			      $y <= $yy+$str_width) ||
19857			     ($y >= $yy-$str_width     &&
19858			      $y <= $lastyy+$str_width))) {
19859			    my($p1, $p2) = anti_transpose($x, $y);
19860			    my($fp1, $fp2) = fusspunkt($lastrx, $lastry,
19861						       $rx, $ry,
19862						       $p1, $p2);
19863# XXX Achtung! $index kann nicht gebraucht werden, wenn
19864# mit Multistrassen gearbeitet wird. L�sung?
19865# Zuordnung von Strassen-Indices auf Multistrassen-Indices?
19866#XXX			return ((defined $multistrassen ? undef : $index),
19867# XXX test it:
19868			    my(@points) = ([int_round($fp1), int_round($fp2)],
19869					   [$lastrx, $lastry],
19870					   [$rx, $ry]);
19871			    if ($net and
19872				$net->{Strassen}->isa('MultiStrassen')) {
19873			      SEARCH: {
19874				    for my $i (0 .. $#{$net->{SourceAbk}}) {
19875					if ($net->{SourceAbk}[$i] eq $type) {
19876					    $index +=
19877					      $net->{Strassen}{FirstIndex}[$i];
19878					    last SEARCH;
19879					}
19880				    }
19881				    warn "Can't find index for MultiStrassen...";
19882				    undef $index;
19883				}
19884			    }
19885			    return ($index, @points);
19886			} else {
19887			    ($lastxx, $lastyy) = ($xx, $yy);
19888			    ($lastrx, $lastry) = ($rx, $ry);
19889			}
19890		    } else {
19891			die "Can't parse coord: $ret->[Strassen::COORDS][$i]";
19892		    }
19893		}
19894	    }
19895 	}
19896	warn "nearest_line_points: failed 1st method
19897Tags are @tags
19898Type is $type
19899Index is $index
19900
19901Try 2nd method...";
19902    } else {
19903	die "Can't find index from tags: @tags";
19904    }
19905    # 2. Methode. Die n�chsten zwei Punkte in @coords werden einfach als
19906    # Nachbarn deklariert. Funktioniert ganz gut, es sei denn, die Stra�e
19907    # hat einen *sehr* kurvigen Verlauf (90�-Kurven etc.).
19908    my(@coords_dist, $nearest_i);
19909    my $i;
19910    if ($#coords > 0) {
19911	for($i = 0; $i < $#coords; $i+=2) {
19912	    my($lx, $ly) = ($coords[$i], $coords[$i+1]);
19913	    push(@coords_dist,
19914		 Strassen::Util::strecke([$x, $y],
19915					 [$coords[$i], $coords[$i+1]]));
19916	    if (!defined $nearest_i or
19917		$coords_dist[$nearest_i] > $coords_dist[-1]) {
19918		$nearest_i = $#coords_dist;
19919	    }
19920	}
19921    }
19922    my @res = ([anti_transpose($x, $y)]);
19923    if (!defined $nearest_i) {
19924	die "No nearest point???";
19925    } elsif ($nearest_i == 0) {
19926	push(@res, [@realcoords[0..1]], [@realcoords[2..3]]);
19927    } elsif ($nearest_i == $#coords_dist) {
19928	my $last = $#coords_dist;
19929	push(@res,
19930	     [@realcoords[$last*2-2 .. $last*2-1]],
19931	     [@realcoords[$last*2   .. $last*2+1]]);
19932    } elsif ($coords_dist[$nearest_i-1] < $coords_dist[$nearest_i+1]) {
19933	push(@res,
19934	     [@realcoords[$nearest_i*2-2 .. $nearest_i*2-1]],
19935	     [@realcoords[$nearest_i*2   .. $nearest_i*2+1]]);
19936    } else {
19937	push(@res,
19938	     [@realcoords[$nearest_i*2   .. $nearest_i*2+1]],
19939	     [@realcoords[$nearest_i*2+2 .. $nearest_i*2+3]]);
19940    }
19941    (undef, @res);
19942}
19943
19944sub city_settings {
19945    $str_draw{'l'}     = 0;
19946    $p_draw{'o'}       = 0;
19947    $p_far_away{'o'}   = 0;
19948    $str_far_away{'w'} = 0;
19949    $str_far_away{'l'} = 0;
19950    $str_regions{'l'}  = [];
19951    $wasserumland      = 0;
19952    pending(1, map { "replot-$_" } ("str-l", "p-o", "str-w", "str-l"));
19953}
19954
19955sub region_settings {
19956    $str_draw{'l'}     = 1; # XXX set to str_draw{'s'}?
19957    $p_draw{'o'}       = 1;
19958    $p_far_away{'o'}   = 0;
19959    $str_far_away{'w'} = 0;
19960    $str_far_away{'l'} = 0;
19961    $str_regions{'l'}  = [];
19962    $wasserumland      = 1;
19963    pending(1, map { "replot-$_" } ("str-l", "p-o", "str-w", "str-l"));
19964}
19965
19966sub jwd_settings {
19967    $str_draw{'l'}     = 1; # XXX set to str_draw{'s'}?
19968    $p_draw{'o'}       = 1;
19969    $p_far_away{'o'}   = 1;
19970    $str_far_away{'w'} = 1;
19971    $str_far_away{'l'} = 1;
19972    $str_regions{'l'}  = []; # XXX Sachsen-Anhalt?
19973    $wasserumland      = 1;
19974    pending(1, map { "replot-$_" } ("str-l", "p-o", "str-w", "str-l"));
19975}
19976
19977# Definiert, wie die grafischen Objekte "gestapelt" werden sollen.
19978# Also ganz unten Gew�sser und Fl�chen, dann Stra�en etc. und ganz oben
19979# Punkte wie Haltestellen, Orte und Kreuzungen.
19980# Allgemeine Fl�chen kommen unter Gew�sser, damit man z.B. bei in
19981# W�ldern gelegenen Seen nicht aufwendig ausschneiden muss.
19982# Ganz oben sind die mit "Custom draw" gezeichneten Strecken.
19983# Weitere Regeln: Labels von Orten sind unter anderen Ortspunkten (damit
19984# die Ortspunkte anw�hlbar bleiben), dagegen sind Labels von Bahnh�fen
19985# �ber den Bahnhofspunkten und Bahnstrecken (m�ssen nicht anw�hlbar sein).
19986# Development-Hilfen (fz) ganz oben anzeigen.
19987sub restack {
19988    my @real_order;
19989    @real_order = real_stack_order();
19990
19991    foreach (@real_order) {
19992	$c->raise($_);
19993    }
19994
19995    Hooks::get_hooks("after_change_stacking")->execute();
19996}
19997
19998# gibt das aktuelle Stacking aus
19999sub real_stack_order {
20000    my @real_order;
20001
20002    push @real_order, @special_lower;
20003    foreach (@normal_stack_order) {
20004	next if m{^\*.*\*$}; # ignore special tags
20005	if (!$special_lower{$_} && !$special_raise{$_}) {
20006	    push @real_order, $_;
20007	}
20008    }
20009    push @real_order, @special_raise;
20010    @real_order;
20011}
20012
20013### AutoLoad Sub
20014sub real_type_stack_order {
20015    my @real_order = real_stack_order();
20016    my @res;
20017    my %seen;
20018    foreach my $type (@real_order) {
20019	$type =~ s/^([^-]*)-.*/$1/;
20020	if (!$seen{$type}) {
20021	    push @res, $type;
20022	    $seen{$type}++;
20023	}
20024    }
20025    @res;
20026}
20027
20028### AutoLoad Sub
20029sub special_normal {
20030    my($abk, $delay) = @_;
20031
20032    if (exists $special_lower{$abk}) {
20033	delete $special_lower{$abk};
20034	remove_from_array(\@special_lower, $abk);
20035    }
20036
20037    if (exists $special_raise{$abk}) {
20038	delete $special_raise{$abk};
20039	remove_from_array(\@special_raise, $abk);
20040    }
20041
20042    restack() unless $delay;
20043}
20044
20045### AutoLoad Sub
20046sub special_raise {
20047    my($abk, $delay) = @_;
20048
20049    if (exists $special_lower{$abk}) {
20050	delete $special_lower{$abk};
20051	remove_from_array(\@special_lower, $abk);
20052    }
20053
20054    $special_raise{$abk}++;
20055    remove_from_array(\@special_raise, $abk);
20056    push @special_raise, $abk;
20057
20058    restack() unless $delay;
20059}
20060
20061### AutoLoad Sub
20062sub special_lower {
20063    my($abk, $delay) = @_;
20064
20065    if (exists $special_raise{$abk}) {
20066	delete $special_raise{$abk};
20067	remove_from_array(\@special_raise, $abk);
20068    }
20069    $special_lower{$abk}++;
20070    remove_from_array(\@special_lower, $abk);
20071    unshift @special_lower, $abk;
20072
20073    restack() unless $delay;
20074}
20075
20076sub remove_from_array {
20077    my($a_ref, $val) = @_;
20078    for(my $i = 0; $i <= $#{$a_ref}; $i++) {
20079	if ($a_ref->[$i] eq $val) {
20080	    splice @$a_ref, $i, 1;
20081	    $i--;
20082	}
20083    }
20084}
20085
20086sub destroy_delayed_restack {
20087    destroy_delayed_sub('restack');
20088}
20089
20090sub fix_stack_order {
20091    my($abk) = @_;
20092    if (!grep { $_ eq $abk } @normal_stack_order) {
20093	push @normal_stack_order, $abk, "$abk-fg";
20094    }
20095}
20096
20097### AutoLoad Sub
20098sub add_to_stack {
20099    my($abk, $how, $other_abk) = @_;
20100    return if (grep { $_ eq $abk } @normal_stack_order);
20101    if (defined $how) {
20102	if ($how eq 'lowermost') {
20103	    unshift @normal_stack_order, $abk, "$abk-fg";
20104	    return;
20105	} elsif ($how eq 'topmost') {
20106	    push @normal_stack_order, $abk, "$abk-fg";
20107	    return;
20108	}
20109    }
20110
20111    # Smart match do-it-yorself:
20112    my $other_abk_match =
20113	(ref $other_abk eq 'ARRAY'
20114	 ? sub {
20115	     my($tag) = @_;
20116	     first { $_ eq $tag } @$other_abk;
20117	 }
20118	 : ref $other_abk eq 'Regexp'
20119	 ? sub {
20120	     my($tag) = @_;
20121	     $tag =~ $other_abk;
20122	 }
20123	 : sub {
20124	     my($tag) = @_;
20125	     $tag eq $other_abk;
20126	 }
20127	);
20128
20129    my $i = 0;
20130    for my $tag (@normal_stack_order) {
20131	# XXX I think I don't have to check against '*...*' tags
20132	if ($other_abk_match->($tag)) {
20133	    if ($how =~ m{^(after|above)$}) {
20134		splice @normal_stack_order, $i+1, 0, $abk, "$abk-fg";
20135		return;
20136	    } elsif ($how =~ m{^(before|below)$}) {
20137		splice @normal_stack_order, $i, 0, $abk, "$abk-fg";
20138		return;
20139	    } else {
20140		die "Cannot handle $how in add_to_stack";
20141	    }
20142	}
20143	$i++;
20144    }
20145    push @normal_stack_order, $abk, "$abk-fg";
20146}
20147
20148### AutoLoad Sub
20149sub set_in_stack {
20150    my($abk, $how, $other_abk) = @_;
20151    remove_from_stack($abk);
20152    add_to_stack($abk, $how, $other_abk);
20153}
20154
20155### AutoLoad Sub
20156sub remove_from_stack {
20157    my($abk) = @_;
20158    my $abk_fg = "$abk-fg";
20159    @normal_stack_order = grep { $_ ne $abk && $_ ne $abk_fg } @normal_stack_order;
20160}
20161
20162sub restack_delayed {
20163    # Use the delaying only on slow systems. For fast systems,
20164    # delaying is disturbing for the interactivity.
20165    delayed_sub(\&restack, -busy => $slowcpu ? !$edit_mode && !$edit_normal_mode : 0,
20166		           -delay => $slowcpu ? 1000 : 300,
20167		           -name => 'restack');
20168}
20169
20170sub destroy_delayed_sub {
20171    my $name = shift;
20172    if ($delayed_sub_timer{$name}) {
20173	$delayed_sub_timer{$name}->cancel;
20174	delete $delayed_sub_timer{$name};
20175    }
20176}
20177
20178sub delayed_sub {
20179    my($sub, %args) = @_;
20180    my $ms   = $args{'-delay'} || 1000;
20181    my $name = $args{'-name'}  || "";
20182    my $busy = (defined $args{'-busy'} ? $args{'-busy'} : 1);
20183    destroy_delayed_sub($name);
20184    $delayed_sub_timer{$name} = $top->after
20185      ($ms, sub {
20186## DEBUG_BEGIN
20187#benchbegin("Delayed sub $name");
20188## DEBUG_END
20189	   IncBusy($top) if $busy;
20190	   eval {
20191	       $sub->();
20192	   };
20193	   warn __LINE__ . ": $@" if $@;
20194	   DecBusy($top) if $busy;
20195## DEBUG_BEGIN
20196#benchend();
20197## DEBUG_END
20198       });
20199}
20200
20201### AutoLoad Sub
20202sub show_logo { # und About
20203    my $as_about = shift || '';
20204    return unless $use_logo || $as_about;
20205
20206    my $logotop = redisplay_top($top, "about-$as_about",
20207				-title => ($as_about ? M('�ber').' ' : '')
20208				. 'BBBike',
20209				-background => 'white');
20210    return if !defined $logotop;
20211
20212    my %git_info;
20213    if ($as_about && -r "$FindBin::RealBin/miscsrc/BBBikeGit.pm") {
20214	require "$FindBin::RealBin/miscsrc/BBBikeGit.pm";
20215	%git_info = BBBikeGit::git_info();
20216    }
20217
20218    $logotop->optionAdd("*" . substr($logotop->PathName, 1)
20219			. "*background" => 'white', 'startupFile');
20220    $logotop->optionAdd("*" . substr($logotop->PathName, 1)
20221			. "*foreground" => 'blue3', 'startupFile');
20222    $logotop->transient($top) unless $as_about;
20223    my $ff = $logotop->Frame(-relief => ($as_about ? 'ridge' : 'flat'),
20224			     -bd => ($as_about ? 2 : 0),
20225			    )->pack(-fill => 'both', -expand => 1);
20226    my $f = $ff->Frame->pack(-side => 'left',
20227			     -fill => 'both', -expand => 1,
20228			     -padx => 4, -pady => 4,
20229			    );
20230    my %common_args =
20231	(
20232	 -padx => 5,
20233	 -highlightthickness => 1,
20234	 -highlightbackground => 'white',
20235	 -relief => 'flat',
20236	 -borderwidth => 0,
20237	);
20238    my $Button_or_Label = ($as_about ? "Button" : "Label");
20239    my $www_b =
20240	$f->$Button_or_Label
20241	    (-text =>
20242	     "$progname $VERSION\n" .
20243	     ($git_info{patchnum} ? "(patchnum $git_info{patchnum})\n" : '') .
20244	     M("Ein Informationssystem f�r Radfahrer in Berlin") .
20245	     "\n\n� 1995-2012 Slaven Rezic",
20246	     %common_args,
20247	     -wraplength => 320,
20248	     -font => $font{'bold'},
20249	     -pady => 0,
20250	     ($as_about ?
20251	      (-command => sub {
20252		   require WWWBrowser;
20253		   WWWBrowser::start_browser($BBBike::BBBIKE_WWW);
20254	       },
20255	      ) : ())
20256	    )->pack(-fill => 'x');
20257    $balloon->attach($www_b, -msg => M"WWW-Version aufrufen")
20258	if $balloon;
20259    my $copying_b =
20260	$f->$Button_or_Label
20261	    (-text => M"Siehe auch die Datei COPYING",
20262	     %common_args,
20263	     ($as_about ?
20264	      (-command => sub { copying_viewer($logotop) }) : ()),
20265	    )->pack(-fill => "x");
20266    if (%git_info) {
20267	$f->$Button_or_Label
20268	    (-text => M"Detaillierte GIT-Information",
20269	     %common_args,
20270	     ($as_about ?
20271	      (-command => sub {
20272		   require Data::Dumper;
20273		   my $t = $logotop->Toplevel(-title => M"Detaillierte GIT-Information");
20274		   my $txt = $t->Scrolled("ROText", -scrollbars => "osoe")->pack(-fill => "both", -expand => 1);
20275		   local $Data::Dumper::Sortkeys = $Data::Dumper::Sortkeys = 1; # doubled to cease -w
20276		   local $Data::Dumper::Indent = 2;
20277		   my $dump = Data::Dumper::Dumper(\%git_info);
20278		   $dump =~ s{^(.*?=)}{" " x length $1}e;
20279		   $txt->insert('end', $dump);
20280		   $t->Button(Name => 'close',
20281			      -command => sub { $t->destroy },
20282			     )->pack(-fill => "x", -expand => 1);
20283	       }) : ()),
20284	     )->pack(-fill => 'x');
20285    }
20286    if ($as_about) {
20287	my $os_info = "OS: $^O";
20288	if ($os eq 'win') {
20289	    $os_info .= " (" . ($Config{'cc'} =~ /^gcc/
20290				? 'gcc' :
20291				($Config{'cc'} eq 'cl.exe'
20292				 ? 'Visual C'
20293				 : $Config{'cc'})) . ")";
20294	}
20295	# Are we running an emulation?
20296	# This could be wrong, e.g. if cygwin's uname is
20297	# in the PATH, but Win32 perl is running,
20298	# hence the "maybe"
20299	if (is_in_path("uname")) {
20300	    chomp(my $real_os = `uname`);
20301	    if ($^O !~ /^$real_os$/i) {
20302		$os_info .= " (Real OS, maybe: $real_os)";
20303	    }
20304	}
20305	$f->Label(-text => "perl $]\nTk $Tk::VERSION\n$os_info",
20306		  -font => $font{'small'},
20307		  -justify => 'left',
20308		 )->pack(-anchor => 'w', -expand => 1,
20309			 -fill => 'x');
20310    }
20311
20312    # Send mail to software maintainer
20313    my $mail_b =
20314	$f->$Button_or_Label
20315	    (-text => $BBBike::EMAIL,
20316	     -pady => 0,
20317	     %common_args,
20318	     ($as_about ?
20319	      (-command => sub {
20320		   if ($^O eq 'MSWin32') {
20321		       require Win32Util;
20322		       Win32Util::start_mail_composer($BBBike::EMAIL);
20323		   } else {
20324		       enter_send_mail(M"BBBike perl/Tk",
20325				       -to => $BBBike::EMAIL,
20326				      );
20327		   }
20328	       }) : ()),
20329	     -font => $font{'normal'})->pack(-fill => 'x');
20330    $balloon->attach($mail_b, -msg => M"Mail an den Autor schicken")
20331	if $balloon;
20332
20333    $ff->Label(-image => $srtbike_photo
20334	      )->pack(-side => 'left', -anchor => "ne");
20335    if ($as_about) {
20336	my $okb = $logotop->Button(Name => 'ok',
20337				   -command => sub { hide_logo($as_about) },
20338				  )->pack(-anchor => 'c', -pady => 4);
20339	$okb->focus;
20340	$logotop->bind('<Return>' => sub { $okb->invoke });
20341    } else {
20342	$logotop->transient($top);
20343    }
20344    $logotop->withdraw;
20345    $logotop->Popup(-popover => ($as_about ? 'cursor' : $top));
20346    $logotop->update; # damit der Inhalt sofort erscheint
20347}
20348
20349### AutoLoad Sub
20350sub hide_logo {
20351    my $as_about = shift || '';
20352    my $t = $toplevel{"about-$as_about"};
20353    if (defined $t && Tk::Exists($t)) {
20354	$t->destroy;
20355	undef $toplevel{"about-$as_about"};
20356    }
20357}
20358
20359### AutoLoad Sub
20360sub copying_viewer {
20361    my $top = shift;
20362    simple_file_viewer($top, "$FindBin::RealBin/COPYING",
20363		       -title => M"COPYING",
20364		       -class => "Bbbike Copyright",
20365		      );
20366}
20367
20368### AutoLoad Sub
20369sub simple_file_viewer {
20370    my($top, $file, %args) = @_;
20371    my $title = $args{-title};
20372    my $class = $args{-class};
20373    if (open(C, $file)) {
20374	binmode C;
20375	my $t = $top->Toplevel
20376	    ((defined $title ? (-title => $title) : ()),
20377	     (defined $class ? (-class => $class) : ()),
20378	    );
20379	my $txt = $t->Scrolled("ROText", -scrollbars => "osoe")->pack(-fill => "both", -expand => 1);
20380	while(<C>) {
20381	    $txt->insert("end", $_);
20382	}
20383	close C;
20384	$t->Button(Name => 'close',
20385		   -command => sub { $t->destroy },
20386		  )->pack(-fill => "x", -expand => 1);
20387    } else {
20388	status_message(Mfmt("Die Datei %s kann nicht ge�ffnet werden: %s",
20389			    $file, $!), "error");
20390    }
20391}
20392
20393######################################################################
20394# Utilities ...
20395
20396### AutoLoad Sub
20397sub usage {
20398    my($msg, $getopt_listref) = @_;
20399    my(@getopt_list) = @$getopt_listref;
20400    if (defined $msg) {
20401	$msg .= "\n";
20402    } else {
20403	$msg = '';
20404    }
20405
20406    my @opt;
20407    my $i;
20408    for($i = 0; $i <= $#getopt_list; $i+=2) {
20409	if ($getopt_list[$i] =~ /([^!=]+)(!|=.)?$/) {
20410	    my $mod = $2 || '';
20411	    if ($mod eq '!') {
20412		push @opt, map { "[-[no]$_]" } split(/\|/, $1);
20413	    } else {
20414		push @opt, map { "[-$_$mod]" } split(/\|/, $1);
20415	    }
20416	} else {
20417	    push @opt, "[-$getopt_list[$i]]";
20418	}
20419    }
20420    die $msg . wrap("usage: $progname ", "\t", join(" ", @opt))
20421      . "\n";
20422}
20423
20424### AutoLoad Sub
20425sub windrose { # funktioniert nur mit quadratischen Buttons
20426    my($senkrecht) = @_; # "Geschwindigkeit" des Scrollens
20427    my $e = $windrose_button->XEvent;
20428    my($x, $y) = ($e->x, $e->y);
20429    my($w, $h) = ($windrose_button->width, $windrose_button->height);
20430    $senkrecht = 1 unless defined $senkrecht;
20431
20432    my $is_center = sub {
20433	my($x, $y) = @_;
20434	($x > $w*0.4 && $x < $w*0.6 &&
20435	 $y > $h*0.4 && $y < $h*0.6)
20436    };
20437    my $center_delay;
20438
20439    if ($is_center->($x, $y) && !$center_delay) {
20440	$center_delay = $c->after
20441	  (1000, sub {
20442	       undef $center_delay;
20443	       my $e = $windrose_button->XEvent;
20444	       my($x, $y) = ($e->x, $e->y);
20445	       if ($is_center->($x, $y)) {
20446		   $c->center_view;
20447	       }
20448	   });
20449    } elsif ($x-0.25*$w < 0.5*$y) {
20450	if ($x-0.75*$w > -0.5*$y) {
20451	    my($y) = $c->yview;
20452	    $c->yview(scroll =>  $senkrecht, 'units'); # S
20453	} elsif ($x+0.5*$w > 2*$y) {
20454	    $c->yview(scroll => -$senkrecht, 'units'); # N
20455	    $c->xview(scroll => -$senkrecht, 'units'); # W
20456	} elsif ($x-1.5*$w > -2*$y) {
20457	    $c->yview(scroll =>  $senkrecht, 'units'); # S
20458	    $c->xview(scroll => -$senkrecht, 'units'); # W
20459	} else {
20460	    $c->xview(scroll => -$senkrecht, 'units'); # W
20461	}
20462    } else {
20463	if ($x-0.75*$w < -0.5*$y) {
20464	    $c->yview(scroll => -$senkrecht, 'units'); # N
20465	} elsif ($x+0.5*$w < 2*$y) {
20466	    $c->yview(scroll =>  $senkrecht, 'units'); # S
20467	    $c->xview(scroll =>  $senkrecht, 'units'); # E
20468	} elsif ($x-1.5*$w < -2*$y) {
20469	    $c->yview(scroll => -$senkrecht, 'units'); # N
20470	    $c->xview(scroll =>  $senkrecht, 'units'); # E
20471	} else {
20472	    $c->xview(scroll =>  $senkrecht, 'units'); # E
20473	}
20474    }
20475}
20476
20477### AutoLoad Sub
20478sub check_font {
20479    my $font = shift;
20480    eval { $top->Label(-font => $font)->destroy };
20481    $@ eq '';
20482}
20483
20484sub IncBusy {
20485    my($top, %args) = @_;
20486    return if !Tk::Exists($top);
20487
20488    if (!$top->{'Busy'}) {
20489	# Explicitely check for Windows - Tk::InputO might be install e.g.
20490	# in a cygwin install for Tk/X11 and fail then if Tk/MSWin32 is
20491	# used.
20492	if ($Tk::platform ne 'MSWin32' && eval q{ require Tk::InputO; 1 }) {
20493	    for my $t ($top, values(%toplevel)) {
20494		next if !Tk::Exists($t);
20495		next if $args{-except} && $args{-except}{$t};
20496		my $io = (Tk::Exists($t->{'BBBikeBusyIO'})
20497			  ? $t->{'BBBikeBusyIO'}
20498			  : $t->InputO);
20499		$io->configure(-cursor => (defined $args{-cursor} ? $args{-cursor} : 'watch'));
20500		$io->place('-x' => 0, '-y' => 0, -relwidth => 1, -relheight => 1);
20501		$io->idletasks;
20502		$t->{'BBBikeBusy'} = 1;
20503		$t->{'BBBikeBusyIO'} = $io;
20504	    }
20505	} else {
20506	    # see "Busy" changes in TkChange.pm
20507	    my $except = delete $args{-except};
20508	    if ($except) {
20509		# In this case we must not use the -recurse option, as
20510		# usually using -except means that there's a "cancel" window
20511		# which has to be accessible all the time
20512		$top->Busy(%args);
20513	    } else {
20514		$top->Busy(-recurse => 1, %args);
20515	    }
20516	}
20517    }
20518    $top->{'BusyCount'}++;
20519}
20520
20521sub DecBusy {
20522    my($top) = @_;
20523    return if !Tk::Exists($top);
20524    $top->{'BusyCount'}-- if $top->{'BusyCount'} > 0;
20525    if ($top->{'BusyCount'} < 1) {
20526	if ($top->{'BBBikeBusyIO'}) {
20527	    for my $t ($top, values(%toplevel)) {
20528		next if !Tk::Exists($t) ||
20529		        !Tk::Exists($t->{'BBBikeBusyIO'});
20530		$t->{'BBBikeBusyIO'}->placeForget;
20531	    }
20532	    delete $top->{'BBBikeBusy'};
20533	} else {
20534	    $top->Unbusy;
20535	}
20536    }
20537}
20538
20539### AutoLoad Sub
20540sub redisplay_top {
20541    my($top, $name, %args) = @_;
20542    my $force = delete $args{-force};
20543    my $deiconify = (exists $args{-deiconify} ? delete $args{-deiconify} : 1);
20544    my $raise     = (exists $args{-raise}     ? delete $args{-raise}     : 1);
20545    my $transient = (exists $args{-transient} ? delete $args{-transient} : 1);
20546    my $geometry  = delete $args{-geometry};
20547    if (!exists $args{-class}) {
20548	$args{-class} = "Bbbike Window";
20549    }
20550    my $t = $toplevel{$name};
20551    my $exists = 0;
20552    if (defined $t && Tk::Exists($t)) {
20553	if ($force) {
20554	    $t->destroy;
20555	    delete $toplevel{$name};
20556	} else {
20557	    $exists = 1;
20558	}
20559    }
20560    if ($exists) {
20561	$t->deiconify if $deiconify;
20562	# win32 ben�tigt zus�tzliches raise
20563	$t->raise     if $raise;
20564	undef;
20565    } else {
20566	$toplevel{$name} = $top->Toplevel(%args);
20567	$toplevel{$name}->geometry($geometry) if $geometry;
20568	set_as_toolwindow($toplevel{$name}) if $transient;
20569	$toplevel{$name}->OnDestroy(sub { delete $toplevel{$name} });
20570	$toplevel{$name};
20571    }
20572}
20573
20574sub pending {
20575    my($bool, @types) = @_;
20576    if ($bool) {
20577	foreach (@types) {
20578	    if (defined $immediate{$_}) {
20579		if ($immediate{$_} == 1) {
20580		    update($_);
20581		} elsif ($immediate{$_} == 2) {
20582		    $pending{$_}++;
20583		    delayed_sub(sub { update() }, -name => 'pending');
20584		}
20585	    } else {
20586		$pending{$_}++;
20587	    }
20588	}
20589    }
20590}
20591
20592sub update {
20593    my $type = shift;
20594    my @types;
20595    if (defined $type) {
20596	@types = ($type);
20597    } else {
20598	@types = keys %pending;
20599    }
20600    foreach $type (@types) {
20601	if ($type =~ /^replot-(.*)-(.*)$/) {
20602	    my($str_p, $elem) = ($1, $2);
20603	    plot($str_p,$elem);
20604	} elsif ($type eq 'recalc-net') {
20605	    make_net();
20606	    read_sperre_tragen() unless $lowmem; # XXX should be called automatically, maybe?
20607	} else {
20608	    die "Unknown update type: $type";
20609	}
20610    }
20611}
20612
20613### AutoLoad Sub
20614sub calc_ampel_optimierung {
20615    return if !$ampel_optimierung;
20616    if ($average_v == -1) {
20617	# manuelle Eingabe, keine Berechnung notwendig...
20618	status_message(Mfmt("Einstellungen: verlorene Strecke pro Ampel: %d m", $lost_strecke_per_ampel), "info");
20619    } else {
20620	require Ampelschaltung;
20621	my $speed = 20;
20622	if ($average_v != 0) {
20623	    $speed = $average_v;
20624	} else {
20625	    if ($active_speed_power{Type} eq 'speed') {
20626		$speed = $speed[$active_speed_power{Index}];
20627	    } elsif ($active_speed_power{Type} eq 'power' and $bikepwr) {
20628		my $bp_obj = new BikePower;
20629		$bp_obj->given('P');
20630		$bp_obj->power($power[$active_speed_power{Index}]);
20631		$bp_obj->calc;
20632		$speed = float_prec($bp_obj->velocity*3.6, 1);
20633	    }
20634	}
20635	my %res = Ampelschaltung::get_lost($speed, $beschleunigung);
20636	$lost_time_per_ampel{X} = $res{-zeit}; # XXX F
20637	$lost_strecke_per_ampel = $res{-strecke};
20638	status_message(Mfmt("Einstellungen f�r %s km/h: verlorene Zeit pro Ampel: %s s, verlorene Strecke pro Ampel: %d m", $speed, float_prec($lost_time_per_ampel{X}, 1), $lost_strecke_per_ampel), "info"); # XXX F
20639    }
20640}
20641
20642sub now_time_hires { Tk::timeofday() }
20643
20644# evtl. utimes benutzen
20645sub cache_decider_init { $cache_decider_time = now_time_hires() }
20646
20647sub cache_decider {
20648    die "cache_decider on empty cache_decider_time scalar"
20649      if !defined $cache_decider_time;
20650    my $now = now_time_hires();
20651    my $r = ($now - $cache_decider_time > $min_cache_decider_time);
20652    if ($verbose && $r) {
20653	warn "Using cache (" . ($now - $cache_decider_time) . " s)!\n";
20654    }
20655    undef $cache_decider_time;
20656    $r;
20657}
20658
20659### AutoLoad Sub
20660sub add_last_loaded {
20661    my($file, $last_loaded_obj, $add_def) = @_;
20662    $add_def = "" if !defined $add_def;
20663    eval {
20664	require File::Spec;
20665	$file = File::Spec->canonpath($file);
20666	$file = File::Spec->rel2abs($file);
20667    };
20668    my $max = $last_loaded_obj->{Max} || 4; # maximale Anzahl in @last_loaded
20669    my $i;
20670    for($i = 0; $i <= $#{ $last_loaded_obj->{List} }; $i++) {
20671	my($file_part) = $last_loaded_obj->{List}->[$i] =~ /^([^\t]*)/;
20672	if ($file_part eq $file) {
20673	    splice @{ $last_loaded_obj->{List} }, $i, 1;
20674	    $i--;
20675	}
20676    }
20677    unshift @{ $last_loaded_obj->{List} }, $file . $add_def;
20678    splice @{ $last_loaded_obj->{List} }, $max
20679	if @{ $last_loaded_obj->{List} } > $max;
20680    update_last_loaded_menu($last_loaded_obj);
20681    if ($os eq 'win') {
20682        require Win32Util;
20683        Win32Util::add_recent_doc($file);
20684    }
20685}
20686
20687sub load_last_loaded {
20688    my $last_loaded_obj = shift;
20689    undef @{ $last_loaded_obj->{List} };
20690    if (open(LAST, $last_loaded_obj->{File})) {
20691	while(<LAST>) {
20692	    chomp;
20693	    s/\r//g; # DOS-Newlines entfernen (kann passieren!)
20694	    push @{ $last_loaded_obj->{List} }, $_;
20695	}
20696	close LAST;
20697	update_last_loaded_menu($last_loaded_obj);
20698    }
20699}
20700
20701sub save_last_loaded {
20702    my $last_loaded_obj = shift;
20703    if (@{ $last_loaded_obj->{List} } && open(LAST, ">$last_loaded_obj->{File}")) {
20704	print LAST join("\n", @{ $last_loaded_obj->{List} }), "\n";
20705	close LAST;
20706    }
20707}
20708
20709sub update_last_loaded_menu {
20710    my $last_loaded_obj = shift;
20711    my $last_loaded_menu = $last_loaded_obj->{Menu};
20712    return unless $last_loaded_menu;
20713    if (!Tk::Exists($last_loaded_menu)) {
20714	die "XXX Can't update last loaded menu $last_loaded_menu";
20715    }
20716    $last_loaded_menu->delete(0, 'end');
20717    if (!@{ $last_loaded_obj->{List} }) {
20718	$last_loaded_menu->command(-label => "Flaschen leer",# kein M
20719				   -state => 'disabled',
20720				   -font => $font{'bold'});
20721    } else {
20722	$last_loaded_menu->command(-label => $last_loaded_obj->{Title},
20723				   -state => 'disabled',
20724				   -font => $font{'bold'});
20725	foreach my $_file (@{ $last_loaded_obj->{List} }) {
20726	    my($file, @args) = split /\t/, $_file;
20727	    $last_loaded_menu->command(-label => $file,
20728				       -command => [$last_loaded_obj->{Cb}, $file, @args],
20729				      );
20730	}
20731    }
20732}
20733
20734### AutoLoad Sub
20735sub fast_settings {
20736    foreach (keys %init_str_draw) {
20737	$init_str_draw{$_} = 0;
20738	$str_outline{$_} = 0;
20739    }
20740    foreach (keys %init_p_draw) {
20741	$init_p_draw{$_} = 0;
20742    }
20743    $show_grade = 0;
20744    $use_logo  = 0;
20745    undef $center_on_str;
20746    undef $center_on_coord;
20747    $init_choose_street = 0;
20748    $autosave_opts = 0; # besser ist's
20749    $do_activate_temp_blockings = 0;
20750}
20751
20752sub set_mouse_desc {
20753    if ($special_edit eq 'radweg') {
20754	$mouse_text[1] = M"Radweg editieren";
20755	$mouse_text[2] = M"Letzte Aktion wiederholen";
20756	$mouse_text[3] = '';
20757    } elsif ($special_edit eq 'ampel') {
20758	$mouse_text[1] = M"Ampel editieren";
20759	$mouse_text[2] = $mouse_text[3] = '';
20760    } else {
20761	$mouse_text[1] = M"Punkt zur Route hinzuf�gen\nMit Alt oder Shift: Mauscursor muss sich nicht �ber einer Stra�e befinden\nZiehen der Maus: Bewegen der Karte";
20762	my $label = $b2_mode_desc{$b2_mode};
20763	if (defined $label) {
20764	    $mouse_text[2] = $label;
20765	} else {
20766	    $mouse_text[2] = "???";
20767	}
20768	if ($right_is_popup) {
20769	    $mouse_text[3] = M"Popup-Men�";
20770	} else {
20771	    $mouse_text[3] = M"Gesamte Route l�schen";
20772	}
20773    }
20774}
20775
20776sub change_font {
20777    my($font_type) = @_;
20778    $font_type = "normal" if !$font_type;
20779    eval {
20780	require Tk::FontDialog;
20781	Tk::FontDialog->VERSION(0.06); # -fixedfont...
20782    };
20783    if ($@) {
20784	return if !perlmod_install_advice('Tk::FontDialog');
20785    }
20786
20787    my %fd_args;
20788    if ($font_type eq 'fixed') {
20789	$fd_args{'-fixedfont'} = 1;
20790	$fd_args{'-fixedfontsbutton'} = 0;
20791	$fd_args{'-initfont'} = $font{'fixed'};
20792    }
20793    my $fedit = $top->FontDialog(%fd_args);
20794    my $f = $fedit->Show;
20795    if (defined $f) {
20796	if ($font_type eq 'fixed') {
20797	    $font{'fixed'} = $f; # XXX probably this does not re-set existing labels
20798	    $fixed_font_family = $top->fontActual($font{'fixed'}, '-family');
20799	    # XXX note that there's no way to set the point size
20800	} else {
20801	    my $normal_font = $f;
20802	    set_fonts($normal_font);
20803	    $top->optionAdd("*font" => $font{normal}, 'userDefault');
20804	}
20805    }
20806}
20807
20808sub size2px {
20809    my $size = shift;
20810    $size <  0 ? -$size : int(0.5 + $size*($top_dpi/72));
20811}
20812
20813sub size2pt {
20814    my $size = shift;
20815    $size >= 0 ?  $size : int(0.5 - $size/($top_dpi/72));
20816}
20817
20818# A part of set_fonts which has to be called very early
20819sub set_sans_serif_font_family {
20820    $has_xft = 0;
20821    $sans_serif_font_family = "Helvetica";
20822    eval {
20823	require Tk::Config;
20824	$has_xft = $Tk::Config::xlib =~ /-lXft\b/;
20825	if ($has_xft) {
20826	    $sans_serif_font_family = "sans-serif";
20827	}
20828    };
20829}
20830
20831# Create the fontset for bbbike. Use $std_font (which must be a
20832# current Tk font name, not a font specification) as default normal
20833# font, or, if not defined, use the system default (e.g. from the
20834# option database). The fontset is stored to the global hash %font.
20835# $top is the main window.
20836sub set_fonts {
20837    my $std_font = shift;
20838    # backward compatibility with Tk 402:
20839    if ($Tk::VERSION <= 402.004) {
20840	set_fonts_402();
20841    } else {
20842	# XXX check it under all platforms!
20843	my $get_std_font = sub {
20844	    my $std_font = $top->optionGet('font', 'Font');
20845	    if (!defined $std_font || $std_font eq '') {
20846		my $l = $top->Label;
20847		$std_font = $l->cget(-font);
20848		if ($^O eq 'MSWin32') {
20849		    # Using MS Sans Serif is probably not correct
20850		    # See also: http://www.tcl.tk/cgi-bin/tct/tip/64.html
20851		    my(%std_font) = $l->fontActual($std_font);
20852		    if ($std_font{-family} =~ m{ms sans serif}i) {
20853			my %font_families = map{(lc($_),1)} $top->fontFamilies;
20854			my $new_family = (  exists $font_families{tahoma} ? "tahoma"
20855					  : exists $font_families{arial} ? "arial" : undef
20856					 );
20857			if (defined $new_family) {
20858			    $std_font = $top->fontCreate(-family => $new_family,
20859							 -size => size2pt($std_font{-size}));
20860			}
20861		    }
20862		}
20863		$l->destroy;
20864	    }
20865	    $std_font;
20866	};
20867
20868	my $font_from_user = 0; # true, if from options or set interactively
20869	my $font_size_from_user = 0;
20870	if (!$std_font) {
20871	    # $font_family, $font_size, $font_weight from cmdline
20872	    if (defined $font_family && $font_family ne "" && !$kde) {
20873		if (!defined $font_size) {
20874		    my $std_font = $get_std_font->();
20875		    $font_size = $top->fontActual($std_font, '-size');
20876		} else {
20877		    $font_size_from_user = 1;
20878		}
20879		$font_from_user = 1;
20880		my(%a) = (-family => $font_family);
20881		if (defined $font_size && $font_size =~ /^-?\d+$/) {
20882		    $a{-size} = $font_size;
20883		} elsif (defined $font_size) {
20884		    warn "Font size defined as <$font_size>, but does not match pattern, so fallback to default size...";
20885		    $a{-size} = $font_size = 10;
20886		}
20887		if (defined $font_weight && $font_weight ne '') {
20888		    $a{-weight} = $font_weight;
20889		}
20890		eval {
20891		    $std_font = $top->fontCreate(%a);
20892		};
20893		if ($@) {
20894		    my $err = $@;
20895		    $std_font = "helvetica 10";
20896		    print STDERR Mfmt("Fehler beim Definieren des Zeichensatzes:\n" .
20897			      "%s\n" .
20898			      "Fallback auf den Zeichensatz <%s>.\n",
20899			      $err, $std_font) .
20900			 wrap("", "",
20901			      Mfmt("Dieser Fehler kann m�glicherweise durch Korrigieren der Eintr�ge <fontfamily> und <fontheight> in <%s> oder <*font> in <~/.Xdefaults> behoben werden.",
20902				   catfile($bbbike_configdir, "config"))) .
20903			 "\n";
20904		}
20905		$top->optionAdd('*font' => $std_font, 'userDefault');
20906	    } else {
20907		$std_font = $get_std_font->();
20908	    }
20909	} else {
20910	    $font_from_user = $font_size_from_user = 1;
20911	}
20912
20913	if (exists $font{'normal'} && $std_font) {
20914	    $top->fontConfigure($font{normal}, $top->fontActual($std_font));
20915	} elsif ($std_font) {
20916	    $font{'normal'} = $top->fontCreate($top->fontActual($std_font));
20917	} else {
20918	    $font{'normal'} = $top->fontCreate;
20919	}
20920
20921        my %normal_attr = $top->fontActual($font{'normal'});
20922
20923        my $size = $normal_attr{'-size'}; # points or pixels depending on Tk ver
20924	my $px = size2px($size);
20925	my $pt = size2pt($size);
20926	my $win_width = @want_extends ? $want_extends[GEOMETRY_WIDTH] : $top->width;
20927	if ($win_width <= 800 && $pt >= 10 && !$font_size_from_user) {
20928## XXX This is evil: because the fontsize will be from time to time smaller
20929## if the use resizes below the limits and then above the limits.
20930## On the other side, this will result in too big fonts on small
20931## displays. Solution?
20932	    if ($win_width <= 320) {
20933		$pt = $pt*8/14;
20934	    } elsif ($win_width <= 640) {
20935 		$pt = $pt*10/14;
20936 	    } else {
20937 		$pt = $pt*12/14;
20938 	    }
20939 	    $top->fontConfigure($font{'normal'}, -size => sprintf("%.f", $pt));
20940	}
20941
20942	# This looks like a no-op, as $font{normal} was usually
20943	# determined from the default font in the option database. But
20944	# with this setting the _Tk font name_ is forced to be used.
20945	# This is a requirement to see immediate effects when changing
20946	# the font using FontDialog.
20947	$top->optionAdd('*font' => $font{'normal'}, 'userDefault');
20948
20949	foreach (qw(veryhuge huge verylarge large bold
20950		    reduced small tiny fixed standard fix15)) {
20951	    if (exists $font{$_}) {
20952		$top->fontConfigure($font{$_}, $top->fontActual($font{'normal'}));
20953	    } else {
20954		$font{$_} = $top->fontCreate($top->fontActual($font{'normal'}));
20955	    }
20956	}
20957
20958	my $minfs = sub {
20959	    my $fs = shift;
20960	    $fs = 6 if ($fs < 6);
20961	    $fs;
20962	};
20963
20964	$top->fontConfigure($font{'bold'},
20965			    -size => sprintf("%.f", $minfs->($pt)),
20966			    -weight => 'bold');
20967	$top->fontConfigure($font{'fix15'}, # exactly 15 pixels height (if not $small_icons in effect)
20968			    -size => ($small_icons ? -8 : -15));
20969	$top->fontConfigure($font{'tiny'},
20970			    -size => sprintf("%.f", $minfs->($pt*8/14)));
20971	$top->fontConfigure($font{'small'},
20972			    -size => sprintf("%.f", $minfs->($pt*10/14)));
20973	$top->fontConfigure($font{'reduced'},
20974			    -size => sprintf("%.f", $minfs->($pt*12/14)));
20975	$top->fontConfigure($font{'large'},
20976			    -size => sprintf("%.f", $minfs->($pt*18/14)));
20977	$top->fontConfigure($font{'verylarge'},
20978			    -size => sprintf("%.f", $minfs->($pt*24/14)));
20979	$top->fontConfigure($font{'huge'},
20980			    -size => sprintf("%.f", $minfs->($pt*28/14)));
20981	$top->fontConfigure($font{'veryhuge'},
20982			    -size => sprintf("%.f", $minfs->($pt*36/14)));
20983	$top->fontConfigure($font{'standard'},
20984			    -size => $standard_height,
20985			    -slant => 'roman',
20986			    -underline => 0,
20987			    -overstrike => 0);
20988	if ($pt >= 8) {
20989	    $top->fontConfigure($font{fixed}, -family => $fixed_font_family);
20990	} else {
20991	    $font{'fixed'} = "5x7"; # XXX really necessary?
20992	}
20993
20994## Here from a Win98 session what fonts are readable
20995##
20996#  Arial:          unterhalb von 5pt: nicht zu gebrauchen
20997#		   5pt - 6pt: noch lesbar, aber einige Buchstaben
20998#			      sehen kaputt aus
20999#		   7pt: OK
21000#  Courier New:	   5pt: kaum lesbar
21001#		   6pt: sieht ziemlich schlecht aus
21002#		   7pt: OK
21003#  Lucida Console:  unterhalb von 5pt: nicht zu gebrauchen
21004#		   5pt: erstaunlich gut
21005#		   6pt und mehr: OK
21006#  MS Sans Serif:  9pt scheint die Minimalgr��e zu sein
21007#  MS Serif:       6pt ist die Minimalgr��e und recht gut lesbar
21008#  System:         16pt scheint die Minimalgr��e zu sein
21009#  Tahoma:         unterhalb von 5pt: nicht zu gebrauchen
21010#		   5pt - 6pt: noch lesbar, aber einige Buchstaben
21011#			      sehen kaputt aus
21012#		   7pt: OK
21013#  Times New Roman:unterhalb von 10px (6pt): nicht zu gebrauchen
21014#		   10px (6pt): ein paar Buchstaben sehen komisch aus
21015#		   12px (7pt): OK, wenn auch etwas gequetscht
21016#  Verdana:        unterhalb von 5pt: nicht zu gebrauchen
21017#		   5pt - 6pt: noch lesbar, aber einige Buchstaben
21018#			      sehen kaputt aus
21019#		   7pt: OK
21020#  Pixel <-> Point (bei Times New Roman)
21021#    3         2
21022#    4         2
21023#    5         4
21024#    6         5
21025#    7         5
21026#    8         5
21027#    9         5
21028#   10         6
21029#   11         6
21030#   12         7
21031#   13         8
21032#   14         8
21033#   15         9
21034#
21035	my %min_px =
21036	    ('helvetica'	      => 8,
21037	     'times'		      => 10,
21038	     'times new roman'        => 10, # 12 w�re eigentlich besser
21039	     'lucida'		      => 8,
21040	     'new century schoolbook' => 8,
21041	     'fixed'		      => 7,
21042	     'arial'		      => 8, # at this size some characters already look somewhat broken (seen on Win98, 800x600 screen)
21043	     'courier new'	      => 8,
21044	     '__DEFAULT__'	      => 8,
21045	    );
21046
21047	# Resize if necessary, to prevent fonts from being too small.
21048	# This is from looking at readable fonts under the iPAQ. I found
21049	# that Lucida can produce the smallest readable fonts.
21050	while(my($k,$v) = each %font) {
21051	    my $family = lc $top->fontActual($v, '-family');
21052	    my $min_px = $min_px{$family} || $min_px{__DEFAULT__};
21053	    my $current_size = $top->fontActual($v, "-size");
21054	    my $current_px = size2px($current_size);
21055	    if ($current_px < $min_px) {
21056		$top->fontConfigure($v, -size => -$min_px);
21057	    }
21058	}
21059    }
21060
21061    # Array of sorted fonts (by size) used e.g. in
21062    # get_orte_label_font()
21063    @font = qw(tiny small reduced normal large verylarge huge veryhuge);
21064
21065    for my $font (@font) {
21066	my $font_key = $font."-italic";
21067	eval {
21068	    if (exists $font{$font_key}) {
21069		$top->fontConfigure($font{$font_key},
21070				    $top->fontActual($font{$font}, -slant => "italic"),
21071				   );
21072	    } else {
21073		$font{$font_key} = $top->fontCreate($top->fontActual($font{$font}), -slant => "italic");
21074	    }
21075	};
21076	if ($@ || !$font{$font_key}) {
21077	    # fallback to non-italic variant
21078	    $font{$font_key} = $font{$font};
21079	}
21080    }
21081}
21082
21083# Set image, if available, otherwise the fallback label
21084sub image_or_text {
21085    my($image, $text) = @_;
21086    if (defined $image) {
21087	(-image => $image);
21088    } else {
21089	(-text => $text);
21090    }
21091}
21092
21093# Doc?
21094### AutoLoad Sub
21095sub image_from_file {
21096    my($top, $file, %args) = @_;
21097    my $mimetype  = $args{'-mimetype'};
21098    my $colormode = $args{'-colormode'} || 'color';
21099
21100    if ($file =~ /\.jpe?g$/i ||
21101	(defined $args{-mimetype} and $args{-mimetype} eq 'image/jpeg')) {
21102	eval { require Tk::JPEG };
21103	if ($@) {
21104	    return if !perlmod_install_advice('Tk::JPEG');
21105	}
21106    } elsif ($file =~ /\.png$/i ||
21107	     (defined $args{-mimetype} and $args{-mimetype} eq 'image/png')) {
21108	eval { require Tk::PNG };
21109	if ($@) {
21110	    return if !perlmod_install_advice('Tk::PNG');
21111	}
21112    }
21113
21114    if ($colormode eq 'mono') {
21115	$top->Bitmap(-file => $file);
21116    } elsif ($colormode eq 'pixmap') {
21117	$top->Pixmap(-file => $file);
21118    } elsif ($colormode eq 'gray') {
21119	$top->Photo(-file => $file, -palette => 8);
21120    } else {
21121	$top->Photo(-file => $file);
21122    }
21123}
21124
21125# Load the image from file $file. Do nothing if $lowmem mode
21126# is set. If the -persistent is set, then store the image into the
21127# persistent %photo hash for caching. -name can be set
21128# for a Tcl-styled image name. In $small_icons mode every
21129# image is shrinked to half width/height.
21130#
21131# .xpm files are loaded into a Tk::Pixmap object, not Tk::Photo
21132# (unless $small_icons is active)
21133#
21134# .svg files are converted using the svg2photo function. In
21135# this case, the options -w and -h are mandatory.
21136sub load_photo {
21137    my($top, $file, %args) = @_;
21138    if (!defined $file) {
21139	require Data::Dumper;
21140	die "File missing in load_photo, called in " . Dumper(caller);
21141    }
21142
21143    my $cache_key = $file;
21144    my %photo_args;
21145    for my $key (qw(-palette -gamma)) {
21146	if (exists $args{$key}) {
21147	    my $val = $photo_args{$key} = delete $args{$key};
21148	    $cache_key .= "-$key:$val";
21149	}
21150    }
21151    for my $key (qw(-w -h)) {
21152	if (exists $args{$key}) {
21153	    my $val = $args{$key};
21154	    $cache_key .= "-$key:$val";
21155	}
21156    }
21157    return $photo{$cache_key} if exists $photo{$cache_key};
21158
21159    my $photo;
21160    unless ($lowmem) {
21161	eval {
21162	    my @name = exists $args{-name} ? ($args{-name}) : ();
21163	    my $do_subsample = $small_icons;
21164	    if ($file =~ m{\.xpm$}i && !$small_icons) {
21165		# Pixmap seem to be more memory-efficient, but it's
21166		# not possible to do subsample operations (in case of
21167		# $small_icons)
21168		$photo = $top->Pixmap(@name, -file => Tk::findINC($file));
21169	    } else {
21170		my $path;
21171		if (file_name_is_absolute($file)) {
21172		    $path = $file;
21173		} else {
21174		    for my $try_file ((-d "$datadir/images" ? "$datadir/images/$file" : ()),
21175				      "$FindBin::RealBin/images/$file",
21176				     ) {
21177			my $try_path = try_image_suffix($try_file);
21178			if (defined $try_path && -r $try_path) {
21179			    $path = $try_path;
21180			    last;
21181			}
21182		    }
21183		    if (!defined $path) {
21184			warn "Could not find photo, try <$file> in some \@INC dirs...\n"; # XXX should never happen?
21185			$path = Tk::findINC($file);
21186		    }
21187		}
21188		if ($path) {
21189		    if ($path =~ m{\.svg$}i) {
21190			my $w = delete $args{-w};
21191			die "-w is mandatory for svg files" if !$w;
21192			my $h = delete $args{-h};
21193			die "-h is mandatory for svg files" if !$h;
21194			if ($small_icons) {
21195			    $w /= 2;
21196			    $h /= 2;
21197			    $do_subsample = 0;
21198			}
21199			$photo = svg2photo($path, $w, $h);
21200		    } else {
21201			$photo = $top->Photo(@name, -file => $path, %photo_args);
21202		    }
21203		}
21204	    }
21205	    if ($do_subsample && $photo) {
21206		# XXX setting of @name missing
21207		my $small_photo = $top->Photo(-width => $photo->width/2,
21208					      -height => $photo->height/2,
21209					      %photo_args);
21210		$small_photo->copy($photo, -subsample => 2, 2);
21211		$photo->delete;
21212		$photo = $small_photo;
21213	    }
21214	};warn $@ if $@;
21215    }
21216    if ($args{-persistent}) {
21217	$photo{$cache_key} = $photo;
21218    }
21219    $photo;
21220}
21221
21222sub load_cursor {
21223    my($def) = @_;
21224    return if $Tk::platform eq 'MSWin32'; # no support for custom cursors yet
21225    my $key = my $lang_def = $def;
21226    if ($def eq 'ziel') {
21227	$lang_def = M($def);
21228    }
21229    my $base = $lang_def . '_ptr.xbm';
21230    my $xbm = Tk::findINC($base);
21231    if (!defined $xbm) {
21232	print STDERR Mfmt("Die Datei <%s> existiert nicht.", $base) . "\n";
21233    } elsif (-r $xbm) {
21234	my $mask = Tk::findINC($lang_def . '_ptr_mask.xbm');
21235	if (-r $mask) {
21236	    $cursor{$key}      = $xbm;
21237	    $cursor_mask{$key} = $mask;
21238	}
21239    }
21240}
21241
21242sub load_stipple {
21243    my($def) = @_;
21244    return $stipple{$def} if exists $stipple{$def};
21245    if ($def =~ m{^gray(?:25|50|75)$}) { # some builtins
21246	$stipple{$def} = $def;
21247    } else {
21248	$stipple{$def} = Tk::findINC($def);
21249	if ($stipple{$def}) {
21250	    $stipple{$def} = '@' . $stipple{$def};
21251	}
21252    }
21253    $stipple{$def};
21254}
21255
21256# do a correct isa call on scrolled widgets
21257sub subw_isa {
21258    my($w, $isa) = @_;
21259    if ($w->Subwidget('scrolled')) {
21260	$w = $w->Subwidget('scrolled');
21261    }
21262    $w->isa($isa);
21263}
21264
21265# Callback bei einem Drop-Vorgang.
21266# Die Datei wird per load_save_route() geladen.
21267### AutoLoad Sub
21268sub accept_drop {
21269    my($c, $seln) = @_;
21270    my $filename;
21271    my @targ = $c->SelectionGet('-selection'=>$seln,'TARGETS');
21272    foreach (@targ) {
21273	if (/FILE_NAME/) {
21274	    $filename = $c->SelectionGet('-selection'=>$seln,'FILE_NAME');
21275	    last;
21276	}
21277	if ($os eq 'win' && /STRING/) {
21278	    $filename = $c->SelectionGet('-selection'=>$seln,$_);
21279	    last;
21280	}
21281    }
21282    if (defined $filename) {
21283	if ($filename =~ /\.bbd/i) {
21284	    plot_layer('str', $filename);
21285	} else {
21286	    load_save_route(0, $filename);
21287	}
21288    }
21289}
21290
21291
21292# Return the start and goal streets of the current route
21293### AutoLoad Sub
21294sub get_route_description {
21295    my(%args) = @_;
21296    my $with_via = exists $args{-withvia} ? delete $args{-withvia} : 1;
21297    if (%args) {
21298	warn "WARNING: get_route_description called with extra arguments: " . join(" ", %args);
21299    }
21300
21301    my $text = "";
21302    my @search_route = @{ get_act_search_route() };
21303    if (@search_route) {
21304	$text = $search_route[0][StrassenNetz::ROUTE_NAME];
21305	if ($with_via && @search_route_points > 2) { # do we have a via?
21306	    # XXX This is a simple solution. A better one use the
21307	    # farthest point instead the point in the middle of the
21308	    # list.
21309	    $text .= " - " . $search_route[@search_route/2][StrassenNetz::ROUTE_NAME];
21310	}
21311	$text .= " - " . $search_route[-1][StrassenNetz::ROUTE_NAME];
21312    }
21313    $text;
21314}
21315
21316# Return the approximated center of the polyline.
21317# Coordinates of the polygon are supplied in @koord (flat list of x and y
21318# values).
21319# If @koord is just a point then return it.
21320### AutoLoad Sub
21321sub get_polyline_center {
21322    my(@koord) = @_;
21323    return @koord if @koord == 2;
21324    my $len = 0;
21325    for(my $i=2; $i<$#koord; $i+=2) {
21326	$len += Strassen::Util::strecke([@koord[$i-2,$i-1]],
21327					[@koord[$i,  $i+1]]);
21328    }
21329    my $len0 = 0;
21330    for(my $i=2; $i<$#koord; $i+=2) {
21331	$len0 += Strassen::Util::strecke([@koord[$i-2,$i-1]],
21332					 [@koord[$i,  $i+1]]);
21333	if ($len0 > $len/2) {
21334	    # XXX ungenau, besser machen!
21335	    return (($koord[$i-2]-$koord[$i])/2+$koord[$i],
21336		    ($koord[$i-1]-$koord[$i+1])/2+$koord[$i+1]);
21337	}
21338    }
21339    warn "Fallback for get_polyline_center, should not happen. Coords are @koord";
21340    (($koord[2]-$koord[0])/2+$koord[0],
21341     ($koord[3]-$koord[1])/2+$koord[1]);
21342}
21343
21344### AutoLoad Sub
21345sub get_bbox_area {
21346    my($item) = @_;
21347    my(@bbox) = $c->bbox($item);
21348    abs(($bbox[2]-$bbox[0]) * ($bbox[3]-$bbox[1]));
21349}
21350
21351# Erzeugt eine Backupdatei
21352### AutoLoad Sub
21353sub make_backup {
21354    my $file = shift;
21355    if (-e $file) {
21356	if (-f $file) {
21357	    my $backup = "$file~";
21358	    rename $file, $backup;
21359	} else {
21360	    status_message(Mfmt("%s ist keine g�ltige Datei, kein Backup.",
21361				$file),
21362			   'err');
21363	}
21364    }
21365}
21366
21367use your qw($StrassenNetz::VERBOSE $Strassen::VERBOSE $wettermeldung2::VERBOSE
21368	    $Tk::SRTProgress::VERBOSE
21369	    $Telefonbuch::VERBOSE $GfxConvert::VERBOSE $Hooks::VERBOSE
21370	    $FURadar::VERBOSE);
21371
21372# Setzt die VERBOSE-Variable in den geladenen Modulen
21373### AutoLoad Sub
21374sub set_verbose {
21375    Strassen::set_verbose($verbose);
21376    $wettermeldung2::VERBOSE  = $verbose;
21377    $Tk::SRTProgress::VERBOSE = $verbose;
21378    $Telefonbuch::VERBOSE     = $verbose;
21379    $GfxConvert::VERBOSE      = $verbose;
21380    $Hooks::VERBOSE           = $verbose;
21381    $FURadar::VERBOSE         = $verbose;
21382    $PLZ::VERBOSE             = $verbose;
21383}
21384
21385# crops the array in $want_extends to the limits in $extends
21386sub crop_geometry {
21387    my($want_extends, $extends) = @_;
21388
21389    # right/bottom limits
21390    my $x = $want_extends->[GEOMETRY_X] =~ /^-/ ?
21391	$top->screenwidth - $want_extends->[GEOMETRY_WIDTH] + $want_extends->[GEOMETRY_X] :
21392	    $want_extends->[GEOMETRY_X];
21393    my $y = $want_extends->[GEOMETRY_Y] =~ /^-/ ?
21394	$top->screenheight - $want_extends->[GEOMETRY_HEIGHT] + $want_extends->[GEOMETRY_Y] :
21395	    $want_extends->[GEOMETRY_Y];
21396    my($maxx) = $want_extends->[GEOMETRY_WIDTH] + $x;
21397    my($maxy) = $want_extends->[GEOMETRY_HEIGHT] + $y;
21398
21399    if ($x < $extends->[GEOMETRY_X]) {
21400	$want_extends->[GEOMETRY_X] = $extends->[GEOMETRY_X];
21401    }
21402    if ($y < $extends->[GEOMETRY_Y]) {
21403	$want_extends->[GEOMETRY_Y] = $extends->[GEOMETRY_Y];
21404    }
21405    if ($x + $want_extends->[GEOMETRY_WIDTH] > $extends->[GEOMETRY_WIDTH]) {
21406	$want_extends->[GEOMETRY_WIDTH] = $extends->[GEOMETRY_WIDTH] - $x;
21407    }
21408    if ($y + $want_extends->[GEOMETRY_HEIGHT] > $extends->[GEOMETRY_HEIGHT]) {
21409	$want_extends->[GEOMETRY_HEIGHT] = $extends->[GEOMETRY_HEIGHT] - $y;
21410    }
21411}
21412
21413sub parse_geometry_string {
21414    my $geometry = shift;
21415    my @extends = (0, 0, 0, 0);
21416    if ($geometry =~ /([-+]?\d+)x([-+]?\d+)/) {
21417	$extends[GEOMETRY_WIDTH] = $1;
21418	$extends[GEOMETRY_HEIGHT] = $2;
21419    }
21420    if ($geometry =~ /([-+]\d+)([-+]\d+)/) {
21421	$extends[GEOMETRY_X] = $1;
21422	$extends[GEOMETRY_Y] = $2;
21423    }
21424    @extends;
21425}
21426
21427# Alternative way to set geometry.
21428sub geometry {
21429    my($t, @extends) = @_;
21430    my $geometry = "$extends[GEOMETRY_WIDTH]x$extends[GEOMETRY_HEIGHT]";
21431    $extends[GEOMETRY_X] = "+$extends[GEOMETRY_X]" if $extends[GEOMETRY_X] !~ /^[+-]/;
21432    $extends[GEOMETRY_Y] = "+$extends[GEOMETRY_Y]" if $extends[GEOMETRY_Y] !~ /^[+-]/;
21433    $geometry .= $extends[GEOMETRY_X] . $extends[GEOMETRY_Y];
21434    $t->geometry($geometry);
21435}
21436
21437sub fix_geometry {
21438    my $geom_string = shift || $top->geometry;
21439    my(@extends) = parse_geometry_string($geom_string);
21440    $extends[GEOMETRY_HEIGHT] += ($top->wrapper)[1];
21441    if ($^O eq 'MSWin32') {
21442        # This seems to be necessary at least on a Win98 machine
21443        # or maybe only on systems where wrapper[1] returns 0?
21444        # 20 should probably be replaced by the value of $SM_CYCAPTION, see Win32Util (19 on this system)
21445        $extends[GEOMETRY_HEIGHT] += 20; # get titlebar height (?) by API functions XXX
21446    }
21447    "$extends[GEOMETRY_WIDTH]x$extends[GEOMETRY_HEIGHT]" .
21448	($extends[GEOMETRY_X] =~ m{^[+-]} ? '' : '+') . $extends[GEOMETRY_X] .
21449	    ($extends[GEOMETRY_Y] =~ m{^[+-]} ? '' : '+') . $extends[GEOMETRY_Y];
21450}
21451
21452# check if the toplevel is too large and resize, if appropriate
21453sub toplevel_checker {
21454    my($t) = @_;
21455    $t->update;
21456    my($sw,$sh) = ($t->screenwidth, $t->screenheight);
21457    my($x,$y,$w,$h) = ($t->x, $t->y, $t->width, $t->height);
21458    $w = $sw if ($w > $sw);
21459    $h = $sh if ($h > $sh);
21460    $x = 0 if ($x+$w > $sw || $x < 0);
21461    $y = 0 if ($y+$h > $sh || $y < 0);
21462    $t->geometry($w."x".$h."+$x+$y");
21463}
21464
21465sub get_polar_location_of_route_end {
21466    return undef if !@realcoords;
21467    require Karte::Polar;
21468    my($px,$py) = $Karte::Polar::obj->standard2map(@{ $realcoords[-1] });
21469    "$px,$py";
21470}
21471
21472sub my_popup {
21473    my $t = shift;
21474    $t->withdraw;
21475    $t->Popup(@popup_style);
21476}
21477
21478sub optedit {
21479    my(%args) = @_;
21480    my $opt_edit = $top->{GetoptEditor};
21481    if (Tk::Exists($opt_edit)) {
21482	$opt_edit->raise;
21483	if ($args{-page}) {
21484	    $opt->raise_page($args{-page});
21485	}
21486	return;
21487    }
21488
21489    # Hack for small screens. Should be a better solution in
21490    # Tk::Getopt. Unfortunately there's even no -font option
21491    # in Tk::Getopt, so try to use a option db hack.
21492    if ($top->screenwidth <= 11024) {
21493	my $font = $top->screenwidth <= 800 ? $font{small} : $font{reduced};
21494	$top->optionAdd("*bbbikeOptionEditor*font" => $font);
21495    }
21496
21497    $opt_edit =	$opt->option_editor
21498	($top,
21499	 Name => "bbbikeOptionEditor", # lowercase beginning!
21500	 ($transient ? (-transient => $top) : ()),
21501	 (!defined $ENV{LANG} || $ENV{LANG} =~ /^de/ ?
21502	  (-string => {optedit => "Optionseditor",
21503		       undo => "Undo",
21504		       lastsaved => "Zuletzt gespeichert",
21505		       save => "Speichern",
21506		       defaults => "Voreinstellungen",
21507		       ok => "Ok",
21508		       oksave => "Ok",
21509		       apply => "Anwenden",
21510		       cancel => "Abbrechen",
21511		       helpfor => "Hilfe f�r",
21512		      }
21513	  ) : ()),
21514	 -buttons => ['oksave',
21515		      #'defaults', # XXX defaults or not defaults???
21516		      #could be misleading, users might think that the
21517		      #defaults just apply to the displayed page
21518		      'cancel'],
21519	 %args, # e.g. -page
21520	);
21521    $top->{GetoptEditor} = $opt_edit;
21522}
21523
21524sub export_visible_map {
21525    my($fmt, $outfile) = @_;
21526
21527    if (!defined $outfile) {
21528	$outfile = $top->getSaveFile
21529	    (-defaultextension => ".$fmt",
21530	     -title => Mfmt('%s-Datei sichern', uc($fmt)),
21531	     -initialdir => $save2_path);
21532    }
21533    return if !defined $outfile;
21534    $save2_path = dirname $outfile;
21535
21536    # Temporarily close all toplevels to make sure that the
21537    # canvas window is topmost (but it's still not perfect!)
21538    my $redisplay_toplevels;
21539    {
21540	my %withdrawn_toplevels;
21541	# $top->stackorder did not work under MSWin32 before Tk 804.028 (but ->can returned true!)
21542	my @stackorder = $Tk::platform ne "MSWin32" || $Tk::VERSION >= 804.028 ? $top->stackorder : ();
21543	$top->Walk(sub {
21544		       my($w) = @_;
21545		       if (Tk::Exists($w) && $w->isa("Tk::Wm") && eval { $w->state } eq 'normal') {
21546			   $w->withdraw;
21547			   $withdrawn_toplevels{$w->PathName()} = $w;
21548		       }
21549		   });
21550	$redisplay_toplevels = sub {
21551	    my %handled_toplevels;
21552	    for my $tpn (reverse @stackorder) {
21553		if ($withdrawn_toplevels{$tpn}) {
21554		    eval { $withdrawn_toplevels{$tpn}->deiconify };
21555		    $handled_toplevels{$tpn}++;
21556		} else {
21557		}
21558	    }
21559	    while(my($tpn, $w) = each %withdrawn_toplevels) {
21560		if (!$handled_toplevels{$tpn}) {
21561		    eval { $w->deiconify };
21562		}
21563	    }
21564	};
21565    }
21566    $top->raise;
21567    $top->update;
21568    $top->tk_sleep(1); # make sure the update was really done
21569
21570    my $imager_fmt = $fmt eq 'ppm' ? 'pnm' : $fmt;
21571    if ($devel_host && eval {
21572	require Imager;
21573	Imager->VERSION(0.62);
21574	die "Imager does not support image format <$imager_fmt>, use fallback...\n"
21575	    if !grep { $imager_fmt eq $_ } Imager->write_types;
21576	require Imager::Screenshot;
21577	Imager::Screenshot->VERSION(0.005);
21578	1;
21579    }) {
21580	my $img;
21581	eval {
21582	    ## This should work, but does not, because $widget->can("frame") seems
21583	    ## to be always true
21584	    ## XXX for Version 0.006 this will work:
21585	    #$img = Imager::Screenshot::screenshot(widget => $c);
21586	    #my $img = Imager::Screenshot::screenshot(widget => $c, decor => 0);
21587	    $img = Imager::Screenshot::screenshot(($Tk::platform eq 'MSWin32' ? 'hwnd' : 'id'),
21588						  hex $c->id);
21589	    if ($img) {
21590		$img->write(file => $outfile, type => $imager_fmt) or $img = undef;
21591	    }
21592	};
21593	warn $@ if $@;
21594	$redisplay_toplevels->();
21595	if (!$img) {
21596	    status_message("Imager and Imager::Screenshot installed, but screenshot failed", "warn");
21597	} else {
21598	    return;
21599	}
21600    }
21601
21602    IncBusy($top);
21603    eval {
21604	my $in_fmt;
21605	my $tmpfile;
21606	my $bgcolor = sprintf('#%02x%02x%02x', map { $_/256 } $c->rgb($c->cget(-background)));
21607	my $NNcolor = sprintf('#%02x%02x%02x', map { $_/256 } $c->rgb($category_color{N}));
21608	my %args =
21609	    (-mapcolor =>
21610	     {# Swap colors to avoid non-white background
21611	      $bgcolor => '#ffffff',
21612	      $NNcolor => $bgcolor,
21613	     },
21614	     -res => $ps_image_res,
21615	     -autocrop => 1,
21616	    );
21617
21618	my $post_processing_needed = 1;
21619
21620	require BBBikePrint; # for using_rotated_fonts
21621	if ((using_rotated_fonts() ||
21622	     $use_xwd_if_possible
21623	    ) and
21624	    $Tk::platform eq 'unix'
21625	    and
21626	    is_in_path("xwd")
21627	   ) {
21628
21629	    $args{-rotate} = -90 if $orientation eq 'portrait';
21630
21631	    $in_fmt = "xwd";
21632	    if ($fmt ne 'xwd') {
21633		require GfxConvert;
21634		GfxConvert::check($in_fmt, $fmt, $tmpfile, $outfile, %args);
21635	    } else {
21636		$post_processing_needed = 0;
21637	    }
21638
21639	    $tmpfile = "/tmp/bbbike.$$.xwd";
21640	    $tmpfiles{$tmpfile}++;
21641
21642	    my $deiconify_subs = withdraw_toplevels();
21643	    $top->raise;
21644	    $top->update;
21645	    system("xwd", "-out", "$tmpfile", "-id", $c->id);
21646	    $_->() for (@$deiconify_subs);
21647	    $top->bell;
21648
21649	} elsif ($fmt eq 'pdf' &&
21650		 !eval {
21651		     require GfxConvert;
21652		     GfxConvert::check($in_fmt, $fmt, $tmpfile, $outfile, %args);
21653		     1;
21654		 }) {
21655	    pdf_export(-visiblemap => 1, -file => $outfile);
21656	    $post_processing_needed = 0;
21657	} else {
21658
21659	    $args{-rotate} = -90 if $orientation eq 'landscape';
21660	    $in_fmt = "ps";
21661
21662	    if ($fmt ne 'ps') {
21663		require GfxConvert;
21664		GfxConvert::check($in_fmt, $fmt, $tmpfile, $outfile, %args);
21665	    } else {
21666		$post_processing_needed = 0;
21667	    }
21668
21669	    die M"Der Export wurde unterbrochen."
21670		if slow_postscript_generation();
21671
21672	    $tmpfile = create_postscript($c,
21673					 -colormode => 'color',
21674					 -rotate => 1,
21675					 -scale_a4 => 0,
21676					);
21677	    if (!defined $tmpfile) {
21678		die M"Tempor�re Postscript-Datei kann nicht erstellt werden.";
21679	    }
21680	}
21681
21682	if (!$post_processing_needed) {
21683	    if (defined $tmpfile) {
21684		mv($tmpfile, $outfile);
21685	    }
21686	} else {
21687	    require GfxConvert;
21688	    # -mapcolor wandelt die Farbe der Nebenstra�en
21689	    # (tk: grey99/ps: 0.9 0.9 0.9) nach wei� um und setzt die
21690	    # Hintergrundfarbe von wei� auf die Hintergrundfarbe des
21691	    # Canvases
21692	    GfxConvert::convert
21693		    ($in_fmt, $fmt, $tmpfile, $outfile,
21694		     %args,
21695		    );
21696	    $tmpfiles{$tmpfile}++;
21697	}
21698    };
21699    my $err = $@;
21700    DecBusy($top);
21701    $redisplay_toplevels->();
21702    if ($err) {
21703	status_message($err, 'err');
21704    }
21705}
21706
21707sub slow_postscript_generation {
21708    # XXX Hmmm, seems to be OK on Win98 with Tk800 and Tk804, with quite a number of layers turned on
21709    # XXX But it is really slow on a Windows2000 system with Tk800 (SiePerl 5.6.1)
21710    return $os eq 'win' &&
21711	$top->messageBox(-icon => "question",
21712			 -message => M"Die PostScript-Erzeugung k�nnte unter Windows langsam sein. Soll trotzdem fortgesetzt werden?",
21713			 -type => "YesNo") !~ /yes/i;
21714}
21715
21716sub get_strassen_file {
21717    my $file = shift;
21718    if ($file =~ /-orig$/) {
21719	# XXX The need to check for this condition seems to be a bug.
21720	# In BBBikeLazy.pm, there are lines like
21721	#     $p_file{$abk} = $file;
21722	# which set the $p_file{...} filename to the -orig version
21723	# in edit mode, and this every time!
21724	$file;
21725    } else {
21726	$file . ($edit_mode_flag ? "-orig" : "");
21727    }
21728}
21729
21730sub get_strassen_obj {
21731    my $file = shift;
21732    my $object;
21733    if ($edit_mode_flag) {
21734	$object = eval { Strassen->new(get_strassen_file($file)) };
21735    }
21736    if (!$object) {
21737	$object = Strassen->new($file); # fallback to non-orig file, if necessary
21738    }
21739    $object;
21740}
21741
21742sub get_any_strassen_obj {
21743    my($linetype, $type) = @_;
21744    my $object;
21745    if ($linetype =~ /^s/) {
21746	if ($type eq 'w') {
21747	    $object = _get_wasser_obj(get_strassen_file($str_file{$type}));
21748	} elsif ($type eq 'l') {
21749	    $object = _get_landstr_obj();
21750	} elsif ($type eq 'comm') {
21751	    $object = _get_comments_obj();
21752	} elsif ($type eq 'fz') {
21753	    $object = _get_fragezeichen_obj();
21754	} else {
21755	    $object = get_strassen_obj($str_file{$type});
21756	}
21757    } else {
21758	$object = get_strassen_obj($p_file{$type});
21759    }
21760    $object;
21761}
21762
21763sub handle_global_directives {
21764    my($s_or_file, $abk) = @_;
21765    my $glob_dir;
21766    if (!ref $s_or_file) {
21767	$glob_dir = Strassen->get_global_directives($s_or_file);
21768    } else {
21769	$glob_dir = $s_or_file->get_global_directives;
21770    }
21771    return if !$glob_dir;
21772    # XXX One day this should contain all of @plotting_hint_line_vars
21773    my %accept_modern_style = map{($_,1)}
21774	qw(line_arrow line_dash);
21775    # XXX Everything else should be slowly discouraged...
21776    my %accept_global_hash_directives = map{($_,1)}
21777	qw(category_size category_color
21778	   category_line_width category_image
21779	   category_stipple category_dash
21780	   category_capstyle
21781	   category_line_shorten category_line_shorten_end
21782	  );
21783    my %accept_global_hashref_directives = map{($_,1)}
21784	qw(str_attrib p_attrib);
21785    my %accept_global_catless_directives = map{($_,1)}
21786	qw(str_color outline_color line_width);
21787    my %accept_global_catless_directives_with_layer_prefix = map{($_,1)}
21788	qw(line_length
21789	   line_dash line_arrow
21790	   line_capstyle
21791	   line_shorten line_shorten_end
21792	   name
21793	  );
21794    # Aliases for directives without category
21795    my %aliases = (
21796		   category_width => "category_line_width",
21797		   line_color => "str_color",
21798		  );
21799    # Aliases for directives with category
21800    my %aliases_withcat = (
21801			   line_color => "category_color",
21802			  );
21803    my @aliases_code = (
21804			sub { $_[0] =~ s{^category_dash\.}{line_dash.}; }, # was used in radwege-orig, and is still used in streets-accurate-categorized..., remove some day XXX
21805		       );
21806
21807    my $get_val = sub {
21808	my($key, $vals) = @_;
21809	my $val = $vals->[0];
21810	if ($key =~ m{(?:_dash|_capstyle|^line_length$)}) { # list of directives using arrays
21811	    $val = [split /\s*,\s*/, $val];
21812	} elsif ($key =~ m{_width}) {
21813	    my @vals = split /\s*,\s*/, $val;
21814	    if (@vals == 1) {
21815		my $factor = $vals[0] / $line_width{default}->[3];
21816		for my $inx (0 .. $#{ $line_width{default} }) {
21817		    $vals[$inx] = int($line_width{default}->[$inx] * $factor + 0.5);
21818		    if ($vals[$inx] < 1) {
21819			$vals[$inx] = 1;
21820		    }
21821		}
21822	    } elsif (@vals != scalar @{ $line_width{default} }) {
21823		warn "$key should have either one or exactly six comma-separated values";
21824	    }
21825	    $val = \@vals;
21826	}
21827	$val;
21828    };
21829
21830    # XXX scrollregion
21831    while(my($directive, $vals) = each %$glob_dir) {
21832	if ($aliases{$directive}) {
21833	    $directive = $aliases{$directive};
21834	}
21835	for my $code (@aliases_code) {
21836	    $code->($directive);
21837	}
21838	if ($accept_modern_style{$directive}) {
21839	    no strict 'refs';
21840	    ${"layer_".$directive}{$abk} = $get_val->($directive, $vals);
21841	} elsif ($accept_global_catless_directives{$directive}) {
21842	    no strict 'refs';
21843	    ${$directive}{$abk} = $get_val->($directive, $vals);
21844	} elsif ($accept_global_catless_directives_with_layer_prefix{$directive}) {
21845	    no strict 'refs';
21846	    ${"layer_".$directive}{$abk} = $get_val->($directive, $vals);
21847	} elsif ($directive eq 'layer_stack') {
21848	    my($how, $other_abk) = split /:/, $vals->[0];
21849	    if (!defined $other_abk) {
21850		status_message("The layer_stack directive needs how:tag as an argument", "die");
21851	    }
21852	    set_in_stack($abk, $how, $other_abk);
21853	} else {
21854	    my($key, $cat) = $directive =~ /^([^\.]+)\.([^\.]+)/;
21855	    if (defined $key) {
21856		if ($aliases_withcat{$key}) {
21857		    $key = $aliases_withcat{$key};
21858		}
21859		if ($accept_modern_style{$key}) {
21860		    no strict 'refs';
21861		    ${"layer_category_".$key}{$abk}{$cat} = $get_val->($key, $vals);
21862		    next;
21863		} elsif ($accept_global_hash_directives{$key}) {
21864		    no strict 'refs';
21865		    ${"layer_".$key}{$abk}{$cat} = $get_val->($key, $vals);
21866		    next;
21867		} elsif ($accept_global_hashref_directives{$key}) {
21868		    no strict 'refs';
21869		    ${$key}->{$abk."-".$cat} = $get_val->($key, $vals); # XXX $abk-???
21870		    next;
21871		}
21872	    }
21873	    #warn "Don't know how to handle global directive <$key>";
21874	}
21875    }
21876}
21877
21878sub withdraw_tearoff_menus {
21879    my($toplevel) = @_;
21880    my @deiconify_subs;
21881    for my $w ($toplevel->children) {
21882	if (Tk::Exists($w) && $w->isa("Tk::Menu") && $w->state eq 'normal') {
21883	    $w->withdraw;
21884	    push @deiconify_subs, sub { $w->deiconify if Tk::Exists($w) };
21885	}
21886    }
21887    @deiconify_subs;
21888}
21889
21890sub withdraw_toplevels {
21891    my $deiconify_subs = [ withdraw_tearoff_menus($top) ];
21892    $top->Walk
21893	(sub {
21894	     my($w) = @_;
21895	     if (Tk::Exists($w) && $w->isa("Tk::Toplevel") &&
21896		 $w->state eq 'normal') {
21897		 $w->withdraw;
21898		 push @$deiconify_subs, sub { $w->deiconify if Tk::Exists($w) };
21899		 push @$deiconify_subs, withdraw_tearoff_menus($w);
21900	     }
21901	 });
21902    $deiconify_subs;
21903}
21904
21905sub set_as_toolwindow {
21906    my($win, $parent) = @_;
21907    if ($transient) {
21908        if (0 && $Tk::platform eq 'MSWin32' && $Tk::VERSION >= 804) {
21909	    # XXX using -topmost seems to be mandatory, but is ugly,
21910	    # because the window is also topmost to other apps
21911	    $win->attributes(-toolwindow => 1, -topmost => 1);
21912        } else {
21913	    $parent = $top if !$parent;
21914	    $win->transient($parent);
21915        }
21916    }
21917}
21918
21919sub get_image {
21920    my($base, $file) = @_;
21921
21922    my $images = ($top->{'MapImages'} ||= {});
21923    my $p = $images->{$base};
21924    if (!$p) {
21925	my $try_file = try_image_suffix($file);
21926	if (defined $try_file) {
21927	    $file = $try_file;
21928	}
21929	eval {
21930	    if ($file =~ /\.png$/ && !exists $INC{"Tk/PNG.pm"}) {
21931		require Tk::PNG;
21932	    }
21933	    if ($file =~ /\.jpe?g$/ && !exists $INC{"Tk/JPEG.pm"} && !exists $INC{"Tk/JPEG/Lite.pm"}) {
21934		require Tk::JPEG; # fallback to Tk::JPEG::Lite? XXX
21935	    }
21936
21937	    #warn "Try $file...\n";
21938	    $p = $c->Photo(-file => $file);
21939	};
21940	if (!$p) {
21941	    eval {
21942		my $try_file = try_image_suffix("$FindBin::RealBin/images/$file");
21943		if (defined $try_file) {
21944		    $file = $try_file;
21945		} else {
21946		    warn "Could not find $file in images, try in \@INC..."; # XXX should never happen?
21947		    $file = Tk::findINC($file);
21948		}
21949		#warn "Try $file...\n";
21950		$p = $c->Photo(-file => $file)
21951		    if defined $file;
21952	    };
21953	}
21954	if ($p) {
21955	    $images->{$base} = $p;
21956	}
21957    }
21958    $p;
21959}
21960
21961sub get_image_for_p {
21962    my($base, $file, $abk) = @_;
21963    get_image_for_any($base, $file, $abk, 'p');
21964}
21965
21966sub get_image_for_str {
21967    my($base, $file, $abk) = @_;
21968    get_image_for_any($base, $file, $abk, 'str');
21969}
21970
21971sub get_image_for_any {
21972    my($base, $file, $abk, $type) = @_;
21973
21974    my($realfile,$w,$h,$refscale,$doxxx);
21975    my $is_svg;
21976    if ($file =~ m{(.*\.svg)(?::(\d+)x(\d+)(?:=1:(\d+)(,xxx)?)?)?$}) {
21977	($realfile,$w,$h,$refscale,$doxxx) = ($1,$2,$3,$4,$5);
21978	$is_svg = 1;
21979    } else {
21980	$realfile = $file;
21981    }
21982    my $images = ($top->{'MapImages'} ||= {});
21983    my $key = $base.' '.$realfile.' '.(defined $w ? $w.'x'.$h.' ' : '').(defined $refscale ? "$mapscale " : '').$type.' '.$abk;
21984    my $p = $images->{$key};
21985    if (!$p) {
21986	eval {
21987	    my $abs_realfile;
21988	    if ($realfile =~ m{^/}) {
21989		$abs_realfile = $realfile;
21990	    } else {
21991		# XXX Es ist nicht zugesichert, dass eine Datei f�r ein
21992		# p/str-Objekt existiert. Somit kann $p/str_file{$abk}
21993		# leer sein und der dirname-Aufruf meckern (fileparse()
21994		# need a valid pathname)
21995		my $bbd_abspath = $type eq 'p' ? $p_file{$abk} : $str_file{$abk};
21996		if ($bbd_abspath !~ m{^/}) { # XXX windows compat? Should check for all occurences of this pattern and replace by function!
21997		    $bbd_abspath = "$datadir/$bbd_abspath";
21998		}
21999		my $dir = dirname($bbd_abspath);
22000		$abs_realfile = "$dir/$realfile";
22001	    }
22002	    if ($is_svg) {
22003		# XXX move svg stuff to some general-purpose function or module
22004		if (!defined $w) {
22005		    ($w,$h) = (100,100); # some hardcoded default
22006		}
22007		if (defined $refscale) {
22008		    my($curr_mapscale) = $mapscale =~ m{^1:(\d+)}; # ignore decimals, if any
22009		    my $factor = $refscale/$curr_mapscale;
22010		    $factor = 0.5+$factor/2 if $doxxx; # XXX good name for xxx? make the factor "flatter"
22011		    $w *= $factor;
22012		    $h *= $factor;
22013		}
22014		$p = svg2photo($abs_realfile, $w, $h);
22015	    } else {
22016		$p = get_image($base, $realfile);
22017		if (!$p) {
22018		    warn "Try $abs_realfile...\n";
22019		    $p = $c->Photo(-file => $abs_realfile);
22020		}
22021	    }
22022	};
22023	if ($@) {
22024	    warn "Warning: $@ (supplid args: ($base, $file, $abk, $type)" if $@;
22025	    $p = $c->Photo(-file => "$FindBin::RealBin/images/px_1t.gif"); # XXX cache this one!
22026	}
22027	if ($p) {
22028	    $images->{$key} = $p;
22029	}
22030    }
22031    $p;
22032}
22033
22034sub svg2photo {
22035    my($file, $w, $h) = @_;
22036    warn "Try to convert from svg to png, geometry ${w}x${h}...\n" if $verbose;
22037    require File::Temp;
22038    require Tk::PNG;
22039    my(undef,$tmpfile) = File::Temp::tempfile(SUFFIX => ".png", UNLINK => 1)
22040	or die "Can't create temporary file: $!";
22041    my @cmd = ("convert", "-geometry", "${w}x${h}", $file, $tmpfile);
22042    system(@cmd) == 0
22043	or die "Error while converting: @cmd, status=$?";
22044    my $p = $c->Photo(-file => $tmpfile);
22045    unlink $tmpfile;
22046    $p;
22047}
22048
22049sub pp_color {
22050    if (ref $pp_color eq 'ARRAY') {
22051	$c->itemconfigure('ppkvp',
22052			  -fill => $pp_color->[0]);
22053## 2nd not yet used:
22054# 	$c->itemconfigure('ppcrs',
22055# 			  -fill => $pp_color->[1]);
22056	$c->itemconfigure('ppcrs',
22057			  -fill => $pp_color->[0]);
22058    } else {
22059	$c->itemconfigure('pp',
22060			  -fill => $pp_color);
22061    }
22062}
22063
22064# Very nice. Note that the Tk::CanvasBalloon::Track method cannot cope with
22065# dealing with stacked items, so the <Motion> binding in std_str_binding
22066# needs additional code to deal with this.
22067sub balloon_info_from_all_tags {
22068    my($c) = @_;
22069    my $e = $c->XEvent;
22070    my($xx, $yy) = ($c->canvasx($e->x), $c->canvasy($e->y));
22071    my $closeenough = $balloon_info_from_all_tags_closeenough;
22072    my(@items) = $c->find(overlapping =>
22073			  $xx-$closeenough, $yy-$closeenough,
22074			  $xx+$closeenough, $yy+$closeenough);
22075    # Now using "reverse", so top-most items are preferred
22076    @items = reverse @items;
22077    if (!@items) {
22078	push @items, "current";
22079    }
22080    my @major_balloon_info;
22081    my @balloon_info;
22082    my %balloon_info_seen;
22083    my $major_item_seen = 0;
22084    my $comments_rx = join("|", map { "comm-" . quotemeta }
22085			   grep { $_ ne "kfzverkehr" } # list types without meaningful "name" field XXX but maybe comm-kfzverkehr should have meaningful names some day...
22086			   @Strassen::Dataset::comments_types);
22087
22088    for my $item (@items) {
22089	my(@tags) = $c->gettags($item);
22090	if ($verbose && $verbose >= 2) {
22091	    require Data::Dumper;
22092	    print STDERR "Line " . __LINE__ . ", File: " . __FILE__ . "\n" . Data::Dumper->new([\@tags],[qw()])->Indent(1)->Useqq(1)->Dump;
22093	}
22094
22095	if ($tags[0] =~ m{^(s|sBAB|l|$comments_rx|qs|ql|hs|hl|fz|u|b|r|f|w|rw|e|v|v-fg|temp_sperre|temp_sperre_s|L\d+|L\d+-fg|L\d+-bg)$}) {
22096	    my $label = $tags[1];
22097	    if ($tags[0] eq 'rw' ||
22098		$tags[0] eq 'comm-cyclepath') { # Special handling for cyclepaths
22099		(my $rw_code) = $tags[2] =~ /^(?:rw|comm-cyclepath)-(RW(?:\d*|\?))/; # XXX should probably use $Radwege::rw_qr without the anchors?
22100		my $name = Radwege::code2name($rw_code);
22101		if (defined $name) {
22102		    if (defined $label && $label ne "") {
22103			$label = "$name ($label)";
22104		    } else {
22105			$label = $name;
22106		    }
22107		}
22108	    } elsif ($tags[0] eq 'temp_sperre') {
22109		$label = $tags[2];
22110	    }
22111	    next if $label =~ m{^\s*$};
22112	    $label =~ s/\|.*$//; # Teil hinter "|" abschneiden
22113	    if ($tags[0] =~ m{^(s|l)$}) { # most significant, should be top-most:
22114		if (!exists $balloon_info_seen{$label}) {
22115		    push @major_balloon_info, $label;
22116		    $balloon_info_seen{$label} = 1;
22117		}
22118		$major_item_seen++;
22119	    } else {
22120		if (($tags[2]||'') =~ m{^e-(CS|img)$}) { # comm-ferry
22121		    if ($label =~ m{^(?:.*)?:\s*(.*)}) {
22122			$label = $1;
22123		    }
22124		} elsif ($tags[0] =~ m{^(qs|ql|hs|hl)$}) {
22125		    if ($label =~ m{^(?:.*)?:\s*(.*)}) {
22126			$label = $1;
22127		    }
22128		    if (my($cat) = $tags[2] =~ m{-(.*)}) {
22129			if ($cat eq 'img') {
22130			    # not the category, but really an quality/handicap
22131			    # image, most probably an in-construction image
22132			    next;
22133			}
22134			$label .= " ($cat)";
22135		    }
22136		} elsif ($tags[0] =~ m{^L\d+-fg$}) {
22137		    $label = $tags[2];
22138		}
22139		if ($major_item_seen && $tags[0] =~ m{^(f|w)$}) {
22140		    next;
22141		}
22142		if (!exists $balloon_info_seen{$label}) {
22143		    push @balloon_info, $label;
22144		    $balloon_info_seen{$label} = 1;
22145		}
22146	    }
22147	}
22148    }
22149
22150    @balloon_info = (@major_balloon_info, @balloon_info);
22151
22152    if ($verbose && $verbose >= 2) {
22153	require Data::Dumper;
22154	print STDERR "Line " . __LINE__ . ", File: " . __FILE__ . "\n" . Data::Dumper->new([\@balloon_info],[qw(balloon_info)])->Indent(1)->Useqq(1)->Dump;
22155    }
22156
22157    if (wantarray) {
22158	if (!@balloon_info) {
22159	    ();
22160	} else {
22161	    @balloon_info;
22162	}
22163    } else {
22164	if (!@balloon_info) {
22165	    undef;
22166	} else {
22167	    join("\n", @balloon_info);
22168	}
22169    }
22170}
22171
22172sub get_special_vehicle { !defined $special_vehicle_rb || $special_vehicle_rb eq 'normal' ? '' : $special_vehicle_rb }
22173
22174# Currently works only if the original background is white
22175sub soft_flash {
22176    my $w = shift;
22177    # XXX Could be a little bit smoother...
22178    my @color_states = ('#ffe126', '#ffdb00', '#ffe126', '#ffe960', '#ffec74', '#fff19a', '#fff6be', '#ffffff');
22179    my $color_i = 0;
22180    my $next_color_state;
22181    $next_color_state = sub {
22182	return if !Tk::Exists($w);
22183	$w->configure(-background => $color_states[$color_i]);
22184	$color_i++;
22185	if ($color_i <= $#color_states) {
22186	    $w->after(100, $next_color_state);
22187	}
22188    };
22189    $next_color_state->();
22190}
22191
22192sub _can_send_mail {
22193    return $BBBikeMail::can_send_mail if defined $BBBikeMail::can_send_mail;
22194
22195    require BBBikeMail;
22196    BBBikeMail::capabilities();
22197    $BBBikeMail::can_send_mail;
22198}
22199
22200# REPO BEGIN
22201# REPO NAME tk_sleep /home/e/eserte/work/srezic-repository
22202# REPO MD5 6e344458a3a154eefaf7b82d5f9bb576
22203
22204=head2 tk_sleep
22205
22206=for category Tk
22207
22208    $top->tk_sleep($s);
22209
22210Sleep $s seconds (fractions are allowed). Use this method in Tk
22211programs rather than the blocking sleep function. The difference to
22212$top->after($s/1000) is that refrsh events are still handled in the
22213sleeping time.
22214
22215=cut
22216
22217sub Tk::Widget::tk_sleep {
22218    my($top, $s) = @_;
22219    my $sleep_dummy = 0;
22220    $top->after($s*1000,
22221                sub { $sleep_dummy++ });
22222    $top->waitVariable(\$sleep_dummy)
22223	unless $sleep_dummy;
22224}
22225# REPO END
22226
22227## DEBUG_BEGIN
22228#BEGIN{mymstat("100% BEGIN");}
22229## DEBUG_END
22230
22231package bbbike; # HACK for autosplit
22232