can't read "::snackAmpSettings(ebg)": no such variable
while executing
"option add *ID3Editors*Entry.background $::snackAmpSettings(ebg) startupFile"
(in namespace eval "::request::id3Table" script line 2)
invoked from within
"namespace eval id3Table {
option add *ID3Editors*Entry.background $::snackAmpSettings(ebg) startupFile
option add *ID3Editors*Entry.foregro..."
(in namespace eval "::request" script line 32)
invoked from within
"namespace eval ::request $script"
("::try" body line 12)
OUTPUT BUFFER:
#------------------------------------------------------------------------------
# id3Table 1.0
#
# ID3 table editor windows for snackAmp Player in Tcl/Tk
# Uses id3Tag package
# Copyright (C) 2003 Tom Wilkason
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
#
# Please send any comments/bug reports to
# tom.wilkason@cox.net (Tom Wilkason)
#
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# Function : id3Table
# Description: Window to edit the proposed tag names based on file name data
# Author : Tom Wilkason
# Date : 2/8/2001
#------------------------------------------------------------------------------
namespace eval id3Table {
option add *ID3Editors*Entry.background $::snackAmpSettings(ebg) startupFile
option add *ID3Editors*Entry.foreground $::snackAmpSettings(efg) startupFile
option add *ID3Editors*Label.font entry interactive ;# high priority
}
interp alias {} slen {} string length
proc id3Table::Edit {fnames {smart 0}} {
package require id3Editor
package require BWidget
set i 0
set tr 0
if {[llength $fnames] == 0} {
tk_messageBox -type ok -icon warning -message "No files specified"
return
}
foreach fname $fnames {
if {![file exists $fname]} {
saLog "'$fname' Does not exist"
}
}
set here [focus]
# Get a unique window, note $W is also a global variable
# holding stuff for this instance of the GUI
while {[winfo exists .ntIdent$i]} {incr i}
set W .ntIdent$i
# Shared database for this instance
global $W.table
upvar #0 $W.table myId3Data
global $W.freedb
upvar #0 $W.freedb freedbData
global $W.renamer
set id3Data $W.table ;# for linking
##
# Create window
#
toplevel $W -cursor $::TA(arrow) -class ID3Editors
wm title $W "Table Editor and Fle Renamer"
settings::Hide $W
wm protocol $W WM_DELETE_WINDOW [list id3Editor::closeWindow $W [me]]
##
# Create a notebook, create a tab for the table data and other as needed
#
set bg [$W cget -background]
set NB [NoteBook $W.nb -side top -arcradius 3 -tabbevelsize 3 \
-background $bg -borderwidth 1]
# tabs
$NB insert 0 table -text "Tags"
$NB insert 1 discid -text "Freedb Search"
$NB insert 2 renamer -text "File Renamer"
set Table [$NB getframe table]
set Discid [$NB getframe discid]
set Renamer [$NB getframe renamer]
set myId3Data(NB) $NB
set myId3Data(Table) $Table
set myId3Data(Discid) $Discid
#pack $TableHull.lbf -side top -expand true -fill both
# Query Tab, also grabs id3 data from tracks
pack [id3Table::Tablebuild $Table.table $fnames $smart] -fill both -expand true
# Freedb Tab
pack [id3Table::freedbQueryTab $Discid.discid] -fill both -expand true
# Renamer Tab
package require id3Renamer
pack [id3Renamer::build $Renamer.renamer $fnames] -fill both -expand true
# Pack the notbook and raise the first tab
pack $NB -fill both -expand true
$NB raise table
# restore any prior widths
settings::restoreWidth $W [me]
ontop::pinOnSystemMenu {} $W
myIcon $W
focus $W
settings::Restore $W
}
#----------------------------------------------------------------------------
# Build the contents of the table tab
#----------------------------------------------------------------------------
proc id3Table::Tablebuild {parent fnames smart} {
set base [frame $parent]
set W [winfo toplevel $base]
upvar $W myData
global $W.table
upvar $W.table myId3Data
set id3Data $W.table
##
# Build the GUI
#
# get current data and line to global with the same name
# as this widget (to allow multiple editors at once)
# Decide when to use a scroll bar and how big to make the screen,
# don't want to make any bigger than necessary.
set i 0
set ls [font metrics entry -linespace]
set needHeight [expr {round(([llength $fnames]) * $ls * 1.3)}]
set maxHeight [winfo pixels . $::snackAmpSettings(scrolledCanvasHeight)]
if {$maxHeight > $needHeight} {
set maxHeight $needHeight
}
set bg [$base cget -background]
set sw [ScrolledWindow $base.data -relief flat -borderwidth 1 -background $bg]
set sf [ScrollableFrame $sw.f -constrainedwidth true -height $maxHeight -background $bg]
$sw setwidget $sf
set hull [$sf getframe]
##
# Genre menubutton
# Artist and Albmum menu buttons
foreach {Type Width} [list Artist 30 Album 30 Year 4 Genre 12] {
set myId3Data($Type) $Type
set mb [menubutton $hull.lb$Type -indicatoron 1 -pady 0 -font listbox\
-text $Type -menu $hull.lb$Type.menu -direction flush -width $Width]
balloon::define $mb "Click to select a common $Type for all files"
if {$Type=="Genre"} {
menu $mb.menu \
-postcommand [list id3Editor::_postGenreNames $mb.menu ::${id3Data}($Type)]
} else {
menu $mb.menu \
-postcommand [list id3Table::_postTypeNames $mb.menu ::${id3Data} $Type]
}
# When variable is updated during postcommand, call id3Table_commonType to set others to same
trace variable ::${id3Data}($Type) w id3Table::_commonType
}
##
# Grid out the labels
#
grid \
[label $hull.lb[incr i] -text "Guess Tag"] \
[label $hull.lb[incr i] -text "Change"] \
$hull.lbArtist \
$hull.lbAlbum \
[label $hull.lb[incr i] -text "Title" -relief raised] \
$hull.lbYear \
[label $hull.lb[incr i] -text "Track" -relief raised] \
$hull.lbGenre -sticky nsew
set i 0
##
# Build one row for each file
#
set tag 0
# Data
set ent 1
foreach {primTagType altTagType} $::snackAmpSettings(ID3ReadOrder) {break}
##
# For each track create a row
#
foreach fname $fnames {
set toGrid [list]
# Get existing tag data
# Determine data from parsed name
# Only updates entries where
# the file name provides it
foreach {field} {Artist Album Title Year Track Genre} {
set fnameData($field) ""
}
# Build smart tag if desired
if {$smart} {
id3Editor::cbSmartTagName fname fnameData
}
# Overlay with existing ID3 data
set hasTag [id3Tag::id3Label $fname fnameData $::snackAmpSettings(ID3ReadOrder)]
set myId3Data($tag,oldFullName) $fname
if {$hasTag} {
foreach {field} {Artist Album Title Year Track Genre} {
set myId3Data($tag,$field) $fnameData($field)
}
}
# Default to true
set myId3Data($tag,ok) 1
lappend toGrid [button $hull.bt[incr i] \
-image saImages.Guess -compound left \
-highlightthickness 0 -pady 0 -text "Guess" \
-command [list id3Table::_cbOneSmartTagName $fname $tag ::${id3Data}]]
balloon::define $hull.bt$i "Press this to guess the tags for \n$fname"
lappend toGrid [checkbutton $hull.en[incr i] \
-variable ::${id3Data}($tag,ok) -font {Helvetica 5}]
balloon::define $hull.en$i "Check this to have OK/Apply modify the tag in \n$fname"
##
# Labels, pop-ups and key bindings
#
foreach {field width} {Artist 30 Album 30 Title 40 Year 5 Track 3} {
set item [entry $hull.en$field$ent \
-width $width -textvariable ::${id3Data}($tag,$field)]
widgetWatch::setWatch $item ::${id3Data}($tag,$field)
if {$field=="Track" || $field == "Year"} {
$item configure -justify right
}
##
# Bind up/down keys to go up down in column
#
bind $item [list Focus $hull.en$field[expr {$ent-1}]]
bind $item [list Focus $hull.en$field[expr {$ent+1}]]
lappend toGrid $item
if {$field=="Track"} {
# Bind Control N to track to auto number those below
bind $item [list id3Table::_NumTrack $hull $ent]
balloon::define $item "Modify $field data then press OK or Apply to make the change. Ctrl-n to auto number in track field."
} elseif {[lsearch -exact [list Title Artist Year Album] $field]>= 0} {
bind $item [list id3Table::_NumCase $hull [list $field]]
bind $item [list id3Table::_clear $hull $field $ent]
balloon::define $item "Modify $field data then press OK or Apply to make the change. Ctrl-e to erase field, Ctrl-f to fix case."
}
}
incr ent
# Make a cascaded menu of genres
set mb [menubutton $hull.en[incr i] -indicatoron 1 -pady 0 -font listbox\
-textvariable ::${id3Data}($tag,Genre) -menu $hull.en$i.menu -direction flush -width 10]
balloon::define $mb "Click to select a genre"
menu $mb.menu \
-postcommand [list id3Editor::_postGenreNames $mb.menu ::${id3Data}($tag,Genre)]
widgetWatch::setWatch $mb ::${id3Data}($tag,Genre)
lappend toGrid $mb
eval grid $toGrid -sticky nsew
incr tag
}
# Ensure only the data columns expand
grid columnconfigure $hull 2 -weight 1
grid columnconfigure $hull 3 -weight 1
grid columnconfigure $hull 4 -weight 1
grid columnconfigure $hull 5 -weight 0
##
# Frame for OK/Cancel/Apply buttons
#
pack [frame $base.but] -fill x -expand false -side bottom
# Line
pack [horzLine $base.l[incr i]] \
-fill x -expand false -side bottom
# Data
pack $base.data -side top -expand true -fill both
set wid 9
# Check All
set checkAll [button $base.but.[incr i] -width $wid -default normal -text "Check All" \
-command [list id3Editor::cbSetOK ::$id3Data 1]]
balloon::define $checkAll "Push this to check all checkboxes"
# Check None
set checkNone [button $base.but.[incr i] -width $wid -default normal -text "Check None" \
-command [list id3Editor::cbSetOK ::$id3Data 0]]
balloon::define $checkNone "Push this to un-check all checkboxes"
# Guess Tags
set guessTags [button $base.but.[incr i] -default normal -text "Guess Tags" \
-command [list id3Table::_cbManySmartTagName ::$id3Data]\
-compound left -image saImages.Guess]
balloon::define $checkNone "Push this to guess tags based on the file name"
# Guess Tags
set v2tag [button $base.but.[incr i] -width $wid -default normal -text "Alt Tag" \
-command [list id3Table::_cbManygetV2data $altTagType ::$id3Data]]
balloon::define $v2tag "Push this to copy in ID3V2 data (if it exists)"
set artAlb [button $base.but.[incr i] -width $wid -default normal -text "Art<->Alb" \
-command [list id3Table::_cbManygetSwap ::$id3Data Artist Album]]
balloon::define $v2tag "Push this to Swap Artist and Album"
set albTit [button $base.but.[incr i] -width $wid -default normal -text "Alb<->Tit" \
-command [list id3Table::_cbManygetSwap ::$id3Data Album Title]]
balloon::define $v2tag "Push this to Swap Album and Title"
set artTit [button $base.but.[incr i] -width $wid -default normal -text "Art<->Tit" \
-command [list id3Table::_cbManygetSwap ::$id3Data Artist Title]]
balloon::define $v2tag "Push this to Swap Artist and Title"
# Guess Case
set guessCase [button $base.but.[incr i] -width $wid -default normal -text "Fix Case" \
-command [list id3Table::_NumCase $hull {Artist Album Title}]]
balloon::define $guessCase "Push this to correct the text case in the tags"
# Apply
set app [button $base.but.[incr i] -default normal -text "Apply" \
-command [list id3Table::_apply $W [me] 0] \
-compound left -image saImages.Save]
balloon::define $app "Push this to modify the ID3 tags for checked entries"
# Apply and close
set ok [button $base.but.[incr i] -default active -text "OK" -padx 4\
-compound left -image saImages.Apply \
-command [list id3Table::_apply $W [me] 1]]
balloon::define $ok "Push this to modify the ID3 tags for checked entries, then close this dialog"
# Cancel
set can [button $base.but.[incr i] -default normal -text "Cancel" -compound left -image saImages.Close \
-command [list id3Editor::closeWindow $W [me]]]
balloon::define $can "Push this to ignore changes made and restore the previous settings"
grid $checkAll $checkNone $artAlb $albTit $artTit -padx [extraPad 1] -pady 2 -sticky ewns
grid $v2tag $guessTags $guessCase $app $ok $can -padx [extraPad 1] -pady 2 -sticky ewns
$ok configure -default active
#bind [winfo toplevel $W] [list $ok invoke]
bind [winfo toplevel $W] [list $can invoke]
return $base
}
#------------------------------------------------------------------------------
# Function : id3Table::_NumTrack
# Description: Auto number tracks below based on this one
#
# Author : Tom Wilkason
#------------------------------------------------------------------------------
proc id3Table::_NumTrack {hull index} {
set len [slen $hull.enTrack]
set start 1
foreach {entry} [lsort -dictionary [winfo children $hull]] {
if {[string match $hull.enTrack* $entry]} {
set enIndex [string range $entry $len end]
set gvalue [$entry cget -textvariable]
if {$enIndex == $index} {
set start [set $gvalue]
if {![string is integer -strict $start]} {
set start 1
set $gvalue $start
}
} elseif {$enIndex >= $index} {
incr start
set $gvalue $start
}
}
}
}
#----------------------------------------------------------------------------
# Clear a field
#----------------------------------------------------------------------------
proc id3Table::_clear {hull field index} {
foreach {entry} [lsort -dictionary [winfo children $hull]] {
regexp {[0-9]+$} $entry tIndex ;# grab what index this is
if {[string match $hull.en${field}* $entry] && ($index==$tIndex)} {
# Need $gvalue due to trace on it
set gvalue [$entry cget -textvariable]
set $gvalue $id3Tag::rmMatch
}
}
}
#------------------------------------------------------------------------------
# Function : id3Table::_NumCase
# Description: Correct the case for each item in this column (field)
#
# Author : Tom Wilkason
#------------------------------------------------------------------------------
proc id3Table::_NumCase {hull fields} {
foreach {entry} [lsort -dictionary [winfo children $hull]] {
foreach field $fields {
if {[string match $hull.en${field}* $entry]} {
# Need $gvalue due to trace on it
set gvalue [$entry cget -textvariable]
set $gvalue [stringTitle [set $gvalue]]
}
}
}
}
#------------------------------------------------------------------------------
# Function : id3Table::_commonType
# Description: Callback to change all genres for block editor
# Author : Tom Wilkason
#------------------------------------------------------------------------------
proc id3Table::_commonType {_Glob Element args} {
upvar $_Glob Glob
set value $Glob($Element)
foreach {item} [array names Glob "*,$Element"] {
# Traced, use full global name
set Glob($item) $value
}
}
#------------------------------------------------------------------------------
# Function : id3Table::_apply
# Description: Retag a set of files
#
# Author : Tom Wilkason
#------------------------------------------------------------------------------
proc id3Table::_apply {W winTag {close 0}} {
global $W.table
upvar $W.table id3Data
global $W.freedb
upvar $W.freedb freedbData
# Process each file that is checked
set indicies [lsort -dictionary [array names id3Data *,ok]]
set i 0
set fnames [list]
set totchanged 0
foreach {element} $indicies {
# If checkbox checked, try to rename it
incr i
if {$id3Data($element)} {
set tag [lindex [split $element ,] 0]
set fname $id3Data($tag,oldFullName)
lappend fnames $fname
# Blank then Get existing data
unset -nocomplain fnameData
#id3Tag::id3Label $fname fnameData
set changed 0
foreach {field} {Artist Album Title Year Track Genre} {
# Copy in only existing non-blank data
if {[hasData id3Data($tag,$field)] > 0} {
set fnameData($field) $id3Data($tag,$field)
incr changed
}
}
# Don't bother to update if the data hasn't changed
if {$changed} {
if {[id3Tag::id3Modify $fname fnameData]} {
waitWindow::percent $i [llength $indicies] "Setting Tag for\n[splitFile $fname]"
incr totchanged
}
}
}
}
if {$totchanged} {
update
# Update any data that is currently shown
trackList::updateFiles $fnames
LB::updateFiles $fnames
}
waitWindow::close
# If close widget when done indicated.
if {$close} {
id3Editor::closeWindow $W $winTag
}
}
#------------------------------------------------------------------------------
# Function : id3Table_cbOneSmartTagName
# Description: Move data from a single smart tag into an indexed array
# used by the block editor.
# Author : Tom Wilkason
#------------------------------------------------------------------------------
proc id3Table::_cbOneSmartTagName {fname index _Array} {
upvar $_Array Data
id3Editor::cbSmartTagName fname oneData
foreach {item} [array names oneData] {
if {$Data($index,$item) ne $oneData($item)} {
# Traced, use full global name
set Data($index,$item) $oneData($item)
set Data($index,ok) 1
}
}
}
#------------------------------------------------------------------------------
# Function : id3Table_cbManySmartTagName
# Description: Create a number of smart tag to name mappings
#
# Author : Tom Wilkason
#------------------------------------------------------------------------------
proc id3Table::_cbManySmartTagName {_Array} {
upvar $_Array Data
foreach {item} [array names Data *,oldFullName] {
set fname $Data($item)
set index [lindex [split $item ,] 0]
Unset oneData
id3Editor::cbSmartTagName fname oneData
foreach {item} [array names oneData] {
if {$Data($index,$item) ne $oneData($item)} {
# Traced, use full global name
set Data($index,$item) $oneData($item)
set Data($index,ok) 1
}
}
}
}
#------------------------------------------------------------------------------
# id3Table::_cbManygetSwap ::$id3Data Artist Title
#------------------------------------------------------------------------------
proc id3Table::_cbManygetSwap {_Array From To} {
Trace
upvar $_Array Data
set from [array names Data *,$From]
set to [array names Data *,$To]
foreach {f} $from {t} $to {
set save $Data($t)
set Data($t) $Data($f)
set Data($f) $save
}
}
#------------------------------------------------------------------------------
# Function : id3Table_cbManygetV2data
# Description: Read the ID3V2/ogg data and if it exists use
# it instead
# used by the block editor.
# Author : Tom Wilkason
#------------------------------------------------------------------------------
proc id3Table::_cbManygetV2data {altTagType _Array} {
upvar $_Array Data
foreach {item} [array names Data *,oldFullName] {
set fname $Data($item)
set index [lindex [split $item ,] 0]
Unset oneData
if {[id3Tag::isogg $fname]} {
set type "OGG"
} else {
set type $altTagType
}
if {[id3Tag::id3Label $fname oneData $type]} {
foreach field {Title Artist Album Track} {
if {[info exists oneData($field)]} {
if {$field=="Track"} {
# Just want the base number
set val [lindex [split $oneData($field) /] 0]
} else {
set val $oneData($field)
}
set Data($index,$field) $val
}
}
set Data($index,ok) 1
}
}
}
#------------------------------------------------------------------------------
# Function : id3Table_postTypeNames
# Description: On pop-up, post the common types
# Author : Tom Wilkason
#------------------------------------------------------------------------------
proc id3Table::_postTypeNames {Menu _Array Element} {
upvar $_Array Array
##
# Get list of current entries
#
set last [$Menu index end]
set added [list]
if {![string match "none" $last]} {
for {set i 0} {$i <= $last} {incr i} {
lappend added [$Menu entrycget $i -label]
}
}
##
# Add any unique items in the array to the menu
#
foreach {item} [lsort -dictionary [array names Array "*,$Element"]] {
set value $Array($item)
if {[slen $value] > 0 && [lsearch -exact $added $value]==-1} {
# TFW: What we really want is to set te $item fields directly
# not the Element which is also a label
$Menu add command -label "$value" \
-command [list set ${_Array}($Element) $value]
lappend added $value
}
}
}
#----------------------------------------------------------------------------
# Dialog to allow select of various freedb entries and select then
# apply them to the associated tag editor window.
#----------------------------------------------------------------------------
proc id3Table::freedbQueryTab {parent} {
set hull [frame $parent]
set W [winfo toplevel $hull]
global $W.table
upvar $W.table id3Data
global $W.freedb
upvar $W.freedb freedbData
# get my hull
set i 0
# frames
frame $hull.list
frame $hull.list.tf
frame $hull.list.bf
#
# Modify to have a list box on type, then selecting a track fills
# the bottom with the details. Then use the apply key
#
set sb [Scrolled_Listbox $hull.list.tf.sb \
-selectmode browse \
-exportselection no \
-activestyle dotbox \
-height 1 \
-width 45 \
-foreground $::snackAmpSettings(fg) \
-selectforeground $::snackAmpSettings(fg) \
-selectbackground $::snackAmpSettings(highlight) \
-background $::snackAmpSettings(bg)]
settings::regColor $sb \
-foreground ::snackAmpSettings(fg) \
-selectforeground ::snackAmpSettings(fg) \
-selectbackground ::snackAmpSettings(highlight) \
-background ::snackAmpSettings(bg)
set i 0
##
# Listbox to hold the track details
#
set lb [Scrolled_Listbox $hull.list.bf.lb \
-selectmode extended \
-exportselection no \
-activestyle dotbox \
-width 45 \
-height 1 \
-foreground $::snackAmpSettings(fg) \
-selectforeground $::snackAmpSettings(fg) \
-selectbackground $::snackAmpSettings(highlight) \
-background $::snackAmpSettings(bg)]
settings::regColor $lb \
-foreground ::snackAmpSettings(fg) \
-selectforeground ::snackAmpSettings(fg) \
-selectbackground ::snackAmpSettings(highlight) \
-background ::snackAmpSettings(bg)
##
# Bind a select on the entry to lookup the data
#
bindtags $lb none
bind $sb <> {
focus %W
id3Table::cbAlbumSelected %W [%W get [%W curselection]]
}
set freedbData(LB) $lb
set freedbData(SB) $sb
##
# Frame for OK/Cancel/Apply buttons
#
frame $hull.but
pack $hull.but -fill x -expand false -side bottom
# Line
pack [horzLine $hull.l[incr i]] \
-expand false -fill x -side bottom
# Search fields, load defaults
set freedbData(sArtist) $id3Data(0,Artist)
set freedbData(sAlbum) $id3Data(0,Album)
set s1 [button $hull.but.[incr i] -text "Artist" \
-command [list id3Table::searchFreedb $W "Artist"] \
-compound left -image saImages.Guess \
]
set s2 [entry $hull.but.[incr i] -textvariable $W.freedb(sArtist) \
-relief sunken -width 30]
set s3 [button $hull.but.[incr i] -text "Album" \
-command [list id3Table::searchFreedb $W "Album"] \
-compound left -image saImages.Guess \
]
set s4 [entry $hull.but.[incr i] -textvariable $W.freedb(sAlbum) \
-relief sunken -width 30]
# Guess freedb Tags
set freedbSearchTags [button $hull.but.[incr i] -default normal -text "Search Tags" \
-command [list id3Table::searchFreedb $W]\
-compound left -image saImages.Guess]
balloon::define $freedbSearchTags "Push this to search the freedb service for Album Artist"
# Try to split album into artst / album for various artists
set useVarious [checkbutton $hull.but.[incr i] \
-text "Various Artist Album" -variable $W.freedb(various)]
balloon::define $useVarious "Check this to extract the Arist Name from the Track for albums with multiple artists"
set l1 [label $hull.but.[incr i] -text "Year"]
set l2 [label $hull.but.[incr i] -textvariable $W.freedb(Year) -relief sunken -width 30]
set l3 [label $hull.but.[incr i] -text "Genre"]
set l4 [label $hull.but.[incr i] -textvariable $W.freedb(Genre) -relief sunken -width 30]
# Guess freedb Tags
set freedbGuessTags [button $hull.but.[incr i] -default normal -text "Guess Tags" \
-command [list id3Table::populateAlbums $W]\
-compound left -image saImages.Guess]
balloon::define $freedbGuessTags "Push this to query the freedb service to find tags based on computed discid"
# Apply
set app [button $hull.but.[incr i] -default normal -text "Apply" -compound left -image saImages.Save \
-command [list id3Table::processButtons $W apply]]
balloon::define $app "Push this Apply the selected album tracks"
# Cancel
set can [button $hull.but.[incr i] -default normal -text "Close" -compound left -image saImages.Close \
-command [list id3Table::processButtons $W cancel]]
balloon::define $can "Push this to cancel, then close this dialog"
# Freedb Html
set ht [button $hull.but.[incr i] -text "Details" -default normal -image saImages.web -compound left\
-command [list id3Table::freedbAlbum $sb]]
balloon::define $ht "Push this to bring up a web page for the selected album"
grid $s1 $s2 $s3 $s4 $freedbSearchTags $useVarious $ht -padx [extraPad 1] -pady 1 -sticky ew
grid $l1 $l2 $l3 $l4 $freedbGuessTags $app $can -padx [extraPad 1] -pady 1 -sticky ew
# Pack em in
pack $hull.list.tf.sb -fill both -expand true -side top
pack $hull.list.bf.lb -fill both -expand true -side top
pack $hull.list.tf -fill both -expand true -side left
pack $hull.list.bf -fill both -expand true -side right
pack $hull.list -fill both -expand true -side top
return $hull
}
#----------------------------------------------------------------------------
# Launch a web browser on the select track
#----------------------------------------------------------------------------
proc id3Table::freedbAlbum {sb} {
if {[catch {$sb get [$sb curselection]} key] } then {
return
}
set W [winfo toplevel $sb]
global $W.table
upvar $W.table id3Data
global $W.freedb
upvar $W.freedb freedbData
if {[slen $key]} {
set record $freedbData(lbkey,$key)
foreach {genre discid} [split $record] {break}
#http://www.freedb.org/freedb_search_fmt.php?cat=rock&id=ad0d840d
#package require HttpD
set url $browser::cddbBase
append url "freedb_search_fmt.php?cat=$genre&id=$discid"
set redir {}
set redir [subst -nocommand $redir]
set redirFile [file join $::HOME redirect.html]
if {[catch {open $redirFile w} result]} {
saLog "Error:$result"
return
} else {
puts $result $redir
close $result
}
browser::urlOpen $redirFile
}
}
#----------------------------------------------------------------------------
# When the user selects the freedb query tab or button, populate
# the left table with the album list if possible.
#----------------------------------------------------------------------------
proc id3Table::populateAlbums {W} {
set win [string trimleft $W ":"]
if {[winfo exists $win]} {
global $W.table
upvar $W.table id3Data
global $W.freedb
upvar $W.freedb freedbData
##
# Raise this page
#
$id3Data(NB) raise discid
package require freedb
# Get list of all tracks
set tracks [list]
foreach {item} [lsort -dictionary [array names id3Data *,oldFullName]] {
lappend tracks $id3Data($item)
}
# Query freedb to see if album exists
set found [freedb::albumList $tracks]
if {[llength $found]==0} {
tk_messageBox -type ok -icon info -message "No Freedb Matches Found on\n$::snackAmpSettings(freedb)"
return
}
# Populate with return records (lookup), verify window still exists
if {[info exists freedbData(SB)] && [winfo exists $freedbData(SB)]} {
$freedbData(SB) delete 0 end
set rows 0
foreach {record} $found {
;# rock 7908110a The Shins / Chutes too Narrow
if {[regexp -- {(.+? .+?) (.*)$} $record -> front back]} {
set freedbData(lbkey,$back) $record ;# save a key
$freedbData(SB) insert end $back
incr rows
} else {
saLog $record
}
}
$freedbData(SB) configure -height $rows
$freedbData(SB) selection set 0
if {[llength $found]==1} {
id3Table::populateTracks ::$W $record
}
}
} else {
saLog "$W no longer exists!"
}
}
#----------------------------------------------------------------------------
# Based on Artist and Album in search field
# Search the freedb database and populate the list
# box with returned hits
#----------------------------------------------------------------------------
proc id3Table::searchFreedb {W {field ""}} {
set win [string trimleft $W ":"]
if {[winfo exists $win]} {
global $W.table
upvar $W.table id3Data
global $W.freedb
upvar $W.freedb freedbData
##
# Raise this page
#
$id3Data(NB) raise discid
package require freedb
# Get list of all tracks
# Query freedb to see if album exists
switch -- $field {
"Album" {
set found [freedb::albumListHTML "" $freedbData(sAlbum)]
}
"Artist" {
set found [freedb::albumListHTML $freedbData(sArtist) ""]
}
default {
set found [freedb::albumListHTML $freedbData(sArtist) $freedbData(sAlbum)]
}
}
if {[llength $found]==0} {
tk_messageBox -type ok -icon info -message "No Freedb Matches Found on\n$::snackAmpSettings(freedb)"
return
}
# Populate with return records (lookup)
if {[info exists freedbData(SB)] && [winfo exists $freedbData(SB)]} {
$freedbData(SB) delete 0 end
set rows 0
foreach {record} $found {
if {[regexp -- {(.+? .+?) (.*)$} $record -> front back]} {
set freedbData(lbkey,$back) $record ;# save a key
$freedbData(SB) insert end $back
incr rows
} else {
saLog $record
}
}
$freedbData(SB) configure -height $rows
$freedbData(SB) selection set 0
if {[llength $found]==1} {
id3Table::populateTracks ::$W $record
}
}
} else {
saLog "$W no longer exists!"
}
}
#------------------------------------------------------------------------------
# When the user selects a row, show the details
#------------------------------------------------------------------------------
proc id3Table::cbAlbumSelected {W key} {
Trace
set win [string trimleft $W ":"]
if {[winfo exists $win]} {
set W [winfo toplevel $win]
global $W.table
upvar $W.table id3Data
global $W.freedb
upvar $W.freedb freedbData
if {[slen $key]} {
id3Table::populateTracks ::$W $freedbData(lbkey,$key)
}
} else {
saLog "$W no longer exists!"
}
}
#----------------------------------------------------------------------------
# Determine if we can lookup the tags from freedb
# Compute the disc ID
#----------------------------------------------------------------------------
proc id3Table::populateTracks {W record} {
Trace
set win [string trimleft $W ":"]
if {[winfo exists $win]} {
global $W.table
upvar $W.table id3Data
global $W.freedb
upvar $W.freedb freedbData
set lb $freedbData(LB) ;# listbox
Unset freedbQuery
set Album [freedb::albumDetails $record freedbQuery]
$lb delete 0 end
set freedbData(record) $record
if {[slen $Album]} {
array unset freedbData "*,Title"
array unset freedbData "*,Track"
set index 0
foreach {key} {Album Title Year Genre} {
if {[info exists freedbQuery($key)]} {
set freedbData($key) $freedbQuery($key)
}
}
foreach {track} [lsort -dictionary [array names freedbQuery Track,*]] {
foreach {key num} [split $track ,] {break}
set freedbData($index,Title) $freedbQuery($track)
set freedbData($index,Track) [id3Tag::cleanTrack $num]
set length $freedbQuery(Length,$num)
$lb insert end "([format %02d $num]) [timify $length] : $freedbQuery($track)"
incr index
}
}
} else {
saLog "$W no longer exists!"
}
}
#----------------------------------------------------------------------------
# Bottom button callback
#----------------------------------------------------------------------------
proc id3Table::processButtons {W button} {
global $W.table
upvar $W.table id3Data
global $W.freedb
upvar $W.freedb freedbData
# Parent is the associated array for the table
switch -- $button {
"apply" {
# This should be redundant, use as a backup
if {![info exists freedbData(record)]} {
return ""
}
set hasArtist [regexp -- {(.+? .+?) (.*) / (.*)$} $freedbData(record) -> front artist album]
# id3Data (index,Title) (index,Track) (index,Artist) (index,Album)
# freedbData (index,Title) (index,Track)
set totalID3 [llength [array names id3Data *,Title]]
set totalfreedb [llength [array names freedbData *,Title]]
if {$totalID3 != $totalfreedb} {
set ans [tk_messageBox -type yesno -icon question \
-message "The number of tracks found differs from the number of tracks to tag\ndo you want to proceed anyway?\n\nfreedb=$totalfreedb, ID3=$totalID3"]
if {$ans!="yes"} {
return
}
set diff 1
} else {
set diff 0
}
# For each freedb return
foreach {key} [lsort -dictionary [array names freedbData *,Title]] {
foreach {index tag} [split $key ,] {break}
#
# Match up source and dest track numbers if tot tracks differ
#
set id3Index $index
if {$diff} {
# Try and find associated id3Index if tracks match up
set fdbTrack [scan $freedbData($index,Track) %d]
set found 0
foreach {key} [lsort [array names id3Data *,Track]] {
set id3Track [scan $id3Data($key) %d]
if {$id3Track==$fdbTrack} {
foreach {idx xxx} [split $key ,] {break}
set id3Index $idx
incr found
break
}
}
if {!$found} {
continue
}
}
# Get track number
# Especially if id3 num and freedb num don't match
set id3Data($id3Index,Track) $freedbData($index,Track)
# Album Title holds Artist / Album
if {[hasData freedbData(Title)] && [regexp -- {(.+)/(.+)$} $freedbData(Title) -> Artist Album]} {
set id3Data($id3Index,Artist) [string trim $Artist]
set id3Data($id3Index,Album) [string trim $Album]
} elseif {$hasArtist} {
set id3Data($id3Index,Artist) $artist
set id3Data($id3Index,Album) $album
} else {
set id3Data($id3Index,Artist) "Unknown"
set id3Data($id3Index,Album) "Unknown"
}
# Track Title can hold track title or Aritst / Title or Artist - Title in the case of various artists
set id3Data($id3Index,Title) $freedbData($index,Title) ;# Default
foreach {re} {{.+?/(.+)-(.+)$} {(.+)/(.+)$} {(.+)-(.+)$}} {
if {$freedbData(various) && [regexp -- $re $freedbData($index,Title) -> Artist Title]} {
set id3Data($id3Index,Title) [string trim $Title]
set id3Data($id3Index,Artist) [string trim $Artist]
if {[info exists album]} {
set id3Data($id3Index,Album) $album
} elseif [info exists Album] {
set id3Data($id3Index,Album) $Album
} else {
set id3Data($id3Index,Album) $freedbData(sAlbum)
}
break
}
}
# Copy in Genre and Year only if dest fields are blank
foreach {key} {Year Genre} {
if {[hasData freedbData($key)] && ![hasData id3Data($index,$key)]} {
set id3Data($id3Index,$key) $freedbData($key)
}
}
}
}
"cancel" {
id3Editor::closeWindow $W freedb
}
default {}
}
}
package provide id3Table 1.0