Wednesday, December 19, 2012

gnocl::cairo instropection

The gnocl::cairo command is not intended to be some form of canvas object, but a means of drawing onto images for display on screen used perhaps in the creation of widgets or images. Bearing in mind the relatively small number of actions required for this task, it's reasonable to assume that the overhead of making multiple calls to the gnocl::cairo command is a worthwhile compromise.

In response to the problem of how to get the extents of rendered text back from Cairo I came up with the solution highlighted in the following code:


set pb(1) [gnocl::pixBuf new -width 256 -height 256]
set myString "Peter Piper picked a peck of pickled peppers."
set actions "
    select_font_face {Sans normal bold}
    set_font_size 15.0
    text_extents $myString"  
array set extents [gnocl::cairo $pb(1) -actions $actions]


In the example above, the following values were obtained.

extents(height)    = 14.0
extents(width)     = 320.0
extents(x_advance) = 322.0
extents(x_bearing) = 1.0
extents(y_advance) = 0.0
extents(y_bearing) = -11.0



Tuesday, December 18, 2012

New Pacakge gnocl::cairo

The process of tidying up the development code prior to a release increment has led me to weeding out some unused or difficult to maintain functions. One of these was the cairo.c module. This was experimental block of code which was an offshoot of developing the gnocl::splashscreen widget.  But, with the transition to Gtk3 on the horizon, all drawing operations will be performed by Cairo and so I thought that it was a good opportunity to make use of some of that old code. Hence, a new package. There's still way to go before it's finished and I'm still working on drawing to pixbufs but things are looking good. Here's the syntax for the command:

gnocl::cairo <drawable-id> -actions <script>

Where the script is simply a list of cairo operations.  Here's a snippet from my test script.

set actions "
# {sample set-line-join}
set_line_width 40.96
move_to {76.8 84.48}
rel_line_to {51.2 -51.2}
rel_line_to {51.2 51.2}
set_line_join miter
stroke

move_to {76.8 161.28}
rel_line_to {51.2 -51.2}
rel_line_to {51.2 51.2}
set_line_join bevel
stroke

move_to {76.8 238.08}
rel_line_to {51.2 -51.2}
rel_line_to {51.2 51.2}
set_line_join round
stroke
"
set pb(1) [gnocl::pixBuf new -width 512 -height 512]
gnocl::cairo $pb(1) -actions "$actions #"


This gives:



There will be some limitations. It's not possible to create several surfaces in advance and then to use them as required. There is only one surface and will be necessary to change its contents. I don't envisage implementing any introspection either, keeping track of what's taking place on the Cairo side of things will need to be handled Tcl-side. One change that I did make was to allow for angles to be specified in degrees rather than radians. I could make this optional with a -useDegree switch but that can be dealt with later.

Here's a list of the Cairo operations implemented so far. The names are derived from the Cairo api for which these are wraps.

        "image_surface_create_from_png",
        "paint",
        "line_to",
        "set_line_cap",
        "set_line_join",
        "rel_line_to",
        "rectangle",
        "arc",
        "arc_negative",
        "curve_to",
        "rel_curve_to",
        "stroke",
        "stroke_preserve",
        "set_source_rgb",
        "set_source_rgba",
        "fill",
        "fill_preserve",
        "set_dash",
        "set_line_width",
        "move_to",
        "pattern_create_linear",
        "pattern_add_color_stop_rgb",
        "pattern_add_color_stop_rgba",
        "set_source",
        "set_source_surface",
        "surface_destroy",
        "pattern_destroy",
        "select_font_face",
        "font_size",
        "show_text",
        "new_path",
        "close_path",
        "clip",
        "new_sub_path",
        "set_fill_rule",
        "scale",
        "rotate",
        "translate",
        "#"

Tuesday, November 06, 2012

gnocl::button -align

Yesterday I was playing with the Glade interface designer and noticed that the UI included buttons with icons whose contents were left aligned. The Gnocl default is centred and I thought that a gnocl::button -align option might be useful. The button icon and label sub-widgets are in turn held within a GtkHBox and positioned by a GtkAlignment container. It was a relatively simple task to create the code to add the alignment option. For the present, plain text will remain centred. Here's my test script.

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

package require Gnocl

proc gnocl::buttonBox { {str <<TITLE>>} b} {
    set box(1) [gnocl::box -orientation vertical -padding 0 ]
    set but(1) [gnocl::button -icon %#Remove -text $str -align centre]
    set box(2) [gnocl::box -orientation vertical -padding 0 ]

    $box(1) add $but(1) -fill {1 1}
    $box(1) add $box(2)
    $box(2) add $b

    $box(1) configure -data "button $but(1) box %box(2)"
    $but(1) configure -data [::list state 1 child $box(2)]
    $but(1) configure -onClicked {
       
    foreach {a b} [%w cget -data] {    set tmp($a) $b     }
   
        if {  $tmp(state) == 1 } {
            %w configure -icon %#Add -align left
            set tmp(state) 0
        } else {
            %w configure -icon %#Remove -align right
            set tmp(state) 1
        }
       
        $tmp(child) configure -visible $tmp(state)
        %w configure -data [::list state $tmp(state) child $tmp(child)]
        }

    return $box(1)

}

set box1 [gnocl::box -orientation vertical]
foreach item {a b c d e} {
    set b1 [gnocl::button -text "Button $item" -onClicked "puts %w"]
    $box1 add $b1
}
set bb(1) [gnocl::buttonBox HELLO $box1]

#--------------------
set box2 [gnocl::box -orientation vertical]
foreach item {g h i j k} {
    set b2 [gnocl::button -text "Button $item" -onClicked "puts %w"]
    $box2 add $b2
}
set bb(2) [gnocl::buttonBox GOODBYE $box2]

#--------------------
set box3 [gnocl::box -orientation vertical]
foreach item {l m n o p} {
    set b3 [gnocl::button -text "Button $item" -onClicked "puts %w"]
    $box3 add $b3
}
set bb(3) [gnocl::buttonBox TATA $box3]

#--------------------
set box4 [gnocl::box -orientation vertical]
$box4 add $bb(1)
$box4 add $bb(2)
$box4 add $bb(3)

set scr [gnocl::scrolledWindow -child $box4]

set main [gnocl::window -child $scr -width 200 -height 400]
$main center


Sunday, November 04, 2012

Getting Closer to Gnocl 0.9.97

Over the past few months a lot of modifications have been made to the development code to warrant a release increment albeit just by 0.0.01! Before this happens, it would be useful to hear from any (?) Gnocl users out there who are using the package and what they expect to see. Foremost, some feedback from users working with glade/builder files would be useful. The code for loading the xml files is something of a load and it might be more suited to branching out into a separate Gnocl family package. What would also be useful is any bug or performance reports from users.

Many thanks...

gnocl::tooltip trigger

Did a little more to the demo script to show how the "trigger" sub-command actually works.

# test-tooltip.tcl
#! /bin/sh/
#\
exec tclsh "$0" "$@"
package require Gnocl

set lab(1) [gnocl::label -text "TANNOY" ]
set lab(2) [ gnocl::label -text "CAMPER 1" -tooltip "Hi-Di-Hi!!!" ]
set lab(3) [ gnocl::label -text "CAMPER 2" -tooltip "quiet" ]

set box [gnocl::box -orientation  vertical ]
$box add $lab(1) -expand 1 -fill {1 1}
$box add $lab(2) -expand 1 -fill {1 1}
$box add $lab(3) -expand 1 -fill {1 1}

set main [gnocl::window -title "Rise-n-Shine" -child $box -setSize 0.125]

#---------------
# create custom tooltip
#---------------
#
proc myToolTip {} {
   
    # create custom tooltip window
    set lab [gnocl::label -text "Ho-Di-Ho" -xPad 10]
    set im [gnocl::image -image %#About ]
    set box [gnocl::box -borderWidth 1 -shadow in]
   
    $box add $im
    $box add $lab

    # keep copy of component widget ids using -data option
    return [gnocl::window -visible 0 -type popup -child $box -backgroundColor #FBF998 -data "lab $lab im $im box $box" ]
}

set tip [myToolTip]
gnocl::tooltip $lab(3) -window $tip

# extract data and modify
eval [lindex [$tip cget -data] 1] configure -text \"Ho-Di-Ho!!!\"

# demonstrate the use of the trigger sub-command.
set i 0
proc update {} {
    global i
    global lab
        incr i
        $lab(1) configure -tooltip "Good Morning Campers -$i"
        gnocl::tooltip trigger
    after 1000 update
}

update

Saturday, November 03, 2012

Entry widget undo/redo in Tcl.

Hacked together a simple proof of method script to implement Undo/Redo functionality for the gnocl::entry widget. There are some improvements that can be done such as overloading the basic entry widget to allow undo/redo as a widget command. Keyboard bindings are fully there too. I'll take a look at these at a later time.



#---------------
# entryundo($ent)Redo.tcl
#---------------
# Created by William J Giddings
# October, 2012
#---------------
# Description:
# Demonstrates simple entry undo($ent)/redo functionality.
#---------------
# Notes:
# cd /Desktop/GnoclEdit_2/undo($ent)er/wjgstuff
#
# This version has a single buffer to show edit history
#
#---------------

# basic Tcl/Gnocl Script
#! /bin/sh/
#\
exec tclsh "$0" "$@"

package require Gnocl

#---------------
#
#---------------
#
proc entry:resetUndo {ent } {
    global undo idx
    set idx($ent) -1
    set undo($ent) ""
}

#---------------
#
#---------------
#
proc entry:undo {ent} {
    global idx undo
    incr idx($ent) -1
    if { $idx($ent) <= -1} {
        $ent configure -value ""
        set idx($ent) -1
        } else {
            $ent configure -value [lindex $undo($ent) $idx($ent)]
        }
    gnocl::update
}

#---------------
#
#---------------
#
proc entry:redo {ent} {
    global idx undo
    incr idx($ent)
    if { $idx($ent) >= [llength $undo($ent)] } { incr idx($ent) -1 }
    $ent configure -value [lindex $undo($ent) $idx($ent)]
    gnocl::update
}

#---------------
#
#---------------
#
proc entry:addUndo {ent} {
   
    global undo idx
   
    set undo($ent) ""
    set idx($ent) -1
   
    $ent configure -onKeyPress {
        if { "%s" == 20 && "%a" == "y" } { redo %w }
        if { "%s" == 20 && "%a" == "z" } { undo %w }
        set idx(%w) [llength $undo(%w)]
        incr idx(%w) -1
        }

    $ent configure -onKeyRelease {
        lappend undo(%w) [%w get]
        incr idx(%w)
        }

    $ent configure -onDestroy {
        puts "BYE BYE"
        array unset undo %w
        array unset idx %w
        }

}

set b(1) [gnocl::button -text Undo -onClicked { entry:undo $ent } ]
set b(2) [gnocl::button -text Redo -onClicked { entry:redo $ent } ]
set b(3) [gnocl::button -text Reset -onClicked { entry:resetUndo $ent }]
set ent [gnocl::entry]
set box [gnocl::box -orientation vertical]
$box add $b(1)
$box add $b(2)
$box add $b(3)
$box add $ent
gnocl::window -child $box

entry:addUndo $ent

Customized Tooltip Windows

Gtk allows the user to create custom tooltips which might include mixed items such as icons, images and text. The recent addition of the gnocl::tooltip command to allows such things to be done. The following test script shows how its done.

# test-tooltip.tcl
#! /bin/sh/
#\
exec tclsh "$0" "$@"
package require Gnocl


set lab(1) [ gnocl::label -text "LABEL 1" -tooltip "I'm label #1" ]
set lab(2) [ gnocl::label -text "LABEL 2" -tooltip "I'm label #2" ]
set box [gnocl::box]
$box add $lab(1) -expand 1 -fill {1 1}
$box add $lab(2) -expand 1 -fill {1 1}

set main [gnocl::window -title main -tooltip "HELLO WORLD!" -child $box -setSize 0.25]

#---------------
# create custom tooltip
#---------------
#
proc myToolTip {} {
   
    # create custom tooltip window
    set lab [gnocl::label -text HIDIHI -xPad 10]
    set im [gnocl::image -image %#About ]
    set box [gnocl::box -borderWidth 1 -shadow in]
   
    $box add $im
    $box add $lab

    # keep copy of component widget ids using -data option
    return [gnocl::window -visible 0 -type popup -child $box -backgroundColor #FBF998 -data "lab $lab im $im box $box" ]
}

set tip [myToolTip]
 
gnocl::tooltip $lab(2) -window $tip

eval [lindex [$tip cget -data] 1] configure -text HoDiHo!!!

Tuesday, September 18, 2012

Text Zooming

The widgets in the Tk package have the really nifty sub-command cget which enables the settings for widget options to be extracted. Implementation of cget functionality within Gnocl is patchy for the most part as in most cases custom code needs to be added to extract settings. I've just added cget -baseFont for the text widget. This means that simple text zooming can be implemented in conjunction with the -onScroll option.

#---------------
# text widget zoom
#---------------
# arguments
#    w    text widget id
#    d    direction of scroll, ie up or down.
#    s    state of keytboard modifiers, must be Ctrl+scroll to run.
# returns
#    nil
proc TextZoom {w d s} {
 
  if { [$w class] != "text" } { return -1 }
 
  set fnt "[$w cget -baseFont]"
 
  set fsize [lindex $fnt 1]

  if {$s != 4} { return }

  switch $d {
      up { incr fsize 2 }
      down {incr fsize -2 }
      }
 
  lset fnt 1 $fsize  
 
  eval $w configure -baseFont [list $fnt]
}

Wednesday, August 22, 2012

gnocl::text new option -variable

All of the standard entry widgets have a -variable option much like those encountered in Tk. This means that setting the value of a widget will cause a named variable to contain a copy of this data. The idea of using the -variable option lies in the ease of setting application wide settings or the setting or obtaining  values from a data entry form. The only exception to this was the text widget.

I've been working on a forms based dictionary and glossary edit library which needed something more than the standard gnocl::entry. After hacking around with Tcl scripts to gather data from a text widget I decided to work on the C-side of things. This required a little bit of tweaking of the C sourcecode which, as it would turn out, was an easier approach than scripting. It should be pointed out, however, that only plain unicode text will be copied. Any tags or invisible text will not be captured.

Here's the test script I used to work through the implementation.

#---------------
# test-textVar.tcl
#---------------
#!/bin/sh
#\
exec tclsh "$0" "$@"

package require Gnocl

set box [gnocl::box -orientation vertical]
 

set but [gnocl::button \
     -text Press \
     -onClicked { 
           puts $var
           set var "How now brown cow." }]

set txt [gnocl::text -variable var -baseFont {Sans 12} -wrapMode word]

$box add $but -fill {0.25 0} -align left
$box add $txt -fill {1 1} -expand 1

gnocl::window -child $box -setSize 0.25
set var "hello world!"

Tuesday, August 21, 2012

gnocl::label new option -resizable

If the label widget is used for well, labelling other widgets, the overall size of the label is pretty-well fixed and then only to a few number of words. But, the Gtk+ label is much more powerful than this. Because the widget is a container for pango text, its a really useful, high-speed alternative to the gnocl::text widget for the display of extensive pieces of text. Added to this, the use of pango markup, which is pretty well the same as a standard html string, makes run time formatting a something of a breeze.

Thursday, July 05, 2012

gnocl::label new options

Just added two new options to the label widget, -mnemonicText and -mnemonicWidget. Whilst these titles are straight from the GtkWidget api, perhaps they need renaming before adding to the distribution. Basically, these options allow fields to be accessed using quick keyboard routes. Something that has been lacking to date. Here's the script that I'm experimenting with:

#---------------
# test-mnemonic.tcl
#---------------
#!/bin/sh
#\
exec tclsh "$0" "$@"

package require Gnocl


proc labelEntry {str var} {
    upvar $var v
    set box [gnocl::box]
    set ent [gnocl::entry -sizeGroup a]
    set lab [gnocl::label -align right -widthGroup aa -mnemonicText $str -mnemonicWidget $ent]
    $box add $lab
    $box add $ent
    return $box
}

set str(1) PRAJNA
set str(2) KARUNA
set str(3) MAITRI

set box [gnocl::box -orientation vertical]

$box add [labelEntry _Prajna str(1)]
$box add [labelEntry _Karuna str(2)]
$box add [labelEntry _Maitri str(3)]

gnocl::window -child $box -setSize 0.125


And, the screenshot:




Tuesday, July 03, 2012

Setting tree/list column header widget

Just added a new option to the gnocl::tree/gnocl::list columnConfigure sub-command to allow embedded widgets.

#---------------
# test-columnWidget.tcl
#---------------
#!/bin/sh
#\
exec tclsh "$0" "$@"

package require Gnocl

set tree [gnocl::tree \
    -types {string integer} \
    -titles {"" "# of Legs"}]

$tree add {} {Mamals Birds Bugs}
$tree add 0 {{Cat 4} {Dog 4} {Human 2}}
 

foreach {paro pred} [$tree add 1 {Parrots Predator}] {}
 

$tree add $paro {{Cockatoo 2} {Amazone 2}}
$tree add $pred {{Hawk 2} {Eagle 2}}

$tree expand \
    -path "1" \
    -recursive 1

gnocl::window \
    -title "Tree" \
    -child $tree \
    -defaultWidth 300 \


set but(1) [gnocl::button -text HIDIHI]
$tree columnConfigure 1 -widget $but(1)

Wednesday, June 27, 2012

gnocl::text -- getSelectionBounds

Just added new command to the text widget functionality to retrieve the bounds of the text buffer selection. If there is no selection, then an empty string is returned, otherwise there is a list of four integers, in the form startRow startCol endRow endCol.

#---------------
# test-selectionBounds.tcl
#---------------
#!/bin/sh
#\
exec tclsh "$0" "$@"
package require Gnocl

set txt [gnocl::text -wrapMode word]
$txt configure -onButtonRelease {
    puts [%w getSelectionBounds]
    }

$txt lorem
   
gnocl::window -child $txt -setSize 0.25


Tuesday, June 12, 2012

gnocl::setStyle -new command

Gtk regards many widget properties as part of a system theme and are called 'style properties' and are not generally accessible via the Gtk+ api. The defaults for the widget can be over-ridden during run time and the loading of a resource file is an example of this. An alternative route, one in which only single changes are intended, can be taken using the new gnocl::setStyle command. This takes three arguments, the specific widget name, the style property name and the value to be assigned.  By default the widget names are NULL strings and, at present, can only be set following widget creation. One useful trick is to name the widget with its own gnocl id. The following script and screenshot show the command at work.


#---------------
# test-style.tcl
#---------------
#!/bin/sh
#\
exec tclsh "$0" "$@"

package require Gnocl

set ent(1) [gnocl::entry -name b1 -value "Gnocl"]
set ent(2) [gnocl::entry -name b2 -value "Gnocl"]
set ent(3) [gnocl::entry -name b3 -value "Gnocl"]

# use gnocl id as name
$ent(3) configure -name $ent(3)

puts "1 >>>[$ent(1) cget -name]"
puts "2 >>>[$ent(2) cget -name]"
puts "3 >>>[$ent(3) cget -name]"

gnocl::setStyle b1 {fg[ACTIVE]} "#FF0000"
gnocl::setStyle b1 font_name "Sans Bold 14"
gnocl::setStyle b1 {base[NORMAL]} "#FFFF00"
gnocl::setStyle b1 {text[NORMAL]} "#FF0000"
gnocl::setStyle b1 {bg[SELECTED]} "#FF0000"

gnocl::setStyle b2 font_name "Courier 14"
gnocl::setStyle b2 {text[NORMAL]} "#00FF00"
gnocl::setStyle b2 {base[NORMAL]} "#000000"

gnocl::setStyle $ent(3) {base[NORMAL]} "#0000FF"
gnocl::setStyle $ent(3) {text[NORMAL]} "#FFFFFF"

set box [gnocl::box -orientation vertical]
$box add $ent(1)
$box add $ent(2)
$box add $ent(3)

gnocl::window -child $box






Sunday, June 10, 2012

gnocl::progress -visible * NOW WORKING

In the Gtk2+ Libs the "visible" property in GtkProgress widget is not implemented. In order todo this the commands gtk_widget_show and  gtk_widget_hide have to be called. Hitherto it was possible to set the gnocl::progresssBar -visible option but it would have no effect. This has now been remedied. It will respond as expected.



Sunday, May 20, 2012

Working with application preferences using gnocl::keyFile

Its easy enough to load/save script settings as some form of serialization on an array, but what if we want to conform to Gtk+ standards and save settings as an *.ini file? The following script shows how this can be done.



#---------------
# test-keyfile.tcl
#---------------
# William J Giddings, 19/05/2012
#---------------

#!/bin/sh
#\
exec tclsh "$0" "$@"

package require Gnocl

#---------------
# manipulate application preferences
#---------------
# preferences stored in global two-dimensional array name "prefs"
# array names are equivalent to the group/key pairings required for keyFile
#---------------
# args
#    cmd        one of load, edit, save
#    keyfile    name of keyfile to load/save, ignored for edit
# returns
#   none
#---------------
proc preferences {cmd {keyFile {} } } {

 global prefs

 switch $cmd {

    load {

        set kf [gnocl::keyFile load $keyFile]
        set prefs(comment) [$kf get comment]
       
        foreach group [$kf get groups] {
            foreach key [$kf get keys -group $group] {
                # assign each value
                set prefs($group,$key) [$kf get value -group $group -key $key]
                }
            }
        }

    edit {
       
        # make a copy of the prefs, might need undoing
        global _prefs
        foreach item [array names prefs] { set _prefs($item) $prefs($item) }

        # dialog response proc
        proc _responseProc {v} {
            global prefs _prefs
            switch $v {
                No {
                    foreach item [array names _prefs] { set prefs($item) $_prefs($item) }
                }
            }
            array unset _prefs
        } ;# _responseProc
       
        # preferences edit gui
        set box [gnocl::box -orientation vertical]
        set notebook [gnocl::notebook]
        set lgroup ""
        foreach item [lsort [array names prefs]] {
            if {$item == "comment"} { continue }
            foreach {group key} [split $item ,] {
                if { $group != $lgroup } {
                    set table($group) [gnocl::table -homogeneous 0]
                    $notebook addPage $table($group) $group
                }
                $table($group) addRow \
                    [list [gnocl::label -text $key -align right -maxWidthChars 10 -widthGroup 1] \
                          [gnocl::entry -variable prefs($group,$key) -data $prefs($group,$key)] ] \
                    -fill 1 -expand {0 0}
                set lgroup $group
            }
        }
        gnocl::dialog \
            -child $notebook \
            -buttons "{{%_C_ancel} %#No} {{%_O_kay} %#Yes}" \
            -onResponse "_responseProc %v"
        }

    save {
        set kf [gnocl::keyFile new]
        $kf set comment -value $prefs(comment)
        foreach item [array names prefs] {
            foreach {group key} [split $item ,] {
                $kf set string -group $group -key $key -value $prefs($item)
            }
        }
        $kf write $keyFile
        $kf free
        }
 } ;# end switch
   
}


if {1} {
    # create as sample .ini file
    set fp [open "sample_config.ini" w]
    puts $fp {
#---------------
# sample_config.ini
#---------------
# this is just an example
# there can be comments before the first group

[Batch]
base=/
path=

[Crop]
top=0
bottom=100
center=50
sides=100

[Scan]
type=tif
resolution=400
threshold=128
mode=binary
format=tiff
x=210
y=297
l=0
t=0
i=0
prefix=img-
last=0

[Page]
width=2480
height=3508
}

    close $fp

    preferences load sample_config.ini
    preferences edit

    parray prefs

    set prefs(comment) "---------------\n\tmy new tmp.ini\n---------------"
    preferences save tmp.ini
}

Thursday, May 03, 2012

pango markup functionality

The arrangement for creating markup strings from the contents of text widget is almost complete. I've just got to add some extra toolbar buttons for the remaining settings and then the job will be done! Here's a screenshot.



Wednesday, May 02, 2012

gnocl::toolbar cget

Did a little more work on the gnocl::toolbar to implement the cget function. Not fully implemented as any cget implementation needs a lots of wrapping to get the job done! All changes posted to SF in today's NB.


Tuesday, May 01, 2012

gnocl::text pango markup

Getting closer towards some working code. I've expanded the list of automatically created pango style text tags to include those which are possible using the GtkTextView widget. One nice feature in pango, ie using unique strikethrough colours isn't available as a text tag attribute. The fg/bg colours are restricted at the moment to red, green, blue, cyan, magenta, yellow, black, gray and white. Apart from the default theme font, the other two settings are serif and sans. Setting unique values for these would not be an impossibility, but these can be created on the fly as normal text tags in a Tcl script.
 

Sunday, April 29, 2012

Obtaining Pango Markup Strings -80% done!

Got this Tcl formatting pango markup strings based upon predefined tags in a gnocl::text widget. At the moment a core set of tags is intialized by setting the -markupTags option to '1'. Like many instances of machine produced code the markup strings are lengthy! But it works. When I have the time I'll look at implementing a C version of the proc an incorporating it into the gnocl sources. At that point it might be worth considering the creation of a new gnocl widget for the task as a separate package.

#---------------
# obtain markup strings from gnocl::text object
#---------------
# args:
#    txt    gnocl text widget containing rich text
# return
#    string with suitable pango markups
# notes:
#    initialize preset markup tags in widget using the -markupTags 1 option
proc gnocl:text_pango {txt} {
    set str ""
    for {set line 0} {$line < [$txt getLineCount]} {incr line} {
        set char 0
        while  {$char < [$txt getLineLength $line]} {

            # get ch at specific line position
            set ch [ $txt get [::list $line $char] [::list $line [expr $char+1] ] ]

            # process active tags at this position in the line
            set tagON ""
            set tagOFF ""

            # get tags as a list
            set tags [$txt tag get [list $line $char]]
            foreach tag $tags {
                append tagON $tag
                if {[string first span $tag] != -1} {
                    lappend tagOFF </span>
                } else {
                    lappend tagOFF [string map [list < </] $tag ]
                }
            }

            # reverse the tagOff list, remove whitepaces
            set tagOFF [lreverse $tagOFF]
            set tagOFF [string map [list { } {}] $tagOFF]
           
            # add characers and markup to output string
            append str $tagON $ch $tagOFF
            incr char
        } ;# end while
     } ;# end for

    return $str
}

Saturday, April 28, 2012

Working on Tcl script to convert gnoc::text tags into pango markup strings.

Getting working pango strings from text widget contents is not a simple issue, the various Gtk/Gnome programming sites contain many references to the possibility but.. Ok, the purposes are different - pango is only for marking up short strings. I want, however to edit pango formatted gnocl::list/tree strings in my text editor. At the moment my solution is working, albeit with rather long, messy mark-up sequences. Why? Pango doesn't allow tags to be overlapped. For example:

tag1On asdasd tag2On asdasdad tag1Off sasdasd tag2Off

Would produce an error. This would have to be:

tag1On asdasd tag2On asdasdad tag2Off tag1Off tag2On sasdasd tag2Off

So far my solution has been to create markups for each character in a string. The next step is simplifying that string.


Sunday, April 22, 2012

Substring markup

I find the Tcl string map subcommand very useful and use it quite extensively in text manipulation. Here's an example of where I wanted to highlight substrings within a piece of text using pango markup.

#---------------
# test-substring-markup.tcl
#---------------
#!/bin/sh
#\
exec tclsh "$0" "$@"
package require Gnocl

#---------------
# search for str2 in str1, then add pango markup formatting
#---------------
proc markup_substring {str1 str2 format} {
    # add necessary quote marks around property values
    set format [string map [list = =\" { } {" }] "$format "]
    set str3 "<span $format>$str2</span>" ;#"
    return [string map [list $str2 $str3] $str1 ]
}


set lab [gnocl::label]
gnocl::window -child $lab -setSize 0.25

# set string text
set str "how now brown cow"

# markup first word
set str [markup_substring $str brown "foreground=red weight=bold background=yellow"]

# cascaded markup is possible but, problems will happen if the search string matches the markup statements.
set str [markup_substring $str cow "foreground=white underline=double background=black"]

$lab configure -text $str




Friday, April 20, 2012

Removed imageviewer reference in base distribution.

Whilst the GtkImageViewer widget is a pretty nice tool to use, its not built from default Gtk libraries and requires extra dependencies to be installed before successful compilation can be obtained. With this in mind, the gnocl::imageViewer command has been branched off into a package in its own right. This should enable most users to recompile with the minimal amount of fuss.

Removing widgets from gnocl::box containers

Hitherto it has been possible to add widgets to a box but not to remove them, perhaps for repacking or hiding. The addition of the remove command to the gnocl::box object will now allow this to be done.  Here's a demo script:

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

package require Gnocl

set title "Shared Buffer"

set tb [gnocl::textBuffer]
set tv(1) [gnocl::text -wrapMode word -buffer $tb]
set tv(2) [gnocl::text -wrapMode word -baseColor #FDFDB3 -buffer $tb]

$tv(1) lorem

# create maincontainer
set box(main) [gnocl::box \
    -orientation vertical \
    -align topLeft]
   
set box(texts) [gnocl::box \
    -orientation vertical \
    -children [list $tv(1)] \
    -fill {1 1} \
    -expand 1]

 # create toolBar
 set split 0
 set toolBar [gnocl::toolBar]
 $toolBar add widget [gnocl::toggleButton \
    -variable split \
    -text "Split" \
    -onToggled {
        if {$split} {
            $box(texts) add $tv(2) -fill {1 1} -expand 1
        } else {
        $box(texts) remove $tv(2)
        } }]

 # pack the main container
 $box(main) add $toolBar -fill {1 0} -expand 0
 $box(main) add $box(texts) -fill {1 1} -expand 1

 set main [gnocl::window \
    -title $title \
    -onDelete { exit } \
    -child $box(main) \
    -defaultWidth 480 \
    -defaultHeight 320]

gnocl::mainLoop



Monday, April 16, 2012

gnocl::menuItem: new option -data and new sub-command cget

I'm working on a script that requires popup menus to be built proceedurall and on-the-fly. As a result, there are a number of parameters that I want to send to the popup callback but without the expense of creating and managing global variables or namespaces. This is where the widget -data option comes in. It means that data strings can be conveniently grouped together with a widget, managed throught the Gtk libraries which is totally (hopefully!) secure. The following code-snipetts shows how it can be used.
 
#---------------
## display popup menu
#---------------
#\code
proc doTagPopup {args} {
   
    gnocl::setOpts [list a 1 b 2 c 3]
    gnocl::setOpts $args
   
    puts "a= $a b= $b c= $c"
    puts "w = $w ; t = $t ; n =$n"
   
    foreach {arg val} $args { puts "$arg = [set [string trimleft $arg -]]" }

    #set matches OM|MANI|PADME|HUM|HRIH
    if {$t == "buttonPress" && $b == "3" } {
       
        # these may need runtime re-definition
        proc doItem { w } {
            gnocl::setOpts [$w cget -data]
            puts ">[$widget tag ranges $tag]<"
            foreach {a b} [$widget tag properties $tag] {
                puts "\t$a = $b"
                }
            }
       
        set menu [gnocl::menu -title "menu" -tearoff 0]
       
        foreach word [split $matches |] {
            $menu add [gnocl::menuItem \
                -text $word \
                -data "-widget $w -word $word -tag $n" \
                -onClicked { doItem %w }  ]
            }
        $menu popup $x $y   
    }   
}
#\endcode

Monday, April 09, 2012

Talk to me Linux!

Just hacked together a simple proc to read text strings aloud using the espeak Linux command. Its good fun. It also shows how useful the gnocl::setOpts command  can be.

#---------------
# espeak.tcl
#---------------
## \file
# File documentation.
#\verbatim
#!/bin/sh
#\
exec tclsh "$0" "$@"
#\endverbatim

package require Gnocl

##
# -f <text file>
#       Text file to speak
# -a <integer>
#       Amplitude, 0 to 200, default is 100
# -g <integer>
#       Word gap. Pause between words, units of 10mS at the default speed
# -l <integer>
#       Line length. If not zero (which is the default), consider
#       lines less than this length as end-of-clause
# -p <integer>
#       Pitch adjustment, 0 to 99, default is 50
# -s <integer>
#       Speed in words per minute, 80 to 390, default is 170
# -v <voice name>
#       Use voice file of this name from espeak-data/voices
# -w <wave file name>
#       Write output to this WAV file, rather than speaking it directly
# -b   Input text encoding, 1=UTF8, 2=8 bit, 4=16 bit
# -m   Interpret SSML markup, and ignore other < > tags
# -q   Quiet, don't produce any speech (may be useful with -x)
# -x   Write phoneme mnemonics to stdout
# -X   Write phonemes mnemonics and translation trace to stdout
# -z   No final sentence pause at the end of the text
proc readAloud {args} {
    # set defaults and parse args
    gnocl::setOpts "-a 5 -s 110 $args"
    eval exec espeak -a $a -s $s [list $t]
}

readAloud -t "How now brown cow. She sell sea shells by the sea shore. Peter Piper picked a peck of pickled peppers."

Friday, April 06, 2012

Wednesday, March 28, 2012

gnocl::setOpts -new command

Sometimes I want to set a block of variables within procedure either as a set of defaults or as variables passed to a proc as options. Hitherto I've used a tcl proc to do this work but thought it high time to add this utility to the gnocl core. Ok, its not strictly graphics but it does make writing event handlers easier. Take the following for example:

doTagPopup -w %w -t %t -n %n -b %b -x %X -y %Y -matches OM|MANI|PADME|HUM|HRIH

The -w options can be used to create local variables. Here's a test snippet:


proc test-setOpts {args} { 
    gnocl::setOpts $args
    puts "$x $y $z"
 
    gnocl::setOpts [list j 4 k 5 l 6]
    puts "$j $k $l"
}

test-setOpts -x 1 -y 2 -z 3

In the first call the args string is parsed and values set en bloc. The leading '-' will be stripped away. In the second example, a paired list is given from which values will be set.


Tuesday, March 27, 2012

Getting text tag ranges

Completed code for returning a list of the ranges of a text tag within a gnocl::text widget.

widgetId tag ranges tagName


Implmenting text tag popup menus

Here's a short script to show how to get popup menus appearing for text tag objects.

#---------------
# texttag-popup.tcl
#---------------
#!/bin/sh
#\
exec tclsh "$0" "$@"

package require Gnocl

set txt [gnocl::text -wrapMode word]

gnocl::window -child $txt -setSize 0.25

#---------------
# display popup menu
#---------------
proc doTagPopup {args} {
   
    foreach {arg val} $args { set [string trimleft $arg "-"] $val }

    if {$t == "buttonPress" && $b == "3" } {
       
        # these may need runtime re-definition
        proc doHello {} {puts hello}
        proc doGoodBye {} {puts goodbye}
       
        set menu [gnocl::menu -title "menu" -tearoff 0]
        $menu add [gnocl::menuItem -text "Hello" -onClicked doHello]
        $menu add [gnocl::menuItem -text "Goodbye" -onClicked doGoodBye]
        $menu popup $x $y
       
        #proc doHello {} {}
        #proc doGoodBye {} {}       
    }
   
}


$txt tag create tag_popup -onEvent {doTagPopup -t %t -b %b -x %X -y %Y } -underline single  -foreground blue

$txt lorem

$txt tag apply {1 0} "1 20" -tags tag_popup

Sunday, March 11, 2012

Another simple error fixed -gnocl::dialog

The gnocl::dialog command has been throwing out a 'message-type' warning. The cause of this has now been removed.


Thursday, February 09, 2012

Friday, January 13, 2012

gnocl::dialog

Made some enhancements here today.  Added the Gtk "other" option  to the range of dialog types, i.e. no icon. But more significantly, implemented automatic setting of type to "other" if the -child option set.

Hiitherto, the gnocl::dialog was simply a means of giving yes/no feedback. Now, however, it's more useful for the setting of parameters and other forms of settings.

Here's a snippet from my current coding project which requires export to Abiword format. The following proc will build a dialog for the setting of vars. The code is not complete, but shows how the gnocl::dialog enhancements are working.

#---------------
# set file export properties
#---------------
# Args
#
# Return
#---------------
proc abi:properties {} {
    global metaData
    global pageSetup
   
    set nb [gnocl::notebook]

    # file metadata
    set box(1) [gnocl::box -orientation vertical]
    foreach name [lsort [array names metaData]] {
        $box(1) add [gnocl::labelEntry \
            -labelWidthChars 15 \
            -align left \
            -text [string totitle $name] ] \
            -fill {1 1} -expand 0
    }

    # page setup
    set box(2) [gnocl::box -orientation vertical]
    foreach name [lsort [array names pageSetup]] {
        $box(2) add [gnocl::labelEntry \
            -labelWidthChars 15 \
            -align left \
            -text [string totitle $name] ] \
            -fill {1 1} -expand 0
    }

    $nb addPage $box(1) "%__MetaData"
    $nb addPage $box(2) "%__Page Setup"

    set dlg [gnocl::dialog \
            -title "Abiword Export Properties" \
            -child $nb \
            -buttons "%#No  %#Yes" \
            -onResponse {
                puts %v
                } ]
}

Saturday, January 07, 2012

gnocl::text serialization

The GTK libs include a means of serlizaing/deserializing the contents of a GtkTextBuffer but the documentation on how to craft such handlers is basically non-existent. There is one package OSXCART which will load save a buffer as an rtf which is often discussed but the process of building such handlers is cumbersome and need to be built into the gnocl sources themselves. The whole process is more speedily handled on the Tcl side using the various tag sub-commands which will provide all the necessary information. How does the process work? Each row and column of the text is parsed firsly for changes in tag states (ie on or off) and the for the text content. If a new tag is added, its name is kept in a list of applied tags. The text is then accumulated until a further tag state change occurs whereupon firstly the tag and then text changes are written an output string. The text accumulator is then reset to empty and so the process continues. If a tag is turned off, the reverse occurs; the text is first written followed by the names of tags turned off.

The following module contains two procs, serealize and desearialize. Only tags are supported at the moment but, if necessary, marks, images and widgets could be supported too.

#---------------
# textSerialize.tcl
#---------------

#---------------
# deserialize utf8 text string and insert into widget
#---------------
# Arguments
#    data    text to deserialze
#    w       
# Returns
#    serialized text string
# Notes    
proc gnocl::textDeserialize {data txt} {
    set onTAGS {}
   
    foreach {a b} $data {
        if {$a == "tags"} {
            # apply taglist to new widget
            foreach c $b { eval "$txt tag create $c" }
        }
        if {$a == "text"} {
            foreach {c d} $b {
                switch $c {
                    tagOn {
                        lappend onTAGS $d
                    }
                    tagOff {
                        set i [lsearch $onTAGS $d]
                        set onTAGS [lreplace $onTAGS $i $i] ;# remove tag from list
                    }
                    text {
                        if {$onTAGS != {}} {
                            $txt insert end $d -tags $onTAGS
                        } else {
                            $txt insert end $d
                        }
                    }
                } ;# end switch
            } ;# end foreach
        } ;# end if
    }
}

#---------------
# obtain text contents from text widget and return as a serialized utf8 string
#---------------
# Arguments
#    txt        gnoc::text widget whose contents are to serialized
# Returns
#    serialized text in the form of a paired list of op-val
# Notes
#    tagOn tag1 text {abc} tagOff tag1 text {def}    
#---------------
proc gnocl::textSerialize {txt} {
    set str(1) {}    ;# output string, ie tags + text
    set str(2) {}    ;# the text itself
    set onTAGS {}    ;# active tage
    set tagOn {}
    set tagOff {}
   
    # create tag table
    foreach {a}  [$txt tag names ] { lappend ttable "$a [$txt tag properties $a]" }
   
    # parse the text to obtain tag changes before text content     
    for {set r 0} { $r < [$txt getLineCount ] } {incr r} {
       
        for {set c 0} { $c <= [$txt getLineLength $r ] } {incr c} {
                   
            if {$c == 0} {
                set tagOn  [$txt tag get [::list $r [expr $c -1]] -on  ]
            } else {
                set tagOn  [$txt tag get [::list $r $c ] -on  ]
                set tagOff [$txt tag get [::list $r $c ] -off ]
            }
           
            # handle tagOff       
            if {$tagOff != ""} {
                foreach t $tagOff {
                    set i [lsearch $onTAGS $t]
                    set onTAGS [lreplace $onTAGS $i $i] ;# remove tag from list
                    append str(1) "text [::list $str(2)] tagOff $t "
                    set str(2) ""
                }
            }

            # handle tagOn
            if { $tagOn != "" } {
                foreach t $tagOn {
                    # check for new tag state changes
                    if { [lsearch $onTAGS $t] == -1} {
                        # first item is a special case
                        if {$r == 0 && $c == 0} {
                            set str(1) "tagOn $t "
                            set onTAGS $t
                            continue
                        }
                        #--------------
                        append str(1) "text [::list $str(2)] "
                        set str(2) ""
                        #--------------                       
                        append str(1) "tagOn $t "
                        lappend onTAGS $t
                    }
                }           
            }
           
            # get text
            append str(2) [$txt get [::list $r $c ] [::list $r [expr $c+1] ] ]
        }
    }
   
    if {$str(2) != {}} {
        append str(1) "txt [::list $str(2)] "
    }
   
    return "tags [::list $ttable]\ntext [::list [string trim $str(1)]]"
}








Sunday, January 01, 2012

Release 0.9.96

Just uploaded the latest stable release to Sourceforge. It been some time since the last major release and a of enhancements have been made. For me the next milestone was implementing the recentFiles functionality available through Gtk+. This is working, although a lot more options and commands need to be implemented for the gnocl::recentManager widget. Having said that, though, I can see that bulk of the functionality would only be rarely used.

Regular readers of this blog may have noticed that the posts have reduced over the past few months. This isn't due to any loss of interest in Gnocl, far from it! I've also been working on my own translation tools which, I have to say, is the basic fuel which keeps the development work going.

Over the past four years or so that I've been working on Gnocl an awful lot of code has been added to the sources. Code that not only provide Tcl access to Gtk resources but provides a whole lot of advanced features which makes coding a breeze.

Happy 2012!!!