#!/usr/bin/wish # tkbib 1.1 main file # Copyright (c) 2000 Patrick H. Madden # SUNY Binghamton Computer Science Dept # pmadden@cs.binghamton.edu # http://vlsicad.cs.binghamton.edu/~pmadden # The latest version should be available at # http://vlsicad.cs.binghamton.edu/~pmadden/tkbib # Edit history: first release, Jan 20, 2000 # Improvements: # Jan 25, 2000, Peter Baum sent a few cleanups for the # main window, and a multi-line fix for the parser # # This file is part of tkbib # # 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, 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; see the file COPYING. If not, write to # the Free Software Foundation, 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. # Check for a command line arg set init_bib "" if {$argc != 0} { set init_bib [lindex $argv 0] } # Determine where this code is installed; on UNIX boxes, we may # need to chase a symbolic link to get to the install directory. set ELIB . set curr_dir [pwd] set source [info script] if {[string compare [file type $source] file]} { set source [file readlink $source] } set ELIB [file dirname $source] # Graphics to put into the "about" dialog box. set aboutlist [list $ELIB/graphics/watson.gif \ $ELIB/graphics/photo3.gif \ $ELIB/graphics/tree.gif ] set aboutlogo $ELIB/graphics/cs-logo2.gif set version "1.0" # Load in the other windows source "$ELIB/about.tcl" source "$ELIB/search.tcl" #CLIP+entry_types+TEXT+64+2000/01/21 16:43:09+39.0+# # Global defines for the various accepted fields in Bib files. # This is all extracted from the Bibtex docs; I've added an "index" # field that is used to sort the various papers in my filing cabinet. # There are two types of fields: ft_r are "required", while ft_o are # "optional" -- right now, I don't distinguish (bibtex will scold # you if you're missing a required field. # set reftypes { article conference book proceedings booklet inbook \ incollection inproceedings manual techreport \ phdthesis mastersthesis unpublished misc} set ft_r(article) {article index author title journal year} set ft_o(article) {volume number pages month note keywords url} set ft_r(book) {book index author editor title publisher year} set ft_o(book) {volume number series address edition month note keywords url} set ft_r(booklet) {booklet index title} set ft_o(booklet) {author howpublished address month year note keywords url } set ft_r(inbook) {inbook index author title chapter pages publisher year} set ft_o(inbook) {volume number series type address edition\ month note keywords url} set ft_r(incollection) {incollection index \ author title booktitle publisher year} set ft_o(incollection) {editor volume number series type \ chapter pages address edition \ month note keywords url} set ft_r(inproceedings) {inproceedings index author title booktitle year} set ft_o(inproceedings) {editor volume number series pages \ address month organization publisher\ note keywords url} set ft_r(conference) {conference index author title booktitle year} set ft_o(conference) {editor volume number series pages \ address month organization publisher \ note keywords url} set ft_r(manual) {manual index title} set ft_o(manual) {author organization address edition \ month year note keywords url} set ft_r(mastersthesis) {mastersthesis index author title school year} set ft_o(mastersthesis) {type address month note keywords url} set ft_r(misc) { misc index } set ft_o(misc) { author title howpublished month year note keywords url} set ft_r(phdthesis) {phdthesis index author title school year} set ft_o(phdthesis) {type address month note keywords url } set ft_r(proceedings) {proceedings index title year} set ft_o(proceedings) {editor volume number series address \ month organization publisher note keywords url } set ft_r(techreport) {techreport index author title institution year} set ft_o(techreport) {type number address month note keywords url } set ft_r(unpublished) {unpublished index author title note} set ft_o(unpublished) {month year keywords url } # Globals to hold all the terminal types (things that are defined # by @string{ }, the entries (all citations), and the keys. set terminals { } set entries { } #CLIP+set_initial+TEXT+135+2000/01/21 12:54:30+3.23+# # set_initial -- the edit window has a number of slots to fill # in data, and this sets the initial value to the appropriate # field in the citation proc set_initial { v entry } { # puts "Entry is $entry" foreach l $entry { # puts "L is $l" if {[string compare $v [lindex $l 0]] == 0} { return [lindex $l 1] } } return "" } #CLIP+edit_entry+TEXT+150+2000/01/21 12:56:01+4.51+# # edit_entry -- Creates a widget for editing a field of a # citation. Some of these are going to be entries (single line), # while others are small 4-line text windows. Things like author, # title, and note need more space than other fields proc edit_entry { n v init_val } { label .e.f.l$n -text $v if {[lsearch {author title note} $v] >= 0} { text .e.f.e$n -width 60 -height 4 -wrap word .e.f.e$n insert 1.1 $init_val } else { entry .e.f.e$n -width 60 .e.f.e$n insert 1 $init_val } grid .e.f.l$n -in .e.f -row $n -column 0 grid .e.f.e$n -in .e.f -row $n -column 1 } #CLIP+edit+TEXT+168+2000/01/29 13:11:11+31.0+# # edit -- Opens an editing window, creates all the widgets to # allow editing, and adds the buttons to either update or dismiss proc edit { deftype key } { global exists global ft_r ft_o global entries set current { } set type $deftype if {[string compare $key ""]} { foreach e $entries { if {[string compare $key [lindex [lindex $e 0] 1]] == 0} { set current $e set type [lindex [lindex $e 0 ] 0] } } } # puts "Found entry $current" if {!$exists} { toplevel .e set exists 1 } else { destroy .e.f destroy .e.fb } wm protocol .e WM_DELETE_WINDOW "edit_done dismiss $key" frame .e.f frame .e.fb button .e.fb.done -text "Update" -command "edit_done $type $key" button .e.fb.dis -text "Dismiss" -command "edit_done dismiss $key" wm title .e "Editing $type record" grid .e.f set n 0 foreach v $ft_r($type) { set init_val [set_initial $v $current] edit_entry $n $v $init_val incr n } foreach v $ft_o($type) { set init_val [set_initial $v $current] edit_entry $n $v $init_val incr n } pack .e.fb.done -in .e.fb -side left pack .e.fb.dis -in .e.fb -side right pack .e.f .e.fb } #CLIP+value_clean+TEXT+224+2000/01/21 12:58:07+3.0+# # value_clean -- Data that goes into a citation should be stripped # of excess double-quotes, and have newlines converted to spaces (the # double-quotes will get added if needed when we write the bib file out). # proc value_clean { txt } { set txt [string trim $txt] # Convert all newlines to spaces regsub -all "\n" $txt " " txt regsub "^\"" $txt "" txt set len [expr [string length $txt] - 1] if {$len >= 0} { if {[string compare [string index $txt $len] "\""] == 0} { set txt [string range $txt 0 [expr $len - 1]] # puts "Removed second quote" } } return $txt } #CLIP+edit_get+TEXT+246+2000/01/21 12:59:10+5.0+# # edit_get -- When we extract the values from the edit widgets, # we have to use the appropriate functions for either text widgets # or entries. Some fields are bigger than others (see edit_entry # to see where the widgets get created). proc edit_get { n v } { if {[lsearch {author title note} $v] >= 0} { set txt [.e.f.e$n get 1.0 end] } else { set txt [.e.f.e$n get] } return [value_clean $txt] } #CLIP+edit_done+TEXT+260+2000/01/21 13:00:27+52.0+# # edit_done -- We've pressed either dismiss or update. If # it's dismiss, just blow away the edit window. If it's update, # pull the values out of the widgets, and insert this into our # list of citations proc edit_done { type oldkey } { global ft_r ft_o entries exists if {[string compare $type dismiss] == 0} { destroy .e set exists 0 return } # We may need to remove an old key, and insert a new one set current_card { } set n 0 foreach v $ft_r($type) { set value [edit_get $n $v] if {[string compare $value ""]} { lappend current_card [list $v [string trim $value]] } incr n } foreach v $ft_o($type) { set value [edit_get $n $v] if {[string compare $value ""]} { lappend current_card [list $v [string trim $value]] } incr n } # Maybe replace an entry ? set n 0 set found -1 foreach e $entries { if {[string compare $oldkey [lindex [lindex $e 0] 1]] == 0} { set found $n } incr n } if {$found >= 0} { set entries [lreplace $entries $found $found] } lappend entries $current_card # .fc.key_l insert end [lindex [lindex $current_card 0] 1] update_lists destroy .e set exists 0 } #CLIP+add_term+TEXT+315+2000/01/21 13:00:56+2.71+# # add_term -- Adds an entry for an @string{ } line in # the bib file. Check to make sure we're not duplicating something.... proc add_term { term longterm } { global terminals if {[lsearch $terminals "$term *"] >= 0} { puts "Duplicate term $term skipped" return } lappend terminals [list $term $longterm] } #CLIP+check_cite_key+TEXT+328+2000/01/21 13:20:34+1.16+# # check_cite_key # Check to see if an entry has already been defined. If so, return # 1 proc check_cite_key { key } { global entries foreach e $entries { if {[string compare $key [lindex [lindex $e 0] 1]] == 0} { return 1 } } return 0 } #CLIP+add_citation+TEXT+343+2000/01/21 13:01:39+4.0+# # add_citation -- Adds a citation to the list of entries. Check # to make sure we don't duplicate a cite key (and bitch if this # is duplicated). proc add_citation { card } { global entries if {[llength $card] == 0} { return } set key [lindex [lindex $card 0] 1] if {[check_cite_key $key]} { puts "Skipped duplicate citation for $key" } else { lappend entries $card } } #CLIP+keycmp+TEXT+362+2000/01/21 13:02:23+2.0+# # keycmp -- Compare two citations, to see which should be listed # first. Used by update_lists to sort cite keys proc keycmp { e1 e2 } { return [string compare [lindex [lindex $e1 0] 1] [lindex [lindex $e2 0] 1]] } #CLIP+update_lists+TEXT+369+2000/01/21 13:10:53+23.0+# # update_lists -- Go through and sort all citations, adding proc update_lists { } { global terminals entries # puts "Sorted: [lsort -command keycmp $entries]" set entries [lsort -command keycmp $entries] .fc.key_l delete 0 end .term.ft.term_l delete 0 end foreach pr $terminals { # puts "EXPAND: [lindex $pr 0] --> [lindex $pr 1]" .term.ft.term_l insert end [lindex $pr 0] } foreach e $entries { # puts "Entry $e, length [llength $e]" set etype [lindex $e 0] # puts "Etype is $etype" .fc.key_l insert end [lindex $etype 1] } # puts "There are [llength $terminals] terms, [llength $entries] entries" } #CLIP+parse_bib+TEXT+392+2000/01/29 11:59:04+50.14+# # parse_bib -- A very simple Bibfile parser. It assumes that # each field of a bib file entry will be on a single line, among # other things. Chances are good you'll need to hand-edit a bib # file to get it to work with tkbib; I'll probably do a lex-and-yacc # converter at a later date (or maybe work on this tcl/tk parser more). proc parse_bib { fn } { global entries terminals set f [open $fn r] set current_card {} while {[gets $f line] >= 0} { # A string definition of some sort.... if {[regexp -nocase "@string\{(.*)=(.*)\}" $line m term longterm]} { add_term $term $longterm continue } if {[regexp -nocase "@(.*)\{(.*)," $line m reftype key]} { # puts "Saw entry type $reftype with key $key" # start_entry $reftype $key # We may have been working on a previous citation, so # save it and start a new one if {[llength $current_card] > 0} { add_citation $current_card } set reftype [string tolower [string trim $reftype]] set key [string trim $key] set current_card [list [list $reftype $key]] continue } # This is a line that gets attached to the current citation # pgb if {[regexp "(.*)=(.*)," $line m field value]} if {[regexp {(.*)=(.*)} $line m field value]} { set value [string trim $value] while { [bracketCount $value] != 0 } { if { [gets $f line] < 0 } { puts "Error in parsing \"$field = $value\"" return } append value " " [string trim $line] } set value [string trimright $value ","] # end pgb set field [string tolower [string trim $field]] set value [value_clean [string trim $value]] lappend current_card [list $field $value] continue } } # When we get here, we may have been working on an entry, # so add it to the list. if {[llength $current_card] > 0} { add_citation $current_card } update_lists } #CLIP+wr_format+TEXT+457+2000/02/18 19:49:28+18.49+# # wr_format -- Determines if we need to put double-quotes around # an entry, and if so, does it. proc wr_format { key term } { if {[string first " " $term] >= 0} { return \"$term\" } if {[string first " " $term] >= 0} { return \"$term\" } if {[string first "-" $term] >= 0} { return \"$term\" } if {[string first $key "title"] >= 0} { return \{\{$term\}\} } # If it is a month or booktitle abbreviation, just return it (Latex # going to expand it). if {[string first $key "month booktitle journal"] >= 0} { return $term } return \"$term\" } #CLIP+write_bib+TEXT+482+2000/01/29 20:38:09+21.37+# # write_bib -- Outputs all current entries in the standard format. # Assumes we're handed an active file handle.... proc write_bib { fn } { global entries terminals set f [open $fn w] foreach t $terminals { puts $f "@string\{[lindex $t 0]=[lindex $t 1]\}" } foreach e $entries { set n 0 foreach pr $e { if {$n == 0} { puts $f "@[lindex $pr 0]\{[lindex $pr 1]," incr n } else { if {[string compare [lindex $pr 1] ""]} { puts $f " [lindex $pr 0]=[wr_format [lindex $pr 0] [lindex $pr 1]]," } } } puts $f "\}" puts $f "" } close $f } #CLIP+open_bib+TEXT+513+2000/01/21 13:19:24+2.37+# # open_bib -- Readins in the bibliography, after getting a file # name from the tk_getOpenFile widget set types { {{BibTex Files} {.bib} } {{All FIles} * } } proc open_bib { } { global types set fn [tk_getOpenFile -filetypes $types] if {[string compare $fn ""] == 0} { return } parse_bib $fn } #CLIP+save_bib+TEXT+530+2000/01/21 13:19:56+10.2+# # save_bib -- Saves the bibliography (uses tk_getSaveFile widget # to get the file name to save under). proc save_bib { } { global types init_bib if { $init_bib == "" } { set fn [tk_getSaveFile -filetypes $types] } else { set fn $init_bib } if {[string compare $fn ""] == 0} { return } write_bib $fn } #CLIP+bracketCount+TEXT+542+2000/01/29 12:13:30+17.0+# # pgb proc bracketCount str { set count 0 set index 0 set s $str while { [set index [string first \{ $s]] >= 0 } { # puts "Found open char at $index" incr count incr index set s [string range $s $index end] } set index 0 set s $str while { [set index [string first \} $s]] >= 0 } { # puts "Found close char at $index" incr count -1 incr index set s [string range $s $index end] } return $count } # end pgb #CLIP+frames+TEXT+567+2000/02/18 19:44:14+78.0+# # frames -- Lots of frame building for the initial windows.... # Editing window does not initially exist... set exists 0 wm title . "tkbib 1.0" frame .mbar menubutton .mbar.file -text "File" -menu .mbar.file.m menu .mbar.file.m .mbar.file.m add command -label "Open" -command open_bib .mbar.file.m add command -label "Save" -command save_bib .mbar.file.m add command -label "Quit" -command {save_bib ; exit} pack .mbar.file -in .mbar -side left menubutton .mbar.about -text Help -menu .mbar.about.m menu .mbar.about.m .mbar.about.m add command -label "About TkBib" -command aboutDialog pack .mbar.file -in .mbar -side left pack .mbar.about -in .mbar -side right frame .f frame .nf foreach t $reftypes { button .nf.$t -text "New $t" -command "edit $t new_key" # Looks better to fill pack .nf.$t -fill x } toplevel .term # Not allowed to remove terminal window! wm protocol .term WM_DELETE_WINDOW { # No operations } wm title .term "tkbib Term Definitions" frame .term.ft listbox .term.ft.term_l -yscrollcommand ".term.ft.term_s set" scrollbar .term.ft.term_s -command ".term.ft.term_l yview" pack .term.ft.term_s -side right -fill y pack .term.ft.term_l -side left -fill both -expand 1 pack .term.ft -expand 1 -fill both frame .fc listbox .fc.key_l -yscrollcommand ".fc.key_s set" scrollbar .fc.key_s -command ".fc.key_l yview" pack .fc.key_l -in .fc -side left -expand 1 -fill both pack .fc.key_s -in .fc -side right -fill y pack .nf -in .f -side left pack .fc -in .f -side right -expand 1 -fill both # pack .fc -side right -expand 1 -fill both # pack .nf -side right tk_menuBar .mbar pack .mbar -side top -fill x pack .f -side bottom -expand 1 -fill both bind .fc.key_l "edit article \[selection get\]" open_search # If we have a cmd line arg, and this is a file, open it. if {$init_bib != ""} { parse_bib $init_bib } #CLIP+pre+TEXT+1+2000/01/29 11:59:04+39.6+#