set enabled_libraries { {pd-lib} {boids} {bsaylor} {creb} {cxc} {cyclone} {cyclone (hammer)} {cyclone (sickle)} {ekext} {fftease} {flatspace} {flib} {freeverb~} {gem} {ggee} {hardware} {hcs} {hid} {iem_ambi} {iem_bin_ambi} {iemgui} {iemlib} {jasch_lib} {jmmmp} {keyboardkeys} {la-kitchen} {list-abs} {mapping} {markex} {maxlib} {memento} {mjlib} {motex} {mrpeach} {nqpoly~} {nqpoly4} {nusmuk} {oscx} {pan} {pddp} {pdjimmies} {pdmtl abstractions} {pdogg} {pdp} {Percolate} {pidip} {pixeltango} {pmpd} {rradical} {rtc} {sigpack} {smlib} {toxy} {unauthorized} {vasp} {xsample} {zexy} {zexy Abstraction} } # comment the following line to enable all libraries set enabled_libraries {pd-lib} # top level categories (tags) set tlc {conversion pdmtl cyclone maxlib zexy vasp storage audio control connectivity imaging math misc} # second level (in menu) categories set c2 { connectivity {osc midi network} conversion {midi audio} cyclone {hammer sickle audio math control} zexy {audio analysis matrix control} maxlib {control time math glue} audio {abstraction conversion fftease cyclone math logical analysis filters delay effects tables} vasp {declaration arithmetic basics functions generators transcendent minmax utilities filters fft displace} storage {abstraction lists matrix tables} pdmtl {control convert list edit flow imaging} imaging {{gem particles} {gem manipulators} {gem pixes} {gem geos} {gem opengl} {pdp image} {pdp processing} {pdp abstraction} {pdp 3d} pidip manipulators wrapper particles automata processing} } #--------------------------------------------------------------------------------------------- # object -> tags mapping array set object_tags {} # tag reverse mapping array set objects_with_tag {} # load object -> tags mapping from file in Pd's path set testfile [file join $::current_plugin_loadpath object_tags.tcllist] if { [file isfile $testfile]} { set f [open $testfile] set tmp_db [read $f] close $f unset f } foreach {library object tags} $tmp_db { # skip unwanted libraries if {[lsearch -exact $enabled_libraries $library] == -1} {continue} foreach tag $tags {lappend object_tags($object) $tag} } unset tmp_db foreach k [array names object_tags] { set object_tags($k) [lsort $object_tags($k)] foreach tag $object_tags($k) {lappend objects_with_tag($tag) $k} } proc object_db_query {q workingset} { global object_tags set q _[join [lsort $q] _.*_]_ set result [list] foreach k $workingset { set v _[join $object_tags($k) __]_ if {[regexp $q $v]} {lappend result $k} } set result } # TODO: benchmark which is faster between the two proc object_db_query_re {q workingset} { global object_tags set q (?b)\\<[join [lsort $q] \\>.*\\<]\\> set result [list] foreach k $workingset { if {[regexp $q $object_tags($k)]} {lappend result $k} } set result } proc complement {e s} { set result [list] foreach i $e {if {[lsearch -exact $s $i] == -1} {lappend result $i}} set result } proc merge {args} { array set tmp {} foreach arg $args { foreach k $arg { set tmp($k) . } } set x [array names tmp] array unset tmp set x } proc pdtk_canvas_popup_addObjectBranch {t m lst} { set n 0 foreach {k v} $lst { if {$k == {.}} { incr n set cbrk 0 if {$n > 18} { set cbrk 1 set n 1 } $m add command -label $v -columnbreak $cbrk \ -command "pdsend \"\$::focused_window obj \$::popup_xcanvas \$::popup_ycanvas $v\"" } else { if {[llength $v] == 0} continue set sub ${m}.sub[incr ::s] menu $sub # fix menu font size on Windows with tk scaling = 1 if {$::windowingsystem eq "win32"} {$sub configure -font menufont} $m add cascade -label $k -menu $sub pdtk_canvas_popup_addObjectBranch $t $sub $v } } } proc print_r {l {indent 0}} { foreach {k v} $l { if {$k == "."} { for {set j 0} {$j < $indent} {incr j} {puts -nonewline " "} puts $v } else { for {set j 0} {$j < $indent} {incr j} {puts -nonewline " "} puts "$k {" print_r $v [expr {$indent+1}] for {set j 0} {$j < $indent} {incr j} {puts -nonewline " "} puts "}" } } } proc @ {l} { set result [list] set ls [lsort $l] foreach i $ls {lappend result . $i} set result } set l [list] set all [array names object_tags] # *partition* by tag into top-level-categories foreach c $tlc { set c_$c [object_db_query $c $all] set all [complement $all [set c_$c]] } set c_others $all # *search* by tag in 2nd-level-categories # add 2-level categories foreach {tlcn c2l} $c2 { set ll [list] set accum [list] foreach c2i $c2l { set lll [object_db_query $c2i [set c_$tlcn]] set accum [merge $accum $lll] if {[llength $lll] > 0} {lappend ll $c2i [@ $lll]} } set others [complement [set c_$tlcn] $accum] if {[llength $others] > 0} {lappend ll "others" [@ $others]} lappend l $tlcn $ll } # add 1-level-categories foreach tlci $tlc { set c2_keys [list] # can't use dict on 8.4 foreach {k v} $c2 {lappend c2_keys $k} if {[lsearch -exact $c2_keys $tlci] != -1} {continue} set c_1_set [object_db_query $tlci [set c_$tlci]] lappend l $tlci [@ $c_1_set] } # end menu structure builder #print_r $l .popup add separator set s 0 pdtk_canvas_popup_addObjectBranch - .popup $l