1#!/usr/bin/perl -w
2use Tk;
3use Tk::JPEG;
4use Tk::DialogBox;
5use Tk::BrowseEntry;
6use Tk::FBox;
7use Tk::TextUndo;
8#use POSIX;
9use strict;
10use MP3::Tag;
11
12### TODO
13#
14# make all buttons alive
15#
16# view binary data (as hex dump)
17#
18# delete/add more than one frame at once
19#
20# better open function
21#
22# ...
23#
24# doing changes automatically to all selected files
25#       (like setting filename, applying Control-u to all filename,
26#        deleting all ID3v2 frames or whatsoever)
27
28
29
30#
31# Sorry, but I know that this is ugly code...
32#
33
34use vars qw/$tktag $VERSION %element %var/;
35
36$VERSION="0.15";
37
38$var{dir}= shift || "./"; #/mp3/unsorted";
39$var{v2specent}={TIT2=>{name=>"Song"}, TPE1=>{name=>"Artist"},
40		 TALB=>{name=>"Album"}};
41$var{filter}="";
42$var{fnformat} = "%a - %s.mp3";
43$var{autoinfo} = ["ID3v2","ID3v1","filename"];
44formatstr($var{fnformat});
45
46$tktag = MainWindow->new(); #main window
47create_window();
48
49#&load_filelist;
50    &scan_dir;
51    &filter_and_show;
52
53MainLoop;
54
55exit 0;
56
57####################################
58
59
60###### tk subs
61sub key_bindings {
62  $tktag->bind('<Control-U>'=>\&use_ucase);
63  $tktag->bind('<Control-u>'=>\&use_ucase_first);
64  $tktag->bind('<Control-l>'=>\&use_lcase);
65  $tktag->bind('<Control-space>'=>\&use_spaces);
66  $tktag->bind('<Control-S>'=>\&save);
67  $tktag->bind('<Control-f>'=>\&autosetfilename);
68
69  $tktag->bind('<Control-n>'=>
70	       sub {
71		 $element{filelist}->activate($element{filelist}
72					      ->index("active")+1);
73		 &select; });
74}
75
76sub create_window {
77  my ($l);
78
79  &key_bindings;
80
81  $element{leftFrame}  = $tktag->Frame();
82  $element{rightFrame} = $tktag->Frame();
83  $element{rightFrameMul} = $tktag->Frame();
84
85  # create menu
86  $element{menu} = create_menu($tktag);
87
88  # special fonts
89  $tktag->fontCreate('C_big', -family => 'courier', -weight => 'bold',
90		     -size => 18);
91
92  # create Filelist
93  $element{filelist} = $element{leftFrame}->Scrolled("Listbox", -width=>25,
94						     -selectmode=>"extended",
95						     -scrollbars=>"osre",
96						     -takefocus=>1,
97						     -exportselection=>0);
98  $element{filelist}->bind("<Double-1>" => \&select);
99  $element{filelist}->bind("<Key-Return>" => \&select);
100  $var{simple}=1;
101  $element{filelist}->bind("<1>" => \&check_multi);
102  $element{filelist}->bind("<Control-a>" => [$element{filelist},
103					     'selectionSet',"0","end"]);
104  my $filter=$element{leftFrame}->Frame();
105  my $cbline=$filter->Frame();
106  my $illine=$filter->Frame();
107  $cbline->Checkbutton(-text     => 'ID3v1',
108		       -variable => \$var{v1filter},
109		       -command  => \&filter_and_show,
110		       -relief   => 'flat')->pack(-side=>"left");
111  $cbline->Checkbutton(-text     => 'ID3v2',
112		       -variable => \$var{v2filter},
113		       -command  => \&filter_and_show,
114		       -relief   => 'flat')->pack(-side=>"left");
115  $illine->Checkbutton(-text     => 'Inverse',
116		       -variable => \$var{filter_inv},
117		       -command  => \&filter_and_show,
118		       -relief   => 'flat')->pack(-side=>"left");
119  $illine->Label(-textvariable=>\$var{visiblefiles}, -relief=>"sunken",
120		 width=>5)->pack(-side=>"left", -anchor=>"e");
121  $cbline->pack(-side=>"top");
122  $l=$filter->Entry(-textvariable=>\$var{filter}, -width=>25)->pack(-side=>"top");
123  $l->bind("<FocusOut>" => \&filter_and_show);
124  $l->bind("<KeyPress-Return>" => \&filter_and_show);
125  $illine->pack(-side=>"top");
126  $element{rightFrameMul}->Label(-text=>"Options for automatic processing ".
127				 "of serveral files")
128    ->pack(-side=>"top", -expand=>1, -fill=>"x");
129  # create filename-area
130  my $fn1area = $element{rightFrame}->Frame(Name=>"fnframe");
131  my $fn2area = $element{rightFrame}->Frame();
132  $element{filenamelabel} = $fn1area->Label(-text=>"Filename:")
133    ->pack(-side=>"left", anchor=>"w");
134  $var{bgcolor}=$element{filenamelabel}->cget("-background");
135  $l=$fn1area->Entry(-textvariable=>\$var{filename}, -validate=>"key",
136		 -vcmd=>val_fn($element{filenamelabel}))
137    ->pack(-side=>"left", -fill=>"x", -expand=>"yes", -anchor=>"w");
138
139  $fn1area->Button(-text=>"Save changes", -command=>\&save)
140    ->pack(-side=>"left", -anchor=>"w");
141  $element{setfilename} = $fn2area->Menubutton(qw/-underline 0 -relief raised/,
142					       -text => "Set Filename  --",
143					       -direction => "below");
144  $element{setfilename}->configure(-menu => $element{setfilename}->menu);
145  $element{setfilename}->command(-label => "from ID3v1 Tag",
146				 -command=>[\&setfilename, "ID3v1"]);
147  $element{setfilename}->command(-label => "from ID3v2 Tag",
148				 -command=>[\&setfilename, "ID3v2"]);
149  $element{setfilename}->pack(-side=>"left", anchor=>"n", -padx=>15);
150
151  $element{setid3v1} = $fn2area->Menubutton(qw/-underline 0 -relief raised/,
152					       -text => "Set ID3v1  --",
153					    -direction => "below");
154  $element{setid3v1}->configure(-menu => $element{setid3v1}->menu);
155  $element{setid3v1}->command(-label => "from Filename",
156			      -command=>[\&copyfntov1]);
157  $element{setid3v1}->command(-label => "from ID3v2 Tag",
158			      -command=>[\&copyv2tov1]);
159  $element{setid3v1}->pack(-side=>"left", anchor=>"n", -padx=>15);
160
161  $element{setid3v2} = $fn2area->Menubutton(qw/-underline 0 -relief raised/,
162					       -text => "Set ID3v2  --",
163					    -direction => "below");
164  $element{setid3v2}->configure(-menu => $element{setid3v2}->menu);
165  $element{setid3v2}->command(-label => "from Filename",
166			      -command=>[\&copyfntov2]);
167  $element{setid3v2}->command(-label => "from ID3v1 Tag",
168			      -command=>\&copyv1tov2);
169  $element{setid3v2}->pack(-side=>"left", anchor=>"n", -padx=>15);
170
171  $element{removebutton} = $fn2area->Menubutton(qw/-underline 0 -relief raised/,
172					       -text => "Remove  --",
173						-direction => "below");
174  $element{removebutton}->configure(-menu => $element{removebutton}->menu);
175  $element{removebutton}->command(-label => "File",
176				  -command=>sub{
177				    $var{filename}="";
178				    $element{filenamelabel}
179				      ->configure(-background=>"red");});
180  $element{removebutton}->command(-label => "ID3v1 Tag",
181				  -command=>sub{
182				    if (exists $var{v1}) {
183				      &remove_id3v1;
184				      $element{v1caption}
185					->configure(-background=>"red");}});
186  $element{removebutton}->command(-label => "ID3v2 Tag",
187				  -command=>sub{
188				    if (exists $var{v2}) {
189				      &remove_id3v2;
190				      $element{v2caption}
191					->configure(-background=>"red");}});
192  $element{removebutton}->pack(-side=>"left", anchor=>"n", -padx=>15);
193
194  # create ID3v1 area
195  my $v1area = $element{rightFrame}->Frame(Name=>"v1frame");
196  my $v1caption = $v1area->Frame(-background=>"red");
197  my $v1labels = $v1area->Frame();
198  my $v1entries = $v1area->Frame();
199  my $v2apic = $v1area->Frame();
200  my $a=0;
201  $element{v1caption}=$v1caption->Label(-text=>"ID3v1.1", -font=>"C_big",
202					-background=>"red", -relief=>"ridge")
203    ->pack(-side=>"top", -anchor=>"c", -expand=>"yes", -fill=>"x");
204  $l = $v1labels->Label(-text=>"Song:")->pack(-side=>"top", -anchor=>"e");
205  $v1entries->Entry(-textvariable=>\$var{v1song}, -width=>30,
206		    -validate=>"key", -vcmd=>val_text($l,30))
207    ->pack(-side=>"top", -anchor=>"w");
208  $l = $v1labels->Label(-text=>"Artist:")->pack(-side=>"top", -anchor=>"e",
209						-pady=>2);
210  $v1entries->Entry(-textvariable=>\$var{v1artist}, -width=>30,
211		    -validate=>"key", -vcmd=>&val_text($l,30))
212    ->pack(-side=>"top", -anchor=>"w");
213  $l = $v1labels->Label(-text=>"Album:")->pack(-side=>"top", -anchor=>"e");
214  $v1entries->Entry(-textvariable=>\$var{v1album}, -width=>30,
215		    -validate=>"key", -vcmd=>&val_text($l,30))
216    ->pack(-side=>"top", -anchor=>"w");
217  $l = $v1labels->Label(-text=>"Comment:")->pack(-side=>"top", -anchor=>"e",
218						 -pady=>1);
219  $v1entries->Entry(-textvariable=>\$var{v1comment}, -width=>30,
220		    -validate=>"key", -vcmd=>val_text($l,28))
221    ->pack(-side=>"top", -anchor=>"w");
222  $element{genrelabel} = $v1labels->Label(-text=>"Genre:")->pack(-side=>"top",
223								 -anchor=>"e");
224  my $genres = ["", sort @{MP3::Tag::genres()}];
225  $v1entries->BrowseEntry(-variable => \$var{v1genre}, -choices => $genres,
226			  -state=>"readonly", -browsecmd=>
227			  sub { $element{genrelabel}
228				  ->configure(-background=>"yellow");
229				$element{v1caption}
230				  ->configure(-background=>"yellow");
231				push @{$var{labels}}, $element{genrelabel} })
232    ->pack(-side=>"top", -anchor=>"w");
233  $l = $v1labels->Label(-text=>"Year:")->pack(-side=>"top", -anchor=>"e");
234  $v1entries->Entry(-textvariable=>\$var{v1year}, -width=>4,
235		    -validate=>"key", -vcmd=>val_num($l,4))
236    ->pack(-side=>"top", -anchor=>"w");
237  $l = $v1labels->Label(-text=>"Track:")->pack(-side=>"top", -anchor=>"e",
238					       -pady=>2);
239  $v1entries->Entry(-textvariable=>\$var{v1track}, -width=>4,
240		    -validate=>"key", -vcmd=>val_num($l,3))
241    ->pack(-side=>"top", -anchor=>"w");
242  $element{apic} = $v2apic->Photo('apic');
243  $element{apic}->blank;
244  $v2apic->Label(-image=>'apic', -height=>150, -width=>180)
245    ->pack(-side=>"top", -anchor=>"center");
246  $element{apictext}=$v2apic->Label(-text=>"")->pack(-side=>"top",
247						     -anchor=>"center");
248  $v1caption->pack(-side=>"top", -expand=>"yes", -fill=>"x");
249  $v1labels->pack(-side=>"left");
250  $v1entries->pack(-side=>"left");
251  $v2apic->pack(-side=>"right");
252
253  # ID3v2 area
254  my $v2area = $element{rightFrame}->Frame(Name=>"v2frame");
255  my $v2caption = $v2area->Frame();
256  my $v2top = $v2area->Frame();
257  my $v2right = $v2area->Frame();
258  my $v2buttons = $v2top->Frame();
259  my $v2labent = $v2top->Frame();
260  my $v2labels = $v2labent->Frame();
261  my $v2entries = $v2labent->Frame();
262  my $v2parea = $v2right->Frame();
263  my $v2iarea = $v2right->Frame();
264  $element{v2caption}=$v2caption->Label(-text=>"ID3v2.3", -font=>"C_big",
265					-background=>"red", -relief=>"ridge")
266    ->pack(-side=>"top", -anchor=>"c", -fill=>"x", -expand=>"yes");
267  while (my ($fname,$val) = each %{$var{v2specent}}) {
268    $val->{label} = $v2labels->Label(-text=>$val->{name})->pack(-side=>"top",
269								-anchor=>"e");
270    $val->{entry} = $v2entries->Entry(-textvariable=>\$var{"v2$fname"},
271				      -width=>40, -validate=>"key",
272				      -vcmd=>[\&val_v2,$element{v2caption},
273					      $val->{label}, $a])
274      ->pack(-side=>"top", -anchor=>"w");
275  }
276  $v2buttons->Button(-text=>"Add Frame", -command=>\&add_frame)
277    ->pack(-side=>"left", -anchor=>"c");
278  $v2buttons->Button(-text=>"Delete Frame(s)", -command=>\&del_frame)
279    ->pack(-side=>"left", -anchor=>"c");
280  $element{frames} = $v2iarea->Scrolled("Listbox", -scrollbars=>"re", -width=>"8",
281					-selectmode=>"browse", -takefocus=>1)
282    ->pack(-side=>"left");
283  $element{frames}->bind("<Double-1>" => \&show_frame);
284  $element{frames}->bind("<Key-Return>" => \&show_frame);
285  $v2parea->Label(-textvariable=>\$var{longname})->pack(-side=>"top",
286							-anchor=>"n", -pady=>5);
287  $element{frameinfo} = $v2parea;
288  $v2labels->pack(-side=>"left");
289  $v2entries->pack(-side=>"left");
290  $v2labent->pack(-side=>"left", -anchor=>"nw");
291  $v2buttons->pack(-side=>"left", -fill=>"y", -expand=>"yes");
292  $v2iarea->pack(-side=>"left", -anchor=>"nw");
293  $v2parea->pack(-side=>"left", -fill=>"both", -expand=>"yes", -anchor=>"nw");
294  $v2caption->pack(-side=>"top", -expand=>"yes", -fill=>"x");
295  $v2top->pack(-side=>"top", -fill=>"x", -expand=>"yes", -anchor=>"n");
296  $v2right->pack(-side=>"left", -fill=>"both", -expand=>"yes", -anchor=>"n");
297
298  # pack left Frame
299  $element{filelist}->pack(-side=>"top", -fill=>"y", -expand=>"yes");
300  $filter->pack(-side=>"bottom");
301
302  # pack right Frame
303  $fn1area->pack(-side=>"top", -anchor=>"nw", -expand=>"yes", -fill=>"x");
304  $fn2area->pack(-side=>"top", -anchor=>"nw", -expand=>"yes", -fill=>"x");
305  $v1area->pack(-side=>"top", -anchor=>"nw", -expand=>"yes", -fill=>"x",
306		-pady=>10);
307  $v2area->pack(-side=>"top", -anchor=>"nw", -expand=>"yes", -fill=>"x");
308  $v2parea->pack(-side=>"top", -anchor=>"nw", -expand=>"yes", -fill=>"x");
309
310  # pack main window
311  $element{menu}->pack(-side=>"top", -fill=>"x", -anchor=>"nw");
312  $element{leftFrame}->pack(-side=>"left", -fill=>"y", -anchor=>"w");
313  $element{rightFrame}->pack(-side=>"left",-fill=>"both",-expand=>"yes",
314			     -anchor=>"nw");
315
316  #prepare open dialog window
317  $element{open} = $tktag->DialogBox(-title=>"Open directory",
318				     -buttons=>["Open", "Cancel"]);
319  $element{open}->add("Label", -text=>"Directory:")->pack();
320  $element{open}->add("Entry", -width=>35, -textvariable=>\$var{dir})->pack();
321
322  $element{fnformat} = $tktag->DialogBox(-title=>"Set filename format",
323					 -buttons=>["Set", "Cancel"]);
324  $element{fnformat}->add("Label", -text=>"Format")->pack();
325  $element{fnformat}->add("Entry", -width=>35, -textvariable=>\$var{fnformat})
326    ->pack();
327  $element{fnformat}->add("Label",
328			  -text=>"%s - Song       %l - Album\n%a - Artist".
329			         "     %t - Track\n %c - Comment    ".
330			         "%g - Genre\n %y - Year".
331			         "\nSee also README file")->pack();
332
333  $element{addelframe} = $tktag->DialogBox(-buttons=>["Ok", "Cancel"]);
334  $element{addelabel} = $element{addelframe}->add("Label")
335    ->pack();
336  $element{addelistbox} = $element{addelframe}->add("Scrolled", "Listbox",
337						 -scrollbars=>"osre", -width=>50)
338    ->pack(-fill=>"both", -expand=>1);
339
340}
341
342
343sub val_v2 {
344  my ($label1, $label2, $change) = @_;
345  if ($_[7] == -1) {
346    $change=0;
347    return 1;
348  }
349  return 1 if $change++;
350  $label1->configure(-background=>"yellow");
351  if (defined $label2) {
352    $label2->configure(-background=>"yellow");
353    push @{$var{labels}}, $label2;
354  }
355  return 1;
356}
357
358sub val_fn {
359  my ($label1, $change) = @_;
360  return
361    sub {
362      if ($_[4] == -1) {
363	$change=0;
364	return 1;
365      }
366      return 1 if $change++;
367      $label1->configure(-background=>"yellow");
368      return 1;
369    }
370}
371
372
373sub val_text {
374  my ($label, $length, $change) = @_;
375  return
376    sub {
377      my ($pro, undef, undef, undef, $type) = @_;
378      return 0 if $type == 1 && length($pro) > $length;
379      if ($type == -1) {
380	$change=0;
381	return 1;
382      }
383      return 1 if $change++;
384      $label->configure(-background=>"yellow");
385      $element{v1caption}->configure(-background=>"yellow");
386      push @{$var{labels}}, $label;
387      return 1;
388    }
389}
390
391sub val_num {
392  my ($label, $length, $change) = @_;
393  return
394    sub {
395      my ($pro, undef, undef, undef, $type) = @_;
396      return 0 if $type == 1 && !($pro =~ /^\d{0,$length}$/o);
397      if ($type == -1) {
398	$change=0;
399	return 1;
400      }
401      return 1 if $change++;
402      $label->configure(-background=>"yellow");
403      $element{v1caption}->configure(-background=>"yellow");
404      push @{$var{labels}}, $label;
405      return 1;
406    }
407}
408
409sub create_menu {
410  my $menu = shift->Menu(-type=>"menubar");
411
412  my $file=$menu->cascade(-label=>"File");
413  $file->command(-label=>"Open", -command=>\&load_filelist);
414  $file->separator();
415  $file->command(-label=>"Exit", -command=>sub {exit 0;});
416
417  my $opts=$menu->cascade(-label=>"Options");
418  $opts->command(-label=>"Filename format", -command=>\&filename_format);
419
420  return $menu;
421}
422
423sub tk_question {
424  my $text = shift;
425  my $ans = $tktag->messageBox(-text=>$text, -title=>"Question", -type=>"YesNo",
426			       -default=>"No", -icon=>"question" );
427  return $ans eq "Yes" ? 1 : 0;
428}
429
430sub tk_warning {
431  my $text = shift;
432  $tktag->messageBox(-text=>$text, -title=>"Warning", -buttons=>"Ok",
433		     -icon=>"warning" );
434}
435
436####### file subs
437
438sub load_filelist {
439  $element{open}->configure(-title=>"Open directory");
440  my $answer = $element{open}->Show;
441  if ($answer eq "Open") {
442    $var{dir} =~ s!/$!!;
443    $var{dir} .= "/";
444    &scan_dir;
445    &filter_and_show;
446  }
447}
448
449sub scan_dir {
450  delete $var{files};
451  opendir(DIR, $var{dir}) or return;
452  $tktag->Busy;
453  $tktag->update;
454  my $file;
455  while (defined($file = readdir(DIR))) {
456    if (-d $var{dir} . $file) {
457      $var{files}->{$file}={dir=>$var{dir}, isDir=>1};
458    } elsif (-f _) {
459      my $mp3=MP3::Tag->new($var{dir}.$file);
460      $var{files}->{$file}={dir=>$var{dir}};
461      foreach ($mp3->getTags) {
462	$var{files}->{$file}->{$_}=1;
463      }
464    }
465  }
466  closedir DIR;
467  $tktag->Unbusy;
468}
469
470sub filter_and_show {
471  my (@files, @dirs, $name, $val, $filter);
472  $element{filelist}->delete("0", "end");
473  if ($var{filter} ne "") {
474    $filter = qr/$var{filter}/i;
475  }
476  while (($name, $val)=each %{$var{files}}) {
477    if (exists $val->{isDir}) {
478      push @dirs, "[$name]";
479    } else {
480      if (!$var{v1filter} || (!$var{filter_inv} && $val->{ID3v1})
481	  || ($var{filter_inv} && !$val->{ID3v1})) {
482	if (!$var{v2filter} || (!$var{filter_inv} && $val->{ID3v2})
483	    || ($var{filter_inv} && !$val->{ID3v2})) {
484	  if (!$filter || (!$var{filter_inv} && $name =~ $filter)
485	      || ($var{filter_inv} && !($name =~ $filter))) {
486	    push @files, $name;
487	  }
488	}
489      }
490    }
491  }
492  $element{filelist}->insert("end", sort @dirs);
493  $element{filelist}->insert("end", sort @files);
494  $var{visiblefiles}=$#files+1;
495}
496
497
498sub select {
499  my $active =$element{filelist}->get("active","active");
500  if ($active =~ /^\[(.*)\]$/) {
501    if (exists $var{files}->{$1}->{isDir}) {
502      $var{dir}.="$1/";
503      &scan_dir;
504      &filter_and_show;
505      return;
506    }
507  }
508  $var{filename}=$active;
509  $var{oldfilename} = $var{filename};
510  my $filename = $var{dir}.$var{filename};
511  $var{mp3}->close if exists $var{mp3};
512  delete $var{v1};
513  delete $var{v2};
514  $element{filenamelabel}->configure(-background=>$var{bgcolor});
515  $var{mp3} = MP3::Tag->new($filename);
516  $var{mp3}->getTags;
517
518  # remove old things
519  foreach (@{$var{fpack}}) {
520    $_->packForget();
521  }
522  $var{fpack} = [];
523  $var{longname}="";
524  if (exists $var{labels}) {
525    while (my $label = shift @{$var{labels}}) {
526      $label->configure(-background=>$var{bgcolor});
527    }
528  }
529
530  # set information of ID3v1 tag
531  if (exists $var{mp3}->{ID3v1}) {
532    $element{v1caption}->configure(-background=>"green");
533    $var{v1}=$var{mp3}->{ID3v1};
534    ($var{v1song},$var{v1artist},$var{v1album},$var{v1year},$var{v1comment},$var{v1track},$var{v1genre})
535      = $var{v1}->all();
536  } else {
537    ($var{v1song},$var{v1artist},$var{v1album},$var{v1year},$var{v1comment},$var{v1track},$var{v1genre})
538      = ("","","","","","","");
539    $element{v1caption}->configure(-background=>"red");
540  }
541
542  #set information of ID3v2 tag
543  if (exists $var{mp3}->{ID3v2}) {
544    $element{v2caption}->configure(-background=>"green");
545    $var{v2}=$var{mp3}->{ID3v2};
546    my $frames = $var{v2}->getFrameIDs;
547    while (my ($fname, $val) = each %{$var{v2specent}}) {
548      if (exists $frames->{$fname}) {
549	$var{"v2$fname"} = $var{v2}->getFrame($fname);
550	$val->{entry}->bind('<FocusOut>' => v2_specent_change($fname));
551      } else {
552	$var{"v2$fname"} = "";
553	$val->{entry}->bind('<FocusOut>' => v2_specent_create($fname));
554      }
555    }
556    if (exists $frames->{"APIC"}) {
557      show_picture("APIC");
558    } else {
559      $element{apic}->blank;
560      $element{apictext}->configure(-text=>"");
561    }
562    my @frames = sort keys %$frames;
563    $element{frames}->delete("0","end");
564    $element{frames}->insert("end", @frames);
565    show_frame($frames[0],1);
566  } else {
567    while (my ($fname, $val) = each %{$var{v2specent}}) {
568      $var{"v2$fname"}="";
569      $val->{entry}->bind('<FocusOut>' => v2_specent_create($fname));
570    }
571    $element{v2caption}->configure(-background=>"red");
572    $element{frames}->delete("0","end");
573    $element{apic}->blank;
574    $element{apictext}->configure(-text=>"");
575  }
576}
577
578sub v2_specent_change {
579  my $fname = shift;
580  return sub {
581    $var{v2}->change_frame($fname, $var{"v2$fname"});
582    if (exists $var{current_frame} && $var{current_frame} eq $fname) {
583      $var{frame_Text}->delete("0.1", "end");
584      $var{frame_Text}->insert("0.1", $var{"v2$fname"});
585    }
586  };
587}
588
589sub v2_specent_create {
590  my $fname = shift;
591  return sub {
592    return if $var{"v2$fname"} eq "";
593    $var{v2}=$var{mp3}->newTag("ID3v2") unless exists $var{v2};
594    $var{v2}->add_frame($fname, $var{"v2$fname"});
595    my @allframes = $element{frames}->get(0,"end");
596    push @allframes, $fname;
597    $element{frames}->delete(0,"end");
598    $element{frames}->insert("end", sort @allframes);
599    $var{v2specent}->{$fname}->{entry}->bind('<FocusOut>' =>
600					     v2_specent_change($fname));
601  };
602}
603
604sub show_frame {
605  my ($fname, $tagchanged) = @_;
606  if (ref $fname) { # called from listbox-bind
607    $fname = $fname->get("active");
608  }
609  save_frameinfo() unless defined $tagchanged;
610  # delete last info
611  foreach (@{$var{fpack}}) {
612    $_->packForget();
613  }
614  $var{fpack} = [];
615  $var{current_frame}="";
616  return unless length($fname) == 4 || length($fname) == 6;
617  my $info;
618  $var{current_frame} = $fname;
619  ($info, $var{longname}) = $var{v2}->getFrame($fname);
620  (undef, $var{restrinp}) = $var{v2}->what_data($fname);
621  if (ref $info) {
622    my ($key, $value);
623    foreach $key (sort keys %$info) {
624      $value = $info->{$key};
625      my $f = $element{frameinfo}->Frame();
626      push @{$var{fpack}}, $f;
627      if ($key =~ s/^_//) {
628	my $l=$f->Label(-text=>"$key:", -justify=>"left")
629	  ->pack(-side=>"left", -anchor=>"w");
630	my $b=$f->Menubutton(qw/-underline 0 -relief raised/,
631			     -text => "Data of " .length($value) .
632			     " Bytes  --", -direction => "below");
633	$b->configure(-menu => $b->menu);
634	$var{"data_$key"}=$value;
635	$b->command(-label => "Save Data", -command=>[\&save_data,$key]);
636	$b->command(-label => "Load Data", -command=>[\&load_data,$key,$b, $l]);
637	$b->command(-label => "View Data", -command=>[\&view_data,$key]);
638	$b->pack(-side=>"left", anchor=>"n");
639      } else {
640	my $l=$f->Label(-text=>"$key:", -justify=>"left")
641	  ->pack(-side=>"left", -anchor=>"w");
642	if (exists $var{restrinp}->{$key}) {
643	  my $state="readonly";
644	  if (exists $var{restrinp}->{$key}->{"_FREE"}) {
645	    $state="normal";
646	    delete $var{restrinp}->{$key}->{"_FREE"};
647	  }
648	  $var{"frame_$key"}=$value;
649	  my @inps=sort keys %{$var{restrinp}->{$key}};
650	  $f->BrowseEntry(-variable => \$var{"frame_$key"}, -choices => \@inps,
651			  -state=>$state, -width=>37, -browsecmd=>
652			  sub { $l->configure(-background=>"yellow");
653				$element{v2caption}
654				  ->configure(-background=>"yellow");})
655	    ->pack(-side=>"right", -anchor=>"e");
656
657	} else {
658	  $var{"frame_$key"} = $f->TextUndo(-height=>2, -width=>40, -wrap=>"word")
659	    ->pack(-side=>"right", -anchor=>"e");
660	  $var{"frame_$key"}->insert("0.0", $value);
661	  $var{"frame_$key"}->bind('<Double-1>' =>
662				   sub{ $var{"frame_$key"}
663					  ->configure(-disable=>1);});
664	  push @{$var{fpack}}, $var{"frame_$key"};
665	  $var{"frame_$key"}->bind('<Key>'=>[\&yyy, $l]);
666	}
667      }
668      $f->pack(-side=>"top", -anchor=>"w", -fill=>"x", -expand=>"yes");
669    }
670  } else {
671    if (exists $var{restrinp}->{Text}) {
672      my $state="readonly";
673      if (exists $var{restrinp}->{Text}->{"_FREE"}) {
674	$state="normal";
675	delete $var{restrinp}->{Text}->{"_FREE"};
676      }
677      $var{"frame_Text"}=$info;
678      my @inps=sort keys %{$var{restrinp}->{Text}};
679      push @{$var{fpack}},$element{frameinfo}->BrowseEntry(
680				       -variable => \$var{"frame_Text"},
681				       -choices => \@inps, -width=>37,
682				       -state=>$state, -browsecmd=>
683				       sub { $element{v2caption}
684					       ->configure(-background=>"yellow")})
685	->pack(-side=>"top", -anchor=>"n");
686    } else {
687      $var{frame_Text} = $element{frameinfo}->TextUndo(-height=>2, -width=>40,
688						       -wrap=>"word")
689	->pack(-side=>"top", -anchor=>"n");
690      $var{frame_Text}->insert("0.0", $info);
691      push @{$var{fpack}}, $var{frame_Text};
692      if (exists $var{v2specent}->{$fname}) {
693	$var{frame_Text}->bind('<Key>'=>[\&xxx, $fname]);
694      }
695    }
696  }
697}
698
699sub save_frameinfo {
700  # save last info
701  return unless exists $var{current_frame};
702  my ($format, $resinp) = $var{v2}->what_data($var{current_frame});
703  my @data=();
704  foreach (@$format) {
705    if (/^_/) {
706      push @data, $var{"data$_"};
707      next;
708    }
709    my $d;
710    if ($var{"frame_$_"} =~ /Text/) {
711      $d= $var{"frame_$_"}->get("0.1","end");
712    } else {
713      $d= $var{"frame_$_"};
714      $d= $resinp->{$_}->{$d} if (defined $resinp->{$_}->{$d});
715    };
716    chomp $d;
717    $d =~ s/ +$//;
718    push @data, $d;
719  }
720  $var{v2}->change_frame($var{current_frame}, @data);
721}
722
723sub xxx {
724  my ($textobject, $fname) = @_;
725  my $text = $textobject->get("0.1","end");
726  chomp($text);
727  $var{"v2$fname"} = $text;
728  $var{v2specent}->{$fname}->{label}->configure(-background=>"yellow");
729  $element{v2caption}->configure(-background=>"yellow");
730}
731
732sub yyy {
733  $_[1]->configure(-background=>"yellow");
734  $element{v2caption}->configure(-background=>"yellow");
735}
736
737
738sub show_picture {
739  my $fname = shift;
740  my $frame = $var{v2}->getFrame($fname);
741  if ($frame->{"MIME type"} =~ /image\/(gif|jpe?g|bmp)/i) {
742    my $type = lc $1;
743    $type =~ s/jpg/jpeg/;
744    open(TMP, ">/tmp/_tk_temp.jpg");
745    binmode TMP;
746    print TMP $frame->{_Data};
747    close TMP;
748    $element{apic}->configure(-format=>$1, -file=>"/tmp/_tk_temp.jpg");
749    my $text .= $frame->{"Picture Type"};
750    $text = $frame->{Description} if $text eq "Other";
751    $text = "ID3v2.3-" . uc($fname) . ": ". $text;
752    $element{apictext}->configure(-text=>$text);
753    unlink "/tmp/_tk_temp.jpg";
754  }
755}
756
757sub save {
758  # save changes to filename
759  if ($var{filename} ne $var{oldfilename}) {
760    return 0 unless change_filename();
761  }
762  $element{filenamelabel}->configure(-background=>$var{bgcolor});
763  #save changes of ID3v1 tag
764  if ($element{v1caption}->cget("-background") eq "yellow") {
765    my @fields;
766    for (qw/v1song v1artist v1album v1year v1comment v1track v1genre/) {
767      push @fields, $var{$_};
768    }
769    $var{v1}=$var{mp3}->newTag("ID3v1") unless exists $var{v1};
770    $var{v1}->all(@fields);
771    $var{v1}->writeTag;
772    $var{v1genre}=$var{v1}->genre();
773    $element{v1caption}->configure(-background=>"green");
774  } elsif (exists $var{v1} && $element{v1caption}->cget("-background") eq "red") {
775    $var{v1}->removeTag;
776    delete $var{v1};
777  }
778
779  #save changes of ID3v2 tag
780  if ($element{v2caption}->cget("-background") eq "yellow") {
781    $var{v2}=$var{mp3}->newTag("ID3v2") unless exists $var{v2};
782    save_frameinfo();
783    while (my ($fname, $val)=each %{$var{v2specent}}) {
784      if ($var{"v2$fname"} ne "" &&
785	  $val->{label}->cget("-background") eq "yellow") {
786	my $d=$var{"v2$fname"};
787	$d=~s/ +$//;
788	if (defined $var{v2}->getFrame($fname)) {
789	  $var{v2}->change_frame($fname, $d);
790	} else {
791	  $var{v2}->add_frame($fname, $d);
792	}
793	$val->{label}->configure(-background=>$var{bgcolor});
794      }
795    }
796    $var{v2}->write_tag;
797    $element{v2caption}->configure(-background=>"green");
798    foreach my $v2e ( @{$var{fpack}}) {
799      foreach ($v2e->children) {
800	$_->configure(-background=>$var{bgcolor}) if /Label/;
801      }
802    }
803  } elsif (exists $var{v2} &&
804	   $element{v2caption}->cget("-background") eq "red") {
805    $var{v2}->remove_tag;
806    delete $var{v2};
807  }
808  if (exists $var{labels}) {
809    while (my $label = shift @{$var{labels}}) {
810      $label->configure(-background=>$var{bgcolor});
811    }
812  }
813}
814
815sub change_filename {
816  my $success = 0;
817  if ($var{filename} eq "") {
818    if (tk_question("Filename is empty. Do you want to ".
819		    "delete $var{oldfilename}?")) {
820      $var{mp3}->close;
821      if (unlink $var{dir}.$var{oldfilename}) {
822	remove_id3v1();
823	remove_id3v2();
824	$element{v1caption}->configure(-background=>"red");
825	$element{v2caption}->configure(-background=>"red");
826	$element{filelist}->delete("active","active");
827	delete $var{files}->{$var{oldfilename}};
828	$success = 1;
829      } else {
830	tk_warning("Cannot delete file.");
831      }
832    }
833  } elsif (-e $var{dir}.$var{filename}) {
834    if (-f _) {
835      if (tk_question("Files $var{filename} exists. Do you want".
836		      " to overwrite it?")) {
837	goto re;
838      }
839    } else {
840      tk_warning("$var{filename} exists and it isn't a plain file.".
841		 " Can't rename $var{oldfilename}!");
842    }
843  } else {
844  re:
845    if (rename($var{dir}.$var{oldfilename}, $var{dir}.$var{filename}) ||
846	(system("mv", $var{dir}.$var{oldfilename}, $var{dir}.$var{filename})==0)) {
847      $var{files}->{$var{filename}}=$var{files}->{$var{oldfilename}};
848      delete $var{files}->{$var{oldfilename}};
849      $var{oldfilename} = $var{filename};
850      my $index=$element{filelist}->index("active");
851      filter_and_show;
852      $element{filelist}->see($index);
853      $element{filelist}->activate($index);
854      $var{mp3}->{filename}=$var{dir}.$var{filename};
855      $success = 1;
856    } else {
857      tk_warning("Couldn't rename $var{oldfilename} to $var{filename}");
858    }
859  }
860  return $success;
861}
862
863sub remove_id3v1 {
864  for (qw/v1song v1artist v1album v1year v1comment v1track v1genre/) {
865    $var{$_} = "";
866  }
867}
868
869sub remove_id3v2 {
870  for (keys %{$var{v2specent}}) {
871    $var{"v2$_"} = "";
872  }
873  $element{frames}->delete("0","end");
874  foreach (@{$var{fpack}}) {
875    $_->packForget();
876  }
877  $var{fpack} = [];
878  $var{longname}="";
879}
880
881sub add_frame {
882  return unless exists $var{mp3};
883  $element{addelframe}->configure(-title=>"Add Frame");
884  $element{addelabel}->configure(-text=>"Select a frame to add:");
885  $element{addelistbox}->delete("0","end");
886  $var{v2}=$var{mp3}->newTag("ID3v2") unless exists $var{v2};
887  my $list = $var{v2}->supported_frames;
888  my @list = map {"$_ - $list->{$_}"} sort keys %$list;
889  $element{addelistbox}->insert("end",@list);
890  if ($element{addelframe}->Show() eq "Ok") {
891    $element{v2caption}->configure(-background=>"yellow");
892    my $fname= substr $element{addelistbox}->get("active","active"),0,4;
893    $fname = $var{v2}->add_frame($fname);
894    my $index=0;
895    my $maxindex = $element{frames}->index("end");
896    while ($index<$maxindex && $element{frames}->get($index,$index) lt $fname) {
897      $index++;
898    }
899    $element{frames}->insert($index, $fname);
900    $element{frames}->activate($index);
901    $element{frames}->selectionSet($index);
902    show_frame($fname);
903  }
904}
905
906sub del_frame {
907  return unless exists $var{v2};
908  $element{addelframe}->configure(-title=>"Delete Frame");
909  $element{addelabel}->configure(-text=>"Select a frame to delete:");
910  $element{addelistbox}->delete("0","end");
911  my $list = $var{v2}->getFrameIDs;
912  my @list = map {"$_ - $list->{$_}"} sort keys %$list;
913  $element{addelistbox}->insert("end",@list);
914  if ($element{addelframe}->Show() eq "Ok") {
915    $element{v2caption}->configure(-background=>"yellow");
916    my $fname= $element{addelistbox}->get("active","active");
917    $fname =~ s/^([A-Z0-9]+) .*/$1/;
918    $var{v2}->remove_frame($fname);
919    my $index=0;
920    my $maxindex = $element{frames}->index("end");
921    while ($index<$maxindex && $element{frames}->get($index,$index) ne $fname) {
922      $index++;
923    }
924    $element{frames}->delete($index,$index);
925    if ($var{current_frame} eq $fname) {
926      show_frame($element{frames}->get("active"),1);
927    }
928    if (exists $var{v2specent}->{$fname}) {
929      $var{"v2$fname"}="";
930      $var{v2specent}->{$fname}->{label}->configure(-background=>"yellow");
931    }
932  }
933}
934
935sub check_multi {
936  my ($h,$w,%info);
937  my @sel = $element{filelist}->curselection();
938  if ($#sel >0 && $var{simple}) {
939    $element{rightFrame}->packForget;
940    $element{rightFrameMul}->pack(-side=>"left",-fill=>"both",
941				  -expand=>"yes", -anchor=>"nw");
942    $var{simple}=0;
943  } elsif ($#sel == 0 && ! $var{simple}) {
944    $element{rightFrameMul}->packForget;
945    $element{rightFrame}->pack(-side=>"left",-fill=>"both",
946			       -expand=>"yes", -anchor=>"nw");
947    $var{simple}=1;
948  }
949}
950
951sub setfilename {
952  my $tag = shift; # may be 'ID3v1', 'ID3v2' or 'filename'
953  $tag =~ s/ID3//;
954  return undef unless exists $var{$tag} || $tag eq "filename";
955  my $new = $var{setfilename}->{stencil};
956  my $i=0;
957  foreach my $part (@{$var{setfilename}->{details}}) {
958    my $code=$part->{tag};
959    if ($tag eq "v2") {
960      $code =~ s/song/TIT2/ ||
961      $code =~ s/album/TALB/ ||
962      $code =~ s/artist/TPE1/;
963    }
964    my $txt="";
965    if ($tag eq "filename") {
966      if ($code eq "song" || $code eq "artist" ||
967	  $code eq "album" || $code eq "track") {
968	$txt = $var{mp3}->{filename}->$code($var{dir}.$var{filename});
969      }
970    } else {
971      $txt = $var{"$tag$code"};
972    }
973    $txt =~ s/ *$//;
974    $txt = substr $txt, 0, $part->{length} if exists $part->{length} &&
975      ((! exists $part->{fill}) || exists  $part->{precise} );
976    $txt = $part->{fill} x ($part->{length}-length($txt)) . $txt
977      if exists $part->{fill};
978    $new =~ s/%$i/$txt/;
979    $i++;
980  }
981  $new =~ s/ /_/g if exists $var{setfilename}->{nospaces};
982  $var{filename}=$new;
983  $element{filenamelabel}->configure(-background=>"yellow");
984  return 1;
985}
986
987sub formatstr {
988  my $format = shift;
989  my %tags = (s=>"song", a=>"artist", l=>"album", y=>"year",
990	      g=>"genre", t=>"track");
991  my @fmt;
992
993  while ($format =~ /%([0-9]*)(?:(!)?:(.))?([salygt])/g) {
994    my $t;
995    $t->{length}=$1 if defined $1 && $1 ne "";
996    $t->{precise}=1 if defined $2 && $2 ne "";
997    $t->{fill}=$3 if defined $3 && $3 ne "";
998    $t->{tag} = $tags{$4} if defined $4 && $4 ne "";
999    push @fmt, $t if defined $4 && $4 ne "";
1000  }
1001  my $i=0;
1002  $format =~ s/%([0-9]*)(?:(!)?:(.))?([salygt])/"%".$i++/eg;
1003  $var{setfilename}->{stencil}=$format;
1004  $var{setfilename}->{details}=\@fmt;
1005}
1006
1007sub element_change {
1008  my $element = shift;
1009
1010  if ($element->PathName =~/v1/) {
1011    $element{v1caption}->configure(-background=>"yellow");
1012  } elsif ($element->PathName =~/v2/) {
1013    $element{v2caption}->configure(-background=>"yellow");
1014  } elsif ($element->PathName =~/fn/) {
1015    $element{filenamelabel}->configure(-background=>"yellow");
1016  }
1017}
1018
1019sub use_ucase {
1020 my $element=shift;
1021 if ($element =~ /Entry/) {
1022   my $evar = $element->cget(-textvariable);
1023   my $ovar = $$evar;
1024   $$evar =~ tr/[a-z]/[A-Z]/;
1025   $$evar =~ s/\.MP3$/.mp3/;
1026   if ($ovar ne $$evar) {
1027     element_change($element);
1028   }
1029 } elsif ($element =~ /Text/) {
1030   my $text = $element->get("0.1","end");
1031   chomp $text;
1032   return if $text eq "";
1033   if ($text =~ tr/[a-z]/[A-Z]/) {
1034     element_change($element);
1035     $element->delete("0.1","end");
1036     $element->insert("end",$text);
1037   }
1038 }
1039}
1040
1041sub use_lcase {
1042 my $element=shift;
1043
1044 if ($element =~ /Entry/) {
1045   my $evar = $element->cget(-textvariable);
1046   return if $$evar eq "";
1047   if ($$evar =~ tr/[A-Z]/[a-z]/) {
1048     element_change($element);
1049   }
1050 } elsif ($element =~ /Text/) {
1051   my $text = $element->get("0.1","end");
1052   chomp $text;
1053   return if $text eq "";
1054   if ($text =~ tr/[A-Z]/[a-z]/) {
1055     element_change($element);
1056     $element->delete("0.1","end");
1057     $element->insert("end",$text);
1058   }
1059 }
1060}
1061
1062sub use_ucase_first {
1063 my $element=shift;
1064
1065 if ($element =~ /Entry/) {
1066   my $evar = $element->cget(-textvariable);
1067   return if $$evar eq "";
1068   my $ovar=$$evar;
1069   $$evar=ucase_first($$evar);
1070   if ($ovar ne $$evar) {
1071     element_change($element);
1072   }
1073 } elsif ($element =~ /Text/) {
1074   my $text = $element->get("0.1","end");
1075   chomp $text;
1076   return if $text eq "";
1077   my $otext = ucase_first($text);
1078   if ($otext ne $text) {
1079     $element->delete("0.1","end");
1080     $element->insert("end",$otext);
1081     element_change($element);
1082   }
1083 }
1084}
1085
1086sub ucase_first {
1087  my $text=shift;
1088  $text =~ s/(\w+)/\L\u$1/gm;
1089  $text =~ s/('[A-Z])/\L$1/g; #');
1090  $text =~ s/\.Mp3$/.mp3/;
1091  return $text;
1092}
1093
1094sub use_spaces {
1095 my $element=shift;
1096 if ($element =~ /Entry/) {
1097   my $evar = $element->cget(-textvariable);
1098   return if $$evar eq "";
1099   my $ovar=$$evar;
1100   $$evar=spaces($$evar);
1101   if ($ovar ne $$evar) {
1102     element_change($element);
1103   }
1104 }
1105}
1106
1107sub spaces {
1108  my $text=shift;
1109  $text =~ s/%20/_/g;
1110  if ((($text =~ tr/././) >2)) {
1111    $text =~ s/\./ /g;
1112    $text =~ s/ (mp3)$/.$1/i;
1113    $text =~ s/(\w)-(\w)/$1 - $2/g;
1114  } elsif ($text =~ /_/) {
1115    $text =~ s/_/ /g;
1116    $text =~ s/(\w)-(\w)/$1 - $2/g;
1117  } else {
1118    $text =~ s/ /_/g;
1119    $text =~ s/_-_/-/g;
1120  }
1121  $text=~ s/ +/ /g;
1122  return $text;
1123}
1124
1125
1126sub filename_format {
1127  if ( $element{fnformat}->Show eq "Set") {
1128    formatstr($var{fnformat});
1129  }
1130}
1131
1132sub view_data {
1133  my $key= shift;
1134  warn "Viewing of $key of $var{current_frame} not supported yet\n";
1135}
1136
1137sub load_data {
1138  my ($key, $button,$label)= @_;
1139  my @types=(["All files", "*"]);
1140  my $file = $tktag->getOpenFile(-filetypes => \@types);
1141  if (open (LOAD, $file)) {
1142    binmode LOAD;
1143    local $/;
1144    undef $/;
1145    $var{"data_$key"}=<LOAD>;
1146    close LOAD;
1147    warn "Please set mime type according to new data\n";
1148    $button->configure(-text => "Data of " .length($var{"data_$key"}) .
1149		       " Bytes  --");
1150    $label->configure(-background=>"yellow");
1151    $element{v2caption}->configure(-background=>"yellow");
1152  } else {
1153    warn "Couldn't open $file for saving $var{current_frame}.$key";
1154  }
1155}
1156
1157sub save_data {
1158  my $key= shift;
1159  my @types=(["All files", "*"]);
1160  my $file = $tktag->getSaveFile(-filetypes => \@types,
1161			     -initialfile => 'Untitled',
1162			     -defaultextension => '.dat');
1163  if (open (SAVE, ">$file")) {
1164    binmode SAVE;
1165    print SAVE $var{"data_$key"};
1166    close SAVE;
1167  } else {
1168    warn "Couldn't open $file for saving $var{current_frame}.$key";
1169  }
1170}
1171
1172sub copyv1tov2 {
1173  my %v1tov2 = (artist=>"TPE1", album=>"TALB", song=>"TIT2",
1174	        year=>"TYER", track=>"TRCK", comment=>"COMM", genre=>"TCON");
1175  return unless exists $var{v1};
1176  $var{v2}=$var{mp3}->newTag("ID3v2") unless exists $var{v2};
1177  my $frames = $var{v2}->getFrameIDs;
1178  while (my ($v1,$v2) = each %v1tov2) {
1179    next if $var{"v1$v1"} eq "";
1180    my ($data, $restr) = $var{v2}->what_data($v2);
1181    my @newdata;
1182    if ($#{$data}==0) {
1183      @newdata = ($var{"v1$v1"});
1184    } else {
1185      foreach (@$data) {
1186	if ($_ eq "Text") {
1187	  push @newdata, $var{"v1$v1"};
1188	} else {
1189	  push @newdata, "";
1190	}
1191      }
1192    }
1193    if (exists $frames->{$v2}) {
1194      $var{v2}->change_frame($v2, @newdata);
1195    } else {
1196      $var{v2}->add_frame($v2, @newdata);
1197      add2framelist($v2);
1198    }
1199    if (exists $var{v2specent}->{$v2}) {
1200      $var{"v2$v2"}=$var{"v1$v1"};
1201      $var{v2specent}->{$v2}->{label}->configure(-background=>"yellow");
1202    }
1203  }
1204  $element{v2caption}->configure(-background=>"yellow");
1205}
1206
1207sub copyv2tov1 {
1208}
1209
1210sub copyfntov1 {
1211  $var{v1}=$var{mp3}->newTag("ID3v1") unless exists $var{v1};
1212  foreach (qw/song artist album track/) {
1213    $var{"v1$_"}=$var{mp3}->{filename}->$_($var{dir}.$var{filename});
1214  }
1215}
1216
1217sub copyfntov2 {
1218}
1219
1220sub add2framelist {
1221  my $fname = shift;
1222  my $index=0;
1223  my $maxindex = $element{frames}->index("end");
1224  while ($index<$maxindex && $element{frames}->get($index,$index) lt $fname) {
1225    $index++;
1226  }
1227  $element{frames}->insert($index, $fname);
1228  $element{frames}->activate($index);
1229  $element{frames}->selectionSet($index);
1230}
1231
1232sub autosetfilename {
1233  foreach (@{$var{autoinfo}}) {
1234    last if setfilename($_);
1235  }
1236}
1237