Friday, November 05, 2021

October News...

 2021-10:
gnocl::text
o insertImage command enhanced to follow the insert command for text, ie..
$wid insertImage <pos> <imagename> -tags <taglist>
Where image can take a standard image %-markup prefix
gnocl::pango
o new subcmds, markup2options, options2markup
attempts to convert a pango markup string <span> tag to a list of text attibutes e.g., 
gnocl::pango markup2option "<span foreground='red' background='blue'>"  
returns 
{-foreground "red" -background "blue"}
gnocl::pango option2markup {-foreground "red" -background "blue"} 
returns
"<span foreground='red' background='blue'>"
created strings.h header file, keep decls for project string utilites
gnocl::text
o tag 
  new sub command - return character tag properties as pango markup
  new options -underlineColor, -strikethroughColor, not used by widget,
but passed as a pango attribute.
gnocl::canvas
o new command clear, remove all items from canvas.
o new text item options: -clip, -clipHeight, -clipWidth, 
  -fontRise, -fontSizePoints, -fontStrikethough, -markup, 
  -textHeight, -textWidth
gnocl::text
o tag ranges issues resolved.
gnocl::labelWidget
o issues with widget commands now resolved.
gnocl::button
o new option -onDestroy
gnocl::dialog
o removed redundant -modal option, dialog widgets are modal by default

First Attempt at Accessing GObject Private Data - FAILED

When the toolbar is placed in the vertical alignment widget texts are still centred. I thought that making these alignable might be more aesthically pleasing. I didn't complete the job, but I did glean a little bit more on just how to access widget private data. It all seems to pivot around the G_TYPE_INSTANCE_GET_PRIVATE macro.

#if 1
/* ATTEMPT TO LEFT ALIGN BUTTON TEXT */

struct _GtkToolButtonPrivate
{
  GtkWidget *button;
  gchar *stock_id;
  gchar *icon_name;
  gchar *label_text;
  GtkWidget *label_widget;
  GtkWidget *icon_widget;
  GtkSizeGroup *text_size_group;
  guint use_underline : 1;
  guint contents_invalid : 1;
};

typedef struct _GtkToolButtonPrivate GtkToolButtonPrivate;

struct _GtkToolButton
{
  GtkToolItem parent;
  /*< private >*/
  GtkToolButtonPrivate *GSEAL (priv);
};


#define GTK_TOOL_BUTTON_GET_PRIVATE(obj)(G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_TOOL_BUTTON, GtkToolButtonPrivate))

GtkToolButton      *button;
GtkToolButtonPrivate *priv;

priv = GTK_TOOL_BUTTON_GET_PRIVATE (para->item);

GtkWidget *label = priv->label_widget;

gtk_misc_set_alignment (label,0.0,0.5);

#endif 

Friday, October 29, 2021

Converting a Pango Markup Span Tag to a List of Text Widget Tag Options

Its always been a trouble task sharing formatted text between the gnocl::text widget and other display widgets such as the gnocl::label, gnocl::tree and gnocl::list widgets. This diffculty arises from not being able to define text attributes as pango markup, this is understandable given that fact that there is not a smooth overlap between the widget and pango attributes.

What was needed was a way of bridging the two requirments, being able to take a markup <span> tag and converting to an -opt val list.

Parsing markup strings using the pango api is not terribly helpful, it is complex and only works on complete strings, "<span .... ...>" would simply through up a pango formatting error.

The solution has been much simpler by using a series of string mapping operations in the source module with the resul being the creation of a new gnocl::pango subcommand "markup2options". For instance, if: 

set markup "<span font='Serif italic bold 24' foreground='green' background='red'>"

then calling

gnocl::pango markup2options $markup

returns

-font "Serif italic bold 24" -foreground "green" -background "red"

Using this a working script would produce something along the lines of:

$txt tag create test {*}[gnocl::pango markup2options $markup]
$txt insert end $sometext -tags test


Wednesday, October 20, 2021

String and Text Block Insertion Allowing for HTML/Pango String Markup

Tcl offers some really excelling string manipulation tools 'straight out of the box' although sometimes something extra might be needed. What if, for instance, the strings contain markup? Any markup is invisible on screen and so any string positioning needs to ignore the effects of that markup on the overall string length.

This can be achieved by using a simple flag to keep track of where characters in a string are containtained within a markup substring or not.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
## insert str1 into str2 at position pos, allow for pango/html markup
# @param[in] str1	
# @param[in] str2
# @param[in] pos
# @param[in] opt
# @return modified string
proc string_insert { str1 str2 pos {opt ""} } {
	switch $opt {
		-markup {
			set flag 1
			set i 0
			set res ""
			foreach c [split $str2 ""] {
				append res $c
				if { $c == "<" } { set flag 0 }
				if { $c == ">" } { set flag 1 }
				if { $flag } { 
					if {$i == $pos } { append res $str1 } 
					incr i
					}  
			}
			return $res
			}
		default {
			return [string range $str2 0 $pos-1]$str1[string range $str2 $pos end]
		}
	}
}



## insert str1 into str2 at specified line and offset, allow for pango/html markup
# @param[in] str1
# @param[in] str2
# @param[in] line
# @param[in] offset
# @param[in] opt
# @return modified string
proc text_insert { str1 str2 line offset {opt ""} } {
	# extract specific line
	set strings [split $str2 \n]
	set ln [lindex $strings $line]
	# make insertion
	if { $opt == "-markup"} {
		set ln [string_insert $str1 $ln $offset $opt]
	} else {
		set ln [string range $ln 0 $offset-1]$str1[string range $ln $offset end]
	}
	# repack the text
	return [join [lreplace $strings $line $line $ln] \n]
}



asdasd

Recent Changes October, 2021

Most significant change during September was the inclusion of the -spellcheck option for the gnocl::text widget.


 2021-09:

gnocl::text

o new boolean option -spellcheck, cget also works, 

gnocl::spellcheck

o moved separate code back into the main module

gnocl::labelEntry

o added substitution strings %X %Y %W %H to -onIconPress/-onIconRelease callback scripts.

o cget -variable now works

gnocl::calendar

o added substitution strings %D %M %Y to -onDoubleDaySelect callback script.

Friday, September 17, 2021

Creating icons from UTF-8 Characters.

Linux distros have heaps of pre-installed icons ready for use. I recently needed to create a toolbar menu which needed to access a set of unique icons which contained single characters. It was, in fact, a pull down menu for the insertion of 'special characters'. The Gtk+ api has complete functionality for creating icons from pixbufs and Gnocl providing convenient access. 

Here's a screenshot and the script.

 


 


# !/bin/sh
# the next line restarts using tclsh \
exec tclsh "$0" "$@"
package require Gnocl
if { [namespace exists jmls] == 0} {
    namespace eval jmls {}
}
set ::app(specialCharacters)  [list Section ¶ Paragraph § Separator • Left-Arrow ← Up-Arrow ↑ Right-Arrow → Down-Arrow ↓ Root √]
proc jmls::charIcon {name ch} {
    
    set pb1 [gnocl::pixBuf new -width 40 -height 40]
    $pb1 text \
        -position [list 15 30] \
        -font [list Sans normal 30] \
        -text $ch \
        -align centre \
        -color white
    gnocl::stockItem add \
        -label $name \
        -icon "%?$pb1"
    $pb1 delete
}
proc jmls::tbarSpecialCharacterMenu {tbar {script ""} } {
    if { $script == "" } {set script {puts $::app(spChar)} }
    set menu [gnocl::menu]
    set mb [$tbar add menuButton \
        -icon %#New -text "Char" \
        -menu $menu \
        -tooltip "Insert special character into active text" \
        -arrowTooltip "Select Character" \
        -onClicked $script ]
    foreach {a b} $::app(specialCharacters) {
        jmls::charIcon $a $b
        $menu add [gnocl::menuRadioItem \
            -text "<big>$b</big>\t<small>($a)</small>" \
            -onValue $b \
            -variable ::app(spChar) \
            -data [list $mb configure -icon %#$a ] \
            -onToggled {eval %d}]
    }
    $mb configure -icon %#[lindex $::app(specialCharacters) 0]
}
set tbar [gnocl::toolBar ]
jmls::tbarSpecialCharacterMenu $tbar
set box [gnocl::box -orientation vertical -borderWidth 0 -spacing 0]
$box add $tbar
gnocl::window  -child $box -setSize 0.2
gnocl::mainLoop


September updates

August saw a few modifications to the core, largely in the form of resolving some minor issues adding some utility functionality.

 

2021-08:
    gnocl::menu
        o bug in configure -tearoff now fixed.
    gnocl::notebook
        o new tab option -tooltip
    gnocl::text
        o fixed problems with -onButtonPress/Release signal handling.
          See gnoclConnectOptCmd.
    gnocl::pointer
        o new subcommand, monitor.
    gnocl::timeOut
        o now works properly.
          Use as an alternative to Tcl after which does not work within a Gtk.
    %c (child) substitution string added to -onKeyPress/Release callbacks
          for GtkBin objects (e.g., toplevel windows).
    gnocl::text
        o functionality of getPos subcommand now enhanced.
          $text-wid getPos <position> <offset>
              position, row column pair in list,
              offset, any relative keyword, e.g. wordStart, wordEnd
          Omitting a position and/or keyword option will result in
          returning a buffer position based upon the current mouse pointer position in the widget

Wednesday, August 04, 2021

gnocl::timeOut

I'm not certain what's going on, but running the Tcl after command within a Gnocl application results in the after command not working properly. No big problem. use the gnocl::timeOut command which offers the same basic functionality.

gnocl::timer milliseconds script

returns timerid

To stop a timer, run

gnocl::timer stop timerid

Here's a sample script:





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

set i 0
set hb [gnocl::hBox]
set lab [gnocl::label -variable i -baseFont {Sans 24}]

set stop  [gnocl::button -text "STOP" -onClicked { 
%w configure -sensitive 0 ; $start configure -sensitive 1
gnocl::timeOut stop $timer } -sensitive 0]

set start [gnocl::button -text "START" -onClicked { 
%w configure -sensitive 0 ; $stop configure -sensitive 1
set timer [gnocl::timeOut 1000 { incr i } ]  }]

$hb add $lab -fill 1 -expand 1 
$hb add "$start $stop"

gnocl::window -title "Timed Counter" -child $hb -width 250

Monday, August 02, 2021

Working with tagging words.

The problem, how to tag a block of text in a textwidget when only a row-col (line-offset) pairing is know?

The Gnocl text widget has employed offsets for some time but this has always been in used in commands where the insertion point is used. Some recent changes to the getPos text subcommand will go some way in picking out words, lines etc., when using the "tag apply" instruction. 

Previously, getPos would only return a line-offset pair based upon the current mouse pointer position over the widget, this has now become the default. The enhancements allow two arguments to be passed, a line-offset pair and a keyword. So, if the line-offset refers to a point mid-way in a word, its now possible to hold of other relative positions using keywords.


  $text tag apply \

[$text getPos [list $a $b] wordStart] \

[$text getPos [list $a $b] wordEnd] \

-tags red

August News and Updates

Been very busy working on other projects during July, so only one improvement to report:


2021-07:

gnocl::notebook

o new tag option -baseFont, cget yet to be completed.

o new command clear, remove all pages

Thursday, July 01, 2021

Working with long text tag names.

Pango is really useful for creating markup strings although it does some caveats. It is markup for display purposes and not interactive editing. Some work has been done in bridging the gap between the display widgets and the text editor by creating preset markup tags which are meaningful names as tag whilst being valid pango strings. Pango also has some convenience tags although extending this list is not possible at all, nor its is directly possible through the Gtk api to directly convert pango attributes to tag properties or vice versa. 

To exchange formatted data between the text edit and display widgets needs a degree of creative programming on the part of the coder:

 

set txt [gnocl::text -useMarkup 0 -wrapMode word]
gnocl::window -child $txt -setSize 0.2 -onDelete {exit}
$txt tag create "<span rise='3000' font_size='small'>" -fontRise 3 -fontSize 8 -foreground red -editable 0
puts [$txt tag names]
$txt insert end AAAA -tags [$txt tag names]
$txt insert end \n
$txt insert end BBBB -tags "{<span rise='3000' font_size='small'>}"

July updates.

Added some useful functionality during June although some work still needs to be completed. The most annoying thing to be fixed was nagging issues over lineEnd and sentenceEnd, which was a simple logic matter. Of the most pleasing, reworking of dump. The aim being to provide the scripter with the ability to created serialized text files, Blog post to follow.

2021-06:

gnocl::text

o issues with lineEnd and sentenceEnd offset keywords now fixed. 

gnocl::labelEntry

o cget -value now works properly.

gnocl::tmpFile

o convenience command to create a temporary file name, with prefix.

gnocl::languages

o return information on languages supported and their naming conventions .

gnocl::text

o reworking dump command.

o new tag option -markup, assign pango markup equivalent (not validated).

o mark options -gravity and -visible can now be set on creation

Saturday, June 26, 2021

Toggling Text Visibility

 A simple way of toggling text is to assign a tag and manipulate its -invisible option, again using the event handling capability of a tag to control the setting. Notice that the snippet below, for each array two tags are created _${a} and _${a}_ where the former is the display header with the control, and the latter the tag to be elided. 
















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

package require Gnocl

set txt [gnocl::text -tabs 150 -editable 0]
gnocl::window -child $txt -onDelete { exit } -setSize 0.25

array set app [gnocl::application]
array set fruit [list apple 1 bannana 2 cherry 3]
array set cars [list morris a humber b austin c]

proc addLabel {wid str tag} {
     $wid addChild [gnocl::label -text $str]
     $wid tag apply lineStart lineEnd -tags $tag
     $wid insert end \n
}

proc tagClick {w n event} {
     if { $event == "buttonPress-1" } {
          $w tag configure ${n}_ -invisible [ gnocl::toggle [$w tag cget ${n}_ -invisible]]
     }
}


foreach a [list fruit cars app] {

     $txt tag create _$a -paragraph blue -onEvent { %w clearSelection ; tagClick %w %n %t-%b }
     $txt insert end $a\n -tags _$a
     
     foreach n [array names $a] {
          $txt tag create _${a}_ -indent 10 -data 1
          $txt insert end "${n}\t[set ${a}($n)]\n" -tags _${a}_
     }
}

Tuesday, June 22, 2021

Getting Widget Style Properties

Until the move over to Gtk4, Gnocl is still built against the Gtk 2.21 libraries. One of the inconveniences of Gtk is getting and setting widget style settings which are considered to be set globally by the desktop style settings and not for the programmer to tinker around with. Needless to say, there are times when different defaults are preferred, largely to draw the users attention to 'something a bit different'.

The function gtk_widget_modify_font  is a convenience function to set the widget basefont as shown in this snippet from the button.c module, 

if ( options[baseFontIdx].status == GNOCL_STATUS_CHANGED ) {
GtkWidget *label;
label = gnoclFindChild ( GTK_WIDGET ( para->button ), GTK_TYPE_LABEL );
PangoFontDescription *font_desc = pango_font_description_from_string ( Tcl_GetString ( options[baseFontIdx].val.obj ) );
gtk_widget_modify_font ( GTK_WIDGET ( label ), font_desc );
pango_font_description_free ( font_desc );
}

Unfortunately, there's no direct retrieval convenience function in the Gtk API and the documentation on the matter is scant but, again from the button.c module, retrieving style properties can be done directly:

if ( idx == baseFontIdx ) {
GNOCL_DEBUG_LINE

GtkWidget *widget;
GtkStyle *style;

widget = gnoclFindChild ( GTK_WIDGET ( para->button ), GTK_TYPE_LABEL );
obj = Tcl_NewStringObj ( pango_font_description_to_string ( gtk_rc_get_style ( GTK_WIDGET ( widget ) )->font_desc), -1 );
}


There is an alternative api function gtk_style_get, as illustrated in this clip from treeList.c

if ( idx == treeLineWidthIdx ) {
gint size;
GtkStyle *style = gtk_rc_get_style ( para->view );
gtk_style_get ( style, G_OBJECT_TYPE ( para->view ), "tree-line-width", &size );
obj = Tcl_NewIntObj ( size );
goto end;
}


This won't work with base font as there's no object property!

The gtkstyle.h include file from the Gtk source code gives the structure containing all the details.


struct _GtkStyle
{
  GObject parent_instance;
  /*< public >*/
  GdkColor fg[5];
  GdkColor bg[5];
  GdkColor light[5];
  GdkColor dark[5];
  GdkColor mid[5];
  GdkColor text[5];
  GdkColor base[5];
  GdkColor text_aa[5]; /* Halfway between text/base */
  GdkColor black;
  GdkColor white;
  PangoFontDescription *font_desc;
  gint xthickness;
  gint ythickness;
  GdkGC *fg_gc[5];
  GdkGC *bg_gc[5];
  GdkGC *light_gc[5];
  GdkGC *dark_gc[5];
  GdkGC *mid_gc[5];
  GdkGC *text_gc[5];
  GdkGC *base_gc[5];
  GdkGC *text_aa_gc[5];
  GdkGC *black_gc;
  GdkGC *white_gc;
  GdkPixmap *bg_pixmap[5];
  /*< private >*/
  gint attach_count;
  gint depth;
  GdkColormap *colormap;
  GdkFont *private_font;
  PangoFontDescription *private_font_desc; /* Font description for style->private_font or %NULL */
  /* the RcStyle from which this style was created */
  GtkRcStyle *rc_style;
  GSList *styles;   /* of type GtkStyle* */
  GArray *property_cache;
  GSList         *icon_factories; /* of type GtkIconFactory* */
};



Thursday, June 17, 2021

Creating temporary files

The C stdio package has two function calls related to the creation of temporary files: tmpFile and tmpnam. The Glib too has similar functions in its api such as g_mkstemp, g_file_open_tmp and so on. For someone programming in C, both of these approaches are excellent resources but someones scripting in Tcl more suitable approaches can be taken these C procedures will require a significant amount code to produce an effective binding which, may not offer worthwhile advantages.

The inclusion of a new command gnocl::tmpFile into to the core offers a convenient way of producing unique filenames. The command takes a single argument, an optional prefix, and returns a unique name with a randomized suffix.

If a prefix is specified:


puts [gnocl::tmpFile aaa]

/tmp/aaa.SYzqiJQA8Eeg4GZC1eDb

puts [gnocl::tmpFile bbb]

/tmp/bbb.VIT88L2zjeynnHn5dVfH

puts [gnocl::tmpFile ccc]

/tmp/ccc.3IkQqXjYtwDowshdjjCc


Without a prefix, the defaul 'TclScript' will be used.


puts [gnocl::tmpFile]

/tmp/TclScript.RcrXWEALx2ZDuS3DITli


If a greater level of control in need in the event of opening multiple temporary files then something along the line of the following procs may be of use.


proc tmp:open {} {

    set fname [gnocl::tmpFile]

    set fp [open $fname w]

    lappend ::tmp:files $fp $fname

    return $fp

}


proc tmp:files {} {

return [set ::tmp:files]

}


proc tmp:close {fp} {

close $fp

set ::tmp:files [dict remove [set ::tmp:files] $fp]

}


proc tmp:closeAll {} {

dict for {k v} [set ::tmp:files] {  close $k }

set ::tmp:files ""

}


proc tmp:unclosed {} {

return [dict keys [set ::tmp:files]]

}


set FP1 [tmp:open]

puts $FP1

set FP2 [tmp:open]


puts [tmp:files]

puts [tmp:unclosed]


tmp:close $FP2

puts [tmp:unclosed]


tmp:closeAll

puts [tmp:unclosed]

Sunday, June 06, 2021

May 2021 News!

 Just a handful on modifications this month.

 

2021-05:
    gnocl::winfo parent $wid
        o no longer crashed when parent == null.
    gnocl::menu
        o -data and cget -children now work correctly.
    gnocl::text
        o fixed minor internal issues relating to getPos, lineStart/lineEnd keyword usage.
    gnocl::notebook
        o intermittant crash using the remove/removePage subcommand fixed.
        o began implementation of embedded tab widgets
    gnocl::eventBox
        o fixed problems with -child command.
            (For some strange reason, the gnoclOptChild call would not work
            for eventBoxes after it had been modified to remove previous
            the child. The same functionality is not handled by the local
            configure/cget functions and it works fine.)
    gnocl::list/tree
        o new command: autosize.
    gnocl::window
        o new option  -hideOnDelete, hides window when deleted,
          useful for preventing toolpalette window from being destroyed. 

Friday, May 28, 2021

Removing a gnocl::application toplevel.

The gnocl::application command is a really use option to create a project ready front end. It creates all the necessary basics, containers, menubars, toolbars and status zones -- it even packs the whole lot into a toplevel window and returns the widget-ids of the contents.


But what, as I recently needed, must be done when the application is the centre of a plug-in to be packed into some pre-existing container held in some other toplevel window?
 

Remove and repack!
 

The whole layout is contained in a frame, a widget ordinarily accessed. This needs to be removed from the default toplevel and repacked elsewhere. 


Here's how to do it.

array set app [gnocl::application]   
$app(topLevel) remove $app(frame)   
$app(topLevel) delete
unset app(topLevel)


Thursday, May 27, 2021

The recently added 'tabCondfigure' and 'tabCget' is now replaced with 'tab configure' and 'tab cget'.

I've just been working on the module to create the gnocl notebook widget, paying particular attention to the tab configuration options.
 

Earlier, and for convenience's sake, I added two commands tabConfigure and tagCget. All well and good, but the established practice of is to have the commands addressing the main container, and sub commands container objects, in the manner of the text and its tags and various other elements.
 

The reasoning for this is that the notebook tab contains a child widget, a label by default, which the Gtk api allows to be directly accessed or even replaced. This is how some applications are able to embed widgets into notebook tabs (take Geany for example).
 

Unlike text tags, which are the same class of object, the notebook tab ans a container can  become the parent of any other widget or even a boxed set of widgets. Aware of this, any specific cget or configure command needs to be customized in order to allow for this extra flexibility.  

To accommodate this the notebook source module contains two functions tabConfigure and tabCget to handle these differences. 

The creating the tabConfigure function was pretty straight forward, but getting the tabCget function just right was a little more elusive, perhaps largely due to the side effects of having just had a covid jab!

Determining which of the options is queried in the source is achieved with a simple while () loop through each element in the tabOptions structure array:


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

        if ( strcmp ( tabOptions[idx].optName, Tcl_GetString ( objv[4] ) ) == 0 ) {
            break;
        }
        idx++;
    };


    
The resultant value for idx is then passed through a switch filter and the appropriate values retrieved.

The practical benefits of the tab configure option are the settings controlling the tear-off and relocation of tabs in the tab bar can be adjusted, menu labels and texts can be reset, and of course, multiple items embedded into the tab.

Sunday, May 16, 2021

Belated April updates....

 


Overlooked posting April's updates. Here goes... 


2021-04:
    gnocl::window
        o new commands: maximize, fullscreen. Take boolean arguments.
        o problems with using -setSize after window first shown fixed.
    gnocl::menu
        o new option -widget for popup subcommand, position popup at bottom left corner of the specified widget.
        o menu tearoff item no longer added by default. This due to "window show" revealing any hidden items.
          The -tearoff option was added to emulate Tk, but in practice this rarely used.
    gnocl::comboEntry
        o new option -editable for gnocl::comboEntry
        o new command clear, a synonym for set "".
          Simply a convenience to comply with other editable widget commands.
    gnocl::clock
        o startup diameter (width/heightRequest) set to 150px.
    gnocl::unicode
        o new command to manipulate and query unicode strings.
          located in module src/unicode/unicode.c
          subcommand 'script', return the unicode script definition for sample character
    gnocl::text
        o -onCopyClipboard option now works, substitution strings implemented %d %t %w

Friday, May 14, 2021

Simple string padding

Just a quick way of centering a substring within a space passed string.  Nothings perfect, especially when odd numbers are concerned.

 proc pad { str max} {
    set len [string length $str]
    
    set tmp [string repeat " " $max]
    set dif [expr $max-$len]
    set fr [expr $dif/2]
    set to [expr $fr+$len]
    set str [string replace $tmp $fr $to $str]
    
    return $str
}


Wednesday, May 12, 2021

Text Widget List Viewer


 

 

The list widget is a great tool for displaying related information in sets. That is a sample, distributed across rows and columns. To do this the list widget creates cells for each sample and allocated memory to handle its formatting and data. In practice, this means two 'blocks' for each piece of data. The information to display, and how to display it.

This arrangement works fine for large humanly workable amounts of data, perhaps up to 1,500 or more rows. But, what about more, much, much more?

I have a translation data set with source and target language pairs well in excess of 57,000 pairs. The list widget will handle these, but the delay receiving and rendering is far too long. The solution then, is to tweak the text widget to handle similar rows with a row picker. Sounds complicated, but its not. Just a couple of tweaks needed. Put the text widget inside an eventBox and then trap any direct events being passed to it, whilst allowing the scrolledWindow container to get the input it needs.

Why do this? By default the text widget will change the mouse pointer to an edit bar. The eventBox retains use of the

Achieving the latter is the easiest, nothing required. The scrolledWindow takes priority over the eventBox by default. So, turn off the text cursor, and make it invisible and respond to the eventBox mouse button events to change the paragraph colour of the selected block -simples!

 


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

package require Gnocl


# ~~~~~~~~~~~~~~~~~~~~~~~~~~

set txt [gnocl::text -editable 0 -cursorVisible 0]
$txt tag create idx -paragraph orange

set ebox [gnocl::eventBox -child $txt -aboveChild 1]
gnocl::window -child $ebox -setSize 0.2


# add some text to the widget.
for {set i 0} {$i<250} {incr i} {
append buff "$i) Lorem\n"
append buff [gnocl::lorem]\n
}
$txt set $buff
puts [$txt getLineCount]


#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# SET EBOX EVENTS TO SIMULATE LINE SELECTION AND TEXT SCROLLING
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$ebox configure \
-onButtonRelease {
    %c tag remove start end -tags idx
    %c setCursor [%c getPos]
    %c tag apply lineStart lineEnd -tags idx
} \
-onScroll {
    if { "%D" == "down" } {
        incr ::scrollPos 5
            if { $::scrollPos > [%c getLineCount] } { set ::scrollPos [%c getLineCount]}
        } else {
            incr ::scrollPos -5
            if { $::scrollPos < 0 } { set ::scrollPos 0}
        }
    %c scrollToPosition "$::scrollPos 0"   
}
 


 

Saturday, May 08, 2021

Zen and the Art of Computer Programming


When we learn to code we work on simple problems, maybe the sort of tasks that were originally used to test and develop our favourite languages, libraries and apis. In a nutshell, simple solutions to simple problems. 

But, when complexity takes over, rules often go out of the window - there may be causes, but these cannot be easily identified and defined. The may be at a 'lower level', perhaps in dependencies or even compilation errors. 

I've just had to resolve one such problem arising from the failure to display of child widgets in an eventBox. After modifying the gnoclOptChild code in the parseoptions.c module to offer the ability to replace child objects in a GtkBin, the new code worked for other GtkBin objects but not for an eventBox

The solution was to copy the contents of the gnoclOptChild function and divide the code between the configure and cget module functions. This also needed a modification to the EBoxOptions array. Happy again now!

So why the Zen and Computer programming? 

Always keep an open mind, things doesn't always go the way you expect them to.

Thursday, May 06, 2021

Notebook Tabs with Embedded Widgets.

Ok, its of limited use but it is possible to embed user defined widgets in a notebook page tab. In all likelihood it will still be a label and a button embedded in hbox and not a list or tree view

Including custom labels does have its drawbacks, its not really practicable to use accelerators in the same way and, if any commands or substitution strings are passed then these will return label widget-ids and not the label text string.



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

package require Gnocl

set nb [gnocl::notebook]

$nb addPage [gnocl::button -text HI] AAAAA

$nb tabConfigure 0 -data 1


# label and box
set hbox [gnocl::hBox -padding 0 -borderWidth 0]
$hbox add [gnocl::label -text %_H_IHIHI]
$hbox add [gnocl::image -image %#SaveAs -stockSize smallToolBar ]


$nb label 0 $hbox

puts [$nb getLabel 0]

gnocl::window -child $nb

Wednesday, May 05, 2021

Setting Variables from Lists

The format for defining a Tcl proc is simply:

proc name {var1 var2.. varn} { }

There is a special keyword when defining the variable list, args. When placed at the end of proceedures argument list, it results all other values being passed as a single list. In effect then, it allows a Tcl proc to recieve a variable number of arguments.

So, the above definition could be re-written as:

proc name {args} { }

The implication here is that keeping to a single string as an even numbered list tags and values, it becomes possible to define variables within the calling procedure. Once passed, these can be set using the standard Tcl foreach command.

proc name {args} {
    foreach {*}$args {break}
}

Now, this process can be taken one step further. Many system commands have values, commands and switches and it can be helpful to emulate these patterns within a Tcl script as it saves the mind power of having to switch between programming styles.

The next step then is:

proc name {val1 val2 args} {
    foreach {*}$args {break}
}

Where val1 and val2 can be subcommands, required values and arguments or optional values in the form of switches.  

If option tags with leading '-' are used be used these will have detrimental effect to creating legal variable names but they can be removed quite easily using one two ways using regsub or a string Tcl commands in combination with foreach. i.e.,

foreach {a b} $args { regsub -line {\-} $a {} a ; set $a $b }

Or, 

foreach {a b} $args { set [string range $a 1 end] $b }


Monday, May 03, 2021

To Close or to Hide, that is the Question.

Sometimes you want make a popup window with some editing or parameter setting functionality which is a bit like a dialog, although floating palette is what immediately comes to mind. 

The problem is though, that whilst it's easy to adjust the window decorations, to remove the delete icons, the window's pull-down menu will still have a close option which will, by default, destroy the window when clicked. 

To disable this, and simply hide the window so that it can be shown again, set the -hideOnDelete option to 1. 

Following this callback scripts can be used to respond to the visibility state of the window, acting as some form of Ok button if necessary.

 

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

package require Gnocl

set txt
[gnocl::text]

set win1 [gnocl::window -child $txt] \
    -title Win1 -x 500 -y 400 -setSize 0.125

# EXTRA OPTIONS
$win configure \
    -hideOnDelete 1 \

    -onHide { puts BYE! } \
    -onShow { puts Hello-Cheeky! }]


# toggle win1 hide/show states
set win2 [gnocl::window -child [gnocl::button -text "Show win1" -onClicked {
    $win1 show
    }] -title Win2 -x 800 -y 400 -setSize 0.125  ]

# revoke the hide on delete status for win1
set win3 [gnocl::window -child [gnocl::button -text "Turn off Hide" -onClicked {
    $win1 configure -hideOnDelete 0
    }] -title Win2 -x 1100 -y 400 -setSize 0.125]



Sunday, May 02, 2021

Had a Real Pig of a Morning Today!


 

I've spent the better part of half a day solving what really ought to a have been a trivial problem. Well, the solution was trivial but tracking down the cause of it all was very frustrating indeed!

Consider this:

set menu [gnoc::menu]

Hitherto, in order to be compliant with now ancient formatting practices, a menu automatically has a tearoff item place at position 0 in the children list. So, to hide it, because we don't want one use:

set menu [gnoc::menu -tearoff 0]

or

set menu [gnoc::menu]

$menu configure -tearoff 0]

So far, so good. But, if the gnocl::application command is used to produce a working UI framework, and extra widgets including addition menu bar items, and the show subcommand used for the application toplevel then BOOM! 

The recently made menus without tearoffs suddenly got tearoffs again. 😠

The issue was that the existing menu.c configure function simply hid or showed the default tearoff item. This needs to be created or destroyed when needed.

Well, job done now. Onwards and upwards!

 

 

 

 

 

Thursday, April 29, 2021

Why didn't I do this before?

Sometimes its so much more convenient (and efficient) to implement something in C rather than Tcl: positioning a popup menu on screen is one of those situations. To this end a new option, -widget, has been added to the menu popup subcommand. 

This causes the callback handler to determine screen coordinates from the on-screen location and height of the widget specified by the -widget option.

Compare:

$abut configure -onClicked "$menu popup -widget %w"

With:

$abut configure -data $menu -onClicked {
        lassign [gnocl::winfo geometry %w] x y w h
        %d popup [expr $x-4] [expr $y+$h] }

This latter approach not only requires memory allocation using -data but 'pollutes' the global namespace with the extra variables 'x y w h' which might result in some form of conflict. 

For those who notice, there is '+4' which is an attempt to handle the buttons style border-width setting. This isn't handled in the callback script, but accommodated in the module core.





Thursday, April 22, 2021

Namespace export-import, aliases and rename

 
    Namespaces are really useful in Tcl as they permit commands and variables to be conveniently grouped together. This means that common command names can be reused within specific contexts, and that variables can be 'hidden' away from errant command calls. All this extra security does come at a cost, a lost of unecessary and respetative typing. Using the gnocl:: namespace prefix is useful if, lets say, there is a need for mixed Tcl and Gnocl programming. But, if there isn't this need what can be done to alleviate the typing burden, albeit with some risks when coming to readability and portability between applications? Variables and commands embedded within a namespace can undergo export-import process or they can be renamed.

The following code example shows how this can be done. When exporting and importing between namespaces its possible to have a naming conflict in the importing namespace. Tcl will pick this up and result in an error. Fortunately this only applies to a handful of instances and in most cases (e.g. list and listStore) command synonyms are built into the gnocl core.

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

package require Gnocl

# what namespaces are present?
puts [namespace children ]

# export all gnocl command and widge names
namespace eval ::gnocl {
    namespace export application listStore button menu window
    foreach class [widgets] { namespace export $class }
    foreach class [commands] { namespace export $class}
}

foreach class [gnocl::widgets] {
    if { [catch { namespace import gnocl::$class } err] }  { puts WIDET-$err }
}

foreach class [gnocl::commands] {
    if { [catch { namespace import gnocl::$class } err] }  { puts COMMAND-$err}   
}

set x 200 ; set dx 400
set opts "-y 400 -width 300 -height 200"

# EXAMPLE 1) create app from and array
array set app [application]

$app(topLevel) show


set lst [listStore -columns 2 -types {string string}]
$lst add [list [list a b] [list b c] ]
$app(container) add $lst -fill 1 -expand 1
$app(topLevel) configure -x [incr x $dx] {*}$opts

# EXAMPLE 2) using aliass
interp alias {} g_window {} gnocl::window
g_window -child [gnocl::text] -x [incr x $dx] {*}$opts

# EXAMPLE 3) renaming commands
foreach class [gnocl::widgets] {
    rename gnocl::$class gcl::$class
}

gcl::window -child [gcl::button -text hello]  -x [incr x $dx] {*}$opts




Wednesday, April 21, 2021

gnocl::unicode -a new command

Determining the language of the user's system and script of a text string

 

 

puts $env(LANG)
puts $env(LANGUAGE)

puts [gnocl::unicode script "我是英国人。"]
puts [gnocl::unicode script "ཀརྨ"]
puts [gnocl::unicode script "aṣṭa"]
puts [gnocl::unicode script "ꛅꛇꛈ"]
 

puts [gnocl::unicode options]
puts [gnocl::unicode commands]

Monday, April 12, 2021

March 2021 Updates and News


 

 

Fashionably late? Maybe not. Here's a listing of the recent enhancements to the gnocl core modules.

2021-03:
    gnocl::fileChooserButton
        o -fileFilters option now works correctly
    gnoclKeyboardCmd, retrieve keyboard state.
        o Returns list of boolean for: NumLock CapsLock ScrollLock
          For Overwrite Mode of text and entry widgets, obtain setting from wiget directly
          using %w cget -overwrite.
    gnocl::window, gnocl::frame, gnocl::expander, gnocl::handleBox, gnocl::scrolledWindow
        o -onDelete %c string option added to handle GTK_BIN objects, return sole child widget.
    gnocl::dialog
        o new option -noButtons, creates a buttonless dialog, with
          response handled through dialog closure
 

Most of March saw me working on the complete rewrite of my translator's workbench, code named 'JMLS'. I've worked on various 'manifestations' of this platform since 1998 but, such a complete and thorough rewrite, I'm seriously reconsidering renaming as 'Tyndale' after arguably the greatest translator in the English language who lived during the 16th Century.

Next month will see a near complete binding of the gtk vte widget along with its introspective help. The absolute dearth of meaningful public documentation on the virtual terminal is frustrating as its difficult to determine how the large range of options and signals work together to form a meaningful whole. Still, time will tell.

Now to bring the gnocl::canvas widget up to date!


Saturday, March 20, 2021

Getting Keyboard Lock and Overwrite Settings

 


 

 

 

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

if { [catch { package present Gnocl } ] } { package require Gnocl }

set win [gnocl::window -child [gnocl::text -name txt] -setSize 0.25]

exec xmodmap -e "add mod3 = Scroll_Lock"

txt configure -onKeyRelease {

    lassign [gnocl::keyboard %w] NumLock CapsLock ScrollLock
    set Overwrite [%w cget -overwrite]
    
    foreach item {NumLock CapsLock ScrollLock Overwrite} {
        puts "$item   [set $item]"
    }
    
}

Saturday, March 06, 2021

March Updates


 

Just a few tweaks this month.

2021-02:
   gnocl::stringUtils
        o new subcommands,
            totitle, convert string to title string by capitalizing all word initial characters
            toroman, convert Arabic integer to Roman numerals
    gnocl::notebook
        o tearoff tab window will now contain the same text as the child tab
        o new command -tabCget
    gnocl::dialog
        o dialog now registered to obtain a widget-id, not directly
          returned but easily obtained by child widgets using the
          [gnocl::winfo toplevel %w] command.
    gnocl::tree/list
        o -onSelectionChanged now supports %d substitution string.
        o -onWidthChanged, callback handler rewritten to automatically
          update column wrapwdith when resized.

Sunday, February 14, 2021

February 2021 Update


 

A tad late in posting this month's news but here's what happened during January.

 

 2021-01:
    gnocl::window
        o issues with snapshot subcommand fixed.
          added snapshot options: -delay, -pointer, -color, -radius, and -alpha.
    gnocl::pixBuf
        o new creation option offScreen.
    gnocl::offScreen
        o new widget type.
    gnocl::notebook
        o fixed problems with -onCreateWindow and -detachable options.
        o popupmenu labels now accept markup.
        o tabConfigure -label, -detchable, -menuText, -menulabel, -position options now work
        o tabConfigure -fill, -expand, -pack do workd, but may give unexpected results.        
    gnocl::dialog
        o -content option retired, use -child if used.
        o new option -actionWidget (add widgets to action area widgets), typically buttons.
        o -onResponse new substitution string, %c. Return child widget-id.        
    gnocl::text
        o swap substrings, now stored in widget params struct rather than data assignment.
        o new option -corrections, substitute whole words.
    gnocl::canvas
        o integrated separate module code into main module. Comparable with Tk.
    gnocl::vte
    gnocl::vfs
    gnocl::file
    gnocl::mime

Thursday, January 21, 2021

Hybrid Menubar and Toolbar Tabber

The customary method of organizing application menubars and toolbars is in a vertical sequence. To save space, sometimes multiple tool sets can be embedded into a single bar. This is fine but what usually results is a duplication if the functionality offered in a pull-down menu and some toolbar button. This can cause a lot of clutter, not only on screen, but in the coding where the duplication is a occurring.

For a user the on-screen clutter can be obtrusive as it begins to eat away at the screen space used by an application in both the horizontal and vertical dimensions. Too many on screen toolbars reduces the size of user work zones and tool many items per bar can result in windows whose resizing becomes constrained.  

Multiple toolbars can be toggled on or off, but then a menu item, nested somewhere in menu in the toolbar needs to be created, the result more clutter. Complex GUI elements are not only demanding in terms of coding, but offputting to any new user who has grown shy of 'bloated apps'. Users become menu weary. 

The solution? Why not switch between toolbars as needed, and leave those not so often used options such as would be found in the File, Settings and Help menu in the menubar.

Switching between toolbars is easily sorted, place them in the pages of a notebook. But, one might ask, will this take up three rows on screen, with one each for the menubar, notebook tabs and the toolbars. Well no, not really. The solution lies in embedding the menubar into the notebook tab bar using the -startWiget option. And, for those who prefer the Help menu tucked on the right hand side of the menu bar, its even possible to embed this separately using the -endWidget option. The result, a clean and simple solution with a modern feel to it.

Here's a screen shot, showing this at work in a proof of concept script.


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

if { [catch { package present Gnocl } ] } { package require Gnocl }
    
set frame [gnocl::vBox]
set nb [gnocl::notebook]
$frame add $nb
set menuBar [gnocl::menuBar]
$nb configure -startWidget $menuBar
set container [gnocl::vBox]
    
$frame add $container -fill 1 -expand 1
gnocl::window -child $frame -setSize 0.3 -onDelete exit

set menu(file) [gnocl::menu ]
set menu(help) [gnocl::menu ]

$menu(file) add [gnocl::menuItem -text "%#New" -onClicked {puts "That's new"}]
$menu(file) add [gnocl::menuItem -text "%#Open" -onClicked {puts "That's new"}]
$menu(file) add [gnocl::menuItem -text "%#Save" -onClicked {puts "That's new"}]
$menu(file) add [gnocl::menuSeparator]
$menu(file) add [gnocl::menuItem -text "%#Quit" -onClicked exit ]
set item(help) [gnocl::menuItem -icon %#Help -text "%__Help" -onClicked { gnocl::dialog -text HELP!}]
set item(about) [gnocl::menuItem -icon %#About -text "%__About" -onClicked { gnocl::aboutDialog }]

$menu(help) add $item(help)
$menu(help) add $item(about)

set file [gnocl::menuItem -text "%__File" -submenu $menu(file)]
set help [gnocl::menuItem -text "%__Help" -submenu $menu(help) ]

$menuBar configure -children [list $file $help]


$nb addPage [gnocl::richTextToolBar  -iconSize small] %__Markup

set tb1 [gnocl::toolBar -style icons -iconSize small]
$nb addPage $tb1 %__Edit

set tb2 [gnocl::toolBar -style icons  -iconSize small]
$nb addPage $tb2 %__Tools

    
$tb1 add item -icon %#Cut   
$tb1 add item -icon %#Copy
$tb1 add item -icon %#Paste
$tb1 add item -icon %#Delete
$tb1 add item -icon %#Undo
$tb1 add item -icon %#Redo

$tb2 add item -icon %#GoBack   
$tb2 add item -icon %#GoDown
$tb2 add item -icon %#GoForward
$tb2 add item -icon %#GoUp
$tb2 add item -icon %#GotoFirst
$tb2 add item -icon %#GotoLast


$container add [gnocl::text -name txt -margins {4} -useMarkup 1] -fill 1 -expand 1

set stat [gnocl::statusBar]
$container add $stat -fill 1
    
txt lorem ;# using widget alias automatically created using the gnocl::text -name option
    
$stat push ready...


Other enhancement that could be implemented included a tearoff capability for the toolbar notebook tabs and the use of menu ribbons outlined in a previous blogpost (clear here to read).