Wednesday, April 08, 2026

Generate Graphvix DOT Files from Gnocl Widget Heirarchies

The innards of complex GUI layouts are often difficult to understand. The newly added gnocl::widgetTree will generate a txt visualization of the GUI layout. These text files can then be converted to Graphvix .DOT files from which a png image be generated.  

 

# tree_to_dot.tcl 
#!/bin/sh
# the next line restarts using tclsh \
exec tclsh "$0" "$@"

#REF: tree_to_graphviz_custom_out.tcl
# Description: Converts GTK tree to DOT with stacked labels and a custom -o output option.

proc parse_tree_to_dot {input_text} {
    set lines [split [string trim $input_text] "\n"]
    set dot_output "digraph GTK_Tree \{\n"
    append dot_output "    node \[shape=box, fontname=\"Helvetica\", fontsize=10, style=\"rounded,filled\", fillcolor=\"white\"\];\n"
    append dot_output "    rankdir=LR;\n"

    array set last_node_at_level {}
    set node_counter 0

    foreach line $lines {
        if {![regexp -indices {[\w\(]} $line match]} continue
        set start_idx [lindex $match 0]
        
        set full_label [string trim [string range $line $start_idx end]]
        
        # Regex to separate Class and Name/ID for stacked labeling
        if {[regexp {^([^\s\(]+)\s*(.*)$} $full_label -> class extra]} {
            set display_label [expr {$extra ne "" ? "$class\n$extra" : $class}]
        } else {
            set display_label $full_label
        }

        set safe_label [string map {\" {\"}} $display_label]
        set current_id "node_[incr node_counter]"
        append dot_output "    $current_id \[label=\"$safe_label\"\];\n"

        set current_depth $start_idx
        set parent_id ""
        set levels [lsort -integer -decreasing [array names last_node_at_level]]
        
        foreach lvl $levels {
            if {$lvl < $current_depth} {
                set parent_id $last_node_at_level($lvl)
                break
            }
        }

        if {$parent_id ne ""} {
            append dot_output "    $parent_id -> $current_id;\n"
        }

        set last_node_at_level($current_depth) $current_id
        foreach lvl [array names last_node_at_level] {
            if {$lvl > $current_depth} { unset last_node_at_level($lvl) }
        }
    }
    append dot_output "\}"
    return $dot_output
}

# --- COMMAND LINE PROCESSING ---

set infile ""
set outfile "output.dot"

for {set i 0} {$i < [llength $argv]} {incr i} {
    set arg [lindex $argv $i]
    switch -exact -- $arg {
        "-o" {
            incr i
            set outfile [lindex $argv $i]
        }
        default {
            set infile $arg
        }
    }
}

# --- FINALIZATION & PREVIEW ---

if {$infile ne "" && [file exists $infile]} {
    set fp [open $infile r]
    set input_data [read $fp]
    close $fp
    
    set result [parse_tree_to_dot $input_data]
    
    set out_fp [open $outfile w]
    puts -nonewline $out_fp $result
    close $out_fp
    
    puts "Source: $infile"
    puts "Result: $outfile"
} else {
    puts "Usage: ./script.tcl <input_file> \[-o output_file\]"
}


Monday, February 09, 2026

Search Source Files for Matching Key Phrases

 
##
# search all sources for pattern string
# returns a tagged list, those matching, and those not-matching
##
proc checkAllSources { {pattern USE_DOC_VAR} } {

    package require fileutil
    
    set FILES [fileutil::findByPattern ../src *.c] 
    
    foreach w [gnocl::widgets] {
        foreach f $FILES { 
            if { [string first /$w.c $f] != -1 } { 
                set fp [open $f r]
                set DATA [read $fp]
                close $fp

                if { [string first $pattern $DATA] != -1 } { 
                    lappend yes $f 
                } else {
                    lappend no $f
                    
                }
            }     
        }
    }
    return "yes {$yes} no {$no}"
}

puts [join [dict get [checkAllSources gnoclGetDocumentationVarArgs] yes] \n]

Sunday, January 25, 2026

Using a Simple C While-Loop to Step Throught Option Arrays

Sometimes its necessary to run through a GnoclOption array. As such arrays are always NULL terminated, a simple while loop solves the problem.


gint idx = 0; 

while ( tagOptions[idx++].optName != NULL )  {

g_print ( "*%d %s\n", idx, tagOptions[idx].optName );

if ( strcmp ( tagOptions[idx].optName, "-myOption" ) ) == 0 ) {

GNOCL_DEBUG_MSG ( "got it!" )

break;

 }

 

As can be seem in the above example, a GnoclOption array is an array of structures. 

 

If just the array size is needed, then simply use the macro GNOCL_TOTAL_OPTIONS(option) to return the length, less the NULL, of course.

The macro ARRAY_SIZE(x) will provide the overall size of an array, including any NULL termination.