Monday, October 06, 2025

Convert Serialized GtkTextBuffer contents to ASCII

 The contents of a GtkTextBuffer can be saved as serialized binary file. Whilst converting such a file to ascii may be problematic if the buffer contains binary image data, saving text can be achieved quite simply as follows:

 

$txt save myfile.ser 

set file [open "myfile.ser" rb]
set DATA [read $file]
close $file

set fp [open myfile.txt w]
puts $header
set i 0
foreach line [split $DATA \n] {
    if {$i == 0} {
        puts $fp ":GTKTEXTBUFFERCONTENTS-0001 <text_view_markup>"
        set i 1
        continue}
    puts $fp $line
}
close $fp

 

Monday, September 29, 2025

Creating Tagged Lists From Separate Key - Value Lists

Various Tcl and Gnocl operations will return a list of values. At times, however, it is useful to preserve the lists of values as a tagged list; in other words, as a dict entry. Assuming that the returned value names are keys, the Tcl format command command can be used to complete the operation. For example:

 

set main [gnocl::window -setSize 0.25]

puts [format "x %s y %s width %s height %s" {*}[$main geometry]] 

 

gives:

x 0 y 0 width 480 height 270

Alternatively, a proc can be written using the foreach command:

 

proc tagList {tagnames values} {
    foreach tag $tagnames val $values {
        lappend buff $tag $val
    
    return $buff
}    

Sunday, June 15, 2025

Parsing Unstructed Option Value String

In using a Tcl script as a command line call, being able to parse the argument passed to the script is vital. The following snippet shows how to retrieve and assign such values.

line 2

line 3

 

# list of tested options
set opts "-a -b -c -d -e"

##
# parse a string of arguments
##
# arguments:
#    opts    - list of options not in dict format
#    args    - list of options-values to be parsed
# returns:
#    tagged list of option/values pairs as dict
#
proc parse {opts args} {

    set i 0
    foreach item $args {
        if { $item in $opts } { 
            set tmp [string trim $item -]
            incr i
        } else {
            lappend opt($tmp) $item
        }        
    }

    if { $i != [array size opt] } { 
        error -errorinfo "Error: unbalanced list in arguments."
    }

    return [array get opt]
}

The following line will result in an error as no value passed for option -e. The list element -5 will not be mistaken for an option as it is not a member of the valid options list.

parse $opts -a {1 2 3} -b a b c -c 4 -3 -d -5 -e 

In the above example option -a receives a braced list, that is its contents is a single member of a list. The following line is balanced:

parse $opts -a {1 2 3} -b a b c -c 4 -3 -d -5 -e f

After stripping initial dashes, the following tagged list is returned 

d -5 e f a {{1 2 3}} b {a b c} c {4 -3}

The unsorted ordering being the result of maintaining values in the parse procedure as an array, In practice, this will have no impact of the application of the code. 

Comparing Version Numbers

Sometimes it necessary to compare a current version number against a minimum requirement. This simple wrap around the package command shows how to do it. 

VERSION NUMBERS

Version numbers consist of one or more decimal numbers separated by dots, such as 2 or 1.162 or 3.1.13.1. The first number is called the major version number. Larger numbers correspond to later versions of a package, with leftmost numbers having greater significance. For example, version 2.1 is later than 1.3 and version 3.4.6 is later than 3.3.5. Missing fields are equivalent to zeroes: version 1.3 is the same as version 1.3.0 and 1.3.0.0, so it is earlier than 1.3.1 or 1.3.0.2. A later version number is assumed to be upwards compatible with an earlier version number as long as both versions have the same major version number. For example, Tcl scripts written for version 2.3 of a package should work unchanged under versions 2.3.2, 2.4, and 2.5.1. Changes in the major version number signify incompatible changes: if code is written to use version 2.1 of a package, it is not guaranteed to work unmodified with either version 1.7.3 or version 3.1.


 

# https://wiki.tcl-lang.org/page/package+vsatisfies
proc version {ver min} {

    package vcompare $ver $min
    #package vsatisfies $ver $min

}

puts [version 3.22.1 3.23.3] ;# -1 (earlier)
puts [version 3.24.1 3.23.3] ;#  1 (later)
puts [version 3.23.3 3.23.3] ;#  0 (equal)
puts [version 4.33.5 3.23.3] ;#  1 (later)
puts [version 4.23.5 4.34.3] ;# -1 (earlier)

Wednesday, April 23, 2025

Remove Duplicate Words from a String

 

The lmap command will loop over each item (x) in the list (str) and if it 

 

 proc removeDuplicates {str} {
    set res ""

    lmap x $str { if {$x ni $res} {lappend res $x} }

    return $res
}

Is it possible to open an Ubuntu app from HTML?

An excellent description on how to launch Linux applications from within a HTML document.

  https://askubuntu.com/questions/330937/is-it-possible-to-open-an-ubuntu-app-from-html

1. Create application launcher script

  • In a terminal run:

    mkdir -p bin
    

    This command will make a bin directory in your home folder if you don't already have it.

  • After run:

    gedit ~/bin/open_app.sh
    

    This will create the new file open_app.sh in gedit.

  • Copy and paste the following script in the new created file:

    #!/bin/bash
    
    if [[ "$1" != "app://" ]]; then 
        app=${1#app://}
        nohup "$app" &>/dev/null &
    else 
        nohup gnome-terminal &>/dev/null &
    fi
    
  • Save the file and close it.

  • Go back into terminal and run:

    chmod +x ~/bin/open_app.sh
    

    to grant execute access for the script.

2. Create .desktop file for application launcher

Now you must create a .desktop launcher for the above script, and tell Ubuntu to use this launcher as app:// protocol handler. Create /usr/share/applications/appurl.desktop file using the following command:

sudo -H gedit /usr/share/applications/appurl.desktop

and add the following content:

[Desktop Entry]
Name=TerminalURL
Exec=/home/radu/bin/open_app.sh %u
Type=Application
NoDisplay=true
Categories=System;
MimeType=x-scheme-handler/app;

Save the file and close it.

3. Refresh mime types database

In the file above, the line MimeType=x-scheme-handler/app; register app:// scheme handler, but to make it work we should update mime types database cache by executing command:

sudo update-desktop-database 

4. Test from terminal

Now everything should work. To test that it works from terminal, run for example this command:

xdg-open 'app://gedit'

4. Test from browser using HTML

You can test from browser by using for example the following HTML web page:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

<head>
    <title>Open some applications</title>
    <meta http-equiv="content-type" content="text/html;charset=utf-8" />
</head>

<body>
        <h3>Open some applications in Ubuntu from HTML</h3>
        <p>Open terminal: <a title="Open" href="app://">app://</a>
        (equivalent with: <a title="Open" href="app://gnome-terminal">app://gnome-terminal</a>)</p>
        <p>Open Nautilus: <a title="Open" href="app://nautilus">app://nautilus</a></p>
        <p>Open Chromium: <a title="Open" href="app://chromium-browser">app://chromium-browser</a></p>
        <p>Open Ubuntu Software Center: <a title="Open" href="app://software-center">app://software-center</a>
        (equivalent with: <a title="Open" href="apt://">apt://</a>)</p>
        <p>...and so on</p>
</body>

</html>

 

Thursday, November 07, 2024

Significant Re-write of how Gnocl Handle Commands

offset idx to accommodate point at which options occur 

idx = objc
no options
gnocl::device 

idx = 1
no subcommand but with options
gnocl::device -option AAA -option AAA... 

idx = 2
subcommand with options
gnocl::device cmdName -option AAA -option AAA... 

idx = 3
gnocl::device cmdName string -option AAA -option AAA... 

 

gnoclParseCommandOptions

 

/**
\brief    Command Template.
**/
int gnoclTemplateCmd ( ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj * const objv[] )
{
    static const char *description = "description";

    static GnoclCommand commands[] = {
        { "cmdName", "args", "description", "default" },
        { NULL }
    };

    static GnoclOption options[] = {
        /* command/widget specfic options */
        {
            "-option", GNOCL_STRING, NULL, NULL,
            "default",
            "description",
            GNOCL_OPTIONS_IMAGE
        },
        { NULL }
    };

    if ( gnoclGetDocumentation ( interp, objc, objv, description, commands, options ) == TCL_OK ) {
        return TCL_OK;
    }

    enum cmdIdx { CmdNameIdx };
    enum optIdx { OptIdx };
    
    gint idx;

    if ( gnoclParseCommandOptions ( interp, 2, objc, objv, options ) != TCL_OK ) {
        gnoclClearOptions ( options );
        return TCL_ERROR;
    }


    if ( Tcl_GetIndexFromObjStruct ( interp, objv[1], ( char ** ) &commands[0].cmdName, sizeof ( GnoclCommand ), "command", TCL_EXACT, &idx ) != TCL_OK ) {
        return TCL_ERROR;
    }

    switch ( idx ) {
        case CmdNameIdx:  {
                GNOCL_DEBUG_LINE
                if ( options[OptIdx].status == GNOCL_STATUS_CHANGED ) {
                    GNOCL_DEBUG_MSG( "YAY! It's working!!!" )
                    GNOCL_DEBUG_MSG (options[OptIdx].val.str)
                }
            }; break;

        case 1:  {
                GNOCL_DEBUG_LINE
            }; break;

        default: {
                GNOCL_DEBUG_LINE
            };
    }

    return TCL_OK;
}