The ViewRects package

The AlphaTcl library contains a package named viewRects which defines utility procs designed to easily manipulate the frame of the subviews and their layout. The hi prefix stands for Human Interface. It provides simple commands to specify the positions and dimensions of the subviews relatively to each other without making any explicit calculations and to assemble the elements of a root window or of any container.
All the procs are defined in the file viewRects.tcl located in the SystemCode directory inside the AlphaTcl library. These procs usually accept, as an optional argument, the name of a variable containing the coordinates of a rectangle and return the same rectangle with modified coordinates: the same rectangle can thus progress and be used successively to position different views. It is important to note that the rectangle is designated by its name and not by a variable. For instance, here is how you would declare a container rectangle named wb (for window bounds):
    hi::initRect 300 250 wb
This instruction declares a rectangle named wb with width 300 pixels and height 250 pixels, with its origin at point (0,0). If you wanted a different origin, say (50,80), you could use the following instruction instead:
    hi::setRect 50 80 300 250 wb

Rectangles specification

In many procs accepting a rectangle argument, this rectangle can be specified in three different ways: Here x and y are the x-coord and y-coord of the lower left corner and w and h are the width and height of the rectangle respectively.
In order to make things even more simple, one can also declare the name of a default rectangle so that all the procs apply, unless otherwise stated, to this rectangle. One can thus apply the procs without even specifying which rectangle they apply to. The name of the default rectangle is usually declared at the beginning of a script. For instance, the instruction to declare a rectangle named vb as the default rectangle is:
    hi::defaultRect vb
Its initial dimensions are {0 0 0 0}.

Quick start


Here is a series of instructions to demonstrate how these procs can be used:
    «» hi::defaultRect vb
    vb
    «» hi::setSize 150 20
    0 0 150 20
    «» hi::setOrigin 10 100
    10 100 150 20
    «» hi::lf
    10 67 150 20
    «» hi::center 300
    75.0 67 150 20
    «» hi::incrSize 15 10
    75.0 67 165 30
    «» hi::vskip 40
    75.0 27 165 30
    «» hi::inset 5
    80.0 32 155 20
    «» hi::top
    52
    «» hi::right
    235.0


The [hi::lf] proc is the equivalent of a line feed: it moves the rectangle vertically by the amount of its previous height, augmented by an interspace (equal to 13 pixels by default and controlled, if necessary, by the [hi::margin] proc). This proc can also be applied horizontally: [hi::lf h] would move the rectangle horizontally by the amount of its previous width, augmented by the interspace.

Procs by task

Initialize rectangles

Get the dimensions

Manipulate the positions

Utility procs

The ViewRects procs

hi::adjust

Adjust a rectangle on the margins of a parent rectangle. The syntax is:
hi::adjust where parentRect ?rectName ""? ?local 1? ?allMargins 0?
The parent rectangle can be specified in any of the forms explained in the
Rectangles specification section.
This proc is used typically to set the initial position of a rect inside the window bounds.
The where argument is made of two letters representing horizontal and vertical positions inside the parent (in no particular order). Possible letters are: t (top), b (bottom), l (left), r (right), or c (center). When a letter c is specified, the proc determines if it is vertically or horizontally centered by looking at the other position. The default is l for the horizontal position and t for the vertical one.
By default the coordinates are returned in local coordinates of the enclosing rectangle, that is to say considering that the lower left corner of the enclosing rectangle is {0 0}. This is what is desired most of the time because the enclosing rectangle will generally be a window frame whose origin is in screen coordinates and not in window relative coordinates. If you really want to get the result in the same coordinates system as the enclosing rectangle, set the local argument to 0: the returned rectangle will be relative to the actual origin of the enclosing rectangle.
If the allMargins argument is set to 1, the returned coordinates are calculated so that all the margins are taken into account. This is done by shrinking the rectangle if necessary, meaning that the width and height of the inner rectangle may be affected: if it is too wide or too high to fit between the margins, its size will be reduced by the necessary amount. By default, the allMargins argument is set to 0 and the adjustment is performed only along the relevant edges, so that the width and height of the rectangle are preserved.
The proc hi::margin can be used to get or set the value of the margin. See the proc hi::align if you want an alignment along the outer edges of the parent ignoring the margins.
Example:
    «» hi::setRect 100 100 200 300 wb
    «» hi::defaultRect vb
    «» hi::setRect 20 150 60 25
    «» hi::adjust tl wb
    113 362 60 25
    «» hi::adjust tr wb
    227 362 60 25
    «» hi::adjust tc wb
    170.0 362 60 25
    «» hi::adjust bl wb
    113 113 60 25
    «» hi::adjust br wb
    227 113 60 25
    «» hi::adjust bc wb
    170.0 113 60 25
    «» hi::adjust lc wb
    113 237.5 60 25
    «» hi::adjust cc wb
    170.0 237.5 60 25
    «» hi::adjust rc wb
    227 237.5 60 25
    «» hi::setRect 20 150 260 325
    «» hi::adjust rc wb 1
    113 113.0 174 274

hi::advance

Set the position of the view rectangle by advancing in a specified direction with respect to last occupied position. The syntax can take two forms:
hi::advance dir ?rectName ""?

hi::advance dir width height ?rectName ""?

In the first form, the proc uses the current dimensions of the rectangle. In the second form, the width and height are modified and the new values are used for the calculations.
The dir argument indicates the direction into which the rectangle is moved. The possible values are up, down, left, or right (or abbreviated u, d, l, or r respectively).
When moving vertically, the new rectangle has its left edge aligned with the previous left edge and an interspace equal to the value of the
hi::vspace variable is inserted. When moving horizontally, the new rectangle has its top edge aligned with the previous top edge. and an interspace equal to the value of the hi::hspace variable is inserted.
Example:
    «» hi::setRect 20 150 60 25
    «» hi::advance d
    20 120 60 25
    «» hi::advance d 80 35
    20 80 80 35
    «» hi::advance u
    20 120 80 35
    «» hi::advance r
    108 120 80 35
    «» hi::advance l 90 40
    10 120 90 40

hi::align

Align a rectangle on given positions. The syntax is:
hi::align edges positions ?rectName ""?

The edges argument is composed of one or several letters t, l, b or r, each of which indicates which edge of the rectangle is to be aligned on the specified positions: t and b imply a vertical alignment, l and r a horizontal alignment.
The positions argument is a list of positions on which to make the alignment: for instance, when adjusting along the right edge, it would typically be set to the width of the enclosing frame. When adjusting along the top edge, it would typically be set to the height of the enclosing frame. This argument should contain as many values as there are letters in the edges argument.
This proc moves the entire rectangle so that its width and height are preserved.
Example:
    «» hi::setRect 20 150 60 25
    «» hi::align r 100
    40 150 60 25
    «» hi::align l 10
    10 150 60 25
    «» hi::align t 200
    10 175 60 25
    «» hi::align b 20
    10 20 60 25
    «» hi::align tr [list 200 100]
    40 175 60 25
    «» hi::align bl [list 20 10]
    10 20 60 25

hi::bottom

Get the bottom y-coord of a named rectangle. The syntax is:
hi::bottom ?rect ""?
The rect argument can be specified in any of the forms explained in the
Rectangles specification section.
Example:
    «» hi::setRect 20 150 60 25
    «» hi::bottom
    150

hi::bounds

Return the bounds of a view. They are in the coordinates system of the view's frame itself. The syntax is:
hi::bounds token ?val?
If the val argument is missing, the proc returns the current value of the token's bounds. Otherwise, val should be a list of the form {x y w h} and the proc sets the bounds to these coordinates.
See the Cocoa Views Guide for more info about frames and bounds:
Cocoa Views Guide

hi::buildRect

Set the coordinates for a rectangle given a size {w h} and an origin {x y}. The syntax is:
hi::buildRect size ?origin ""? ?rect ""?
If the origin is not specified, it defaults to {0 0}. Note that both the size and the origin are specified as two-elements lists.
This proc is useful, for instance, to set the dimensions of a dialog window by passing only the size since a dialog is automatically centered on the screen and its origin is thus irrelevant.
Example:
    «» set size {60 25}
    «» set origin {20 150}
    «» hi::buildRect $size $origin
    20 150 60 25
    «» hi::buildRect $size
    0 0 60 25

hi::buttonDims

Calculate the dimensions of a button based on its label. It returns a two-elements list with the width and the height of the bounding rectangle. The syntax is:
hi::buttonDims ?label ""? ?font ""?

Example:
    «» hi::buttonDims "Synchronize"
    101 25
    «» hi::buttonDims "Synchronize" "name Monaco size 9"
    90 22
    «» hi::buttonDims "Synchronize" "name Monaco size 12"
    110 25
    «» hi::buttonDims "Synchronize" "meta system size 12"
    101 25
    «» hi::buttonDims "Synchronize" "meta systemBold size 12"
    106 25

hi::buttonRect

Calculate the coordinates of a button (typically an OK button) located at the bottom of the enclosing frame. The syntax is:
hi::buttonRect parentRect ?label "OK"? ?side "r"? ?font ""?
The enclosing frame (designated by the parentRect argument) is specified as with the
hi::adjust proc.
Depending on the value of the side argument (l or r), the button can be located in the lower left or lower right corner. An offset equal to the current value of hi::margin is preserved between the border of the button and the border of the enclosing frame.
The return value is the list of rectangle coordinates of the OK button in the same coordinates system as the parent. The parentRect argument is typically set to the window's contents frame with its origin at {0 0}.
One can optionally specify the title of the button (by default "OK") and the font used to display it.
Example:
    «» set parentRect [list 0 0 600 400]
    0 0 600 400
    «» hi::buttonRect $parentRect
    539 13 48 25
    «» hi::buttonRect $parentRect "Typeset"
    512 13 75 25
    «» hi::buttonRect $parentRect "Typeset" "l"
    13 13 75 25

hi::center

Center a rectangle with respect to the given outside dimensions. The syntax is:
hi::center width ?height 0? ?rect ""?

The width argument is the width of the interval in which the rectangle will be centered horizontally. If it is equal to 0, no horizontal centering will occur.
The height argument is the height of the interval in which the rectangle will be centered vertically. If it is equal to 0 or if it is omitted, no vertical centering will occur.
This proc moves the entire rectangle so that its width and height are preserved.
Example:
    «» hi::setRect 20 150 60 25
    «» hi::center 200 100
    70.0 37.5 60 25

hi::close

Cause the root window containing the specified subview to be closed, as if the user had clicked the Close button. The syntax is:
hi::close token
The token argument is the token of a view in the root window, not necessarily the root's token.
Depending on how the root window was created, this may delete it or not. For more information, see the explanations with the
[view hide] command and the -oneShot option. See also the proc hi::exit if you want the root window to be deleted.

hi::cr

This proc is the equivalent of a carriage return. It is equivalent to executing hi::lf and then aligning the rectangle on the left margin. The syntax can take two forms:
hi::lf ?rectName ""?

hi::lf width height ?rectName ""?

They have the same meaning as with the hi::lf proc.
Example:
    «» hi::setRect 20 150 60 25
    «» hi::cr
    13 120 60 25
    «» hi::cr 90 35
    13 80 90 35

hi::defaultRect

If used with no argument, the proc returns the name of the current default rectangle or raises an error if no such name has been declared. The syntax is:
hi::defaultRect ?rectName ""?

Otherwise it lets you declare the name of the rectangle to use by default in the other procs of this package. The variable, if it does not exist, is created in the scope of the caller: it is initialized with coordinates {0 0 0 0}.
Example:
    «» hi::defaultRect vb
    «» hi::rect
    0 0 0 0
Then instructions like
    «» hi::setRect 13 13 150 200
    «» hi::setOrigin 10 20
    «» hi::setSize 50 25
will apply to the rectangle named vb provided the variable vb is defined in the scope of the caller.

hi::exit

Cause the root window containing the specified subview to be deleted as well as all the subviews it contains. The syntax is:
hi::exit token
The token argument is the token of a view in the root window, not necessarily the root's token.
This proc deletes the root window. See the proc
hi::close if you just want to close the root window.

hi::frame

Return or set the frame of a view in parent-relative coordinates. The syntax is:
hi::frame token ?val?
If the val argument is missing, the proc returns the current value of the token's frame. Otherwise, val should be a list of the form {x y w h} and the proc sets the token's frame to these coordinates.

hi::height

Get the height of a named rectangle. The syntax is:
hi::height ?rect ""?
The rect argument can be specified in any of the forms explained in the
Rectangles specification section.
Example:
    «» hi::setRect 20 150 60 25
    «» hi::height
    25

hi::hskip

Move the view rectangle horizontally by the specified amount. The syntax is:
hi::hskip offset ?rectName ""?
This is synonym of:
        hi::shift $offset 0
The idea is that a view layout is composed from left to right, so a positive value of the offset moves the rectangle rightwards. Specify a negative offset to move the rectangle leftwards.
Example:
    «» hi::setRect 20 150 60 25
    «» hi::hskip 10
    30 150 60 25
    «» hi::hskip -20
    10 150 60 25

hi::hspace

Get or set the hspace variable which defines the horizontal spacing between items. The syntax is:
hi::hspace ?value ""?
The horizontal spacing is automatically inserted by the
hi::cr and hi::lf procs.
Example:
    «» hi::hspace 
    8
    «» hi::hspace 8
    10
    «» hi::hspace 
    10

hi::incrSize

Resize a rectangle by a specified horizontal and vertical amount. The syntax is:
hi::incrSize xdelta ?ydelta ""? ?rectName ""?
The xdelta and ydelta arguments can be negative in order to reduce the size of the rectangle. If the ydelta argument is omitted, it is assumed equal to xdelta. This does not modify the origin's coordinates.
Example:
    «» hi::setRect 20 150 60 25
    «» hi::incrSize 10 -5
    20 150 70 20
    «» hi::incrSize 15
    20 150 85 35

hi::initRect

Initialize the coordinates of a rectangle. The width and height arguments are optional (0 by default). The syntax is:
hi::initRect ?width 0? ?height 0? ?rectName ""?

Example:
    «» hi::defaultRect vb
    «» hi::initRect 60 25
    0 0 60 25

hi::inset

Resize a rectangle symmetrically. The syntax is:
hi::inset xdelta ?ydelta ""? ?rectName ""?
If the xdelta argument is positive, both the left and right edges are moved inwards. If it is negative, they are moved in the opposite direction. Same thing with the ydelta argument wrt the top and bottom edges. If the ydelta argument is omitted, it is assumed equal to xdelta. The proc raises an error if any of the new dimensions becomes negative.
Example:
    «» hi::setRect 20 150 60 25
    «» hi::inset 5 2
    25 152 50 21
    «» hi::inset -10
    15 142 70 41

hi::intersection

Return the smallest rectangle containing the intersection of all the given rectangles. The syntax is:
hi::intersection rectList
The proc raises an error if the intersection is empty. The rectangles in the rectList argument may be expressed in any form accepted by the
hi::adjust proc.
Example:
    «» hi::setRect 200 250 60 70 r1
    «» hi::setRect 150 260 100 90 r2
    «» hi::intersection r1 r2
    200 260 50 60
    «» hi::setRect 270 260 100 90 r3
    «» hi::intersection r1 r3
    Error: empty intersection

hi::left

Get the left x-coord of a named rectangle. The syntax is:
hi::left ?rect ""?
The rect argument can be specified in any of the forms explained in the
Rectangles specification section.
Example:
    «» hi::setRect 20 150 60 25
    «» hi::left
    20

hi::lf

Set the position of the view rectangle under the last occupied position. The syntax can take two forms:
hi::lf ?rectName ""?

hi::lf width height ?rectName ""?

In the second form, the width and height are modified and the new values are used for the calculations.
This proc is a special case of the
hi::advance proc with the direction set to down. This is the equivalent of a line feed by the amount of the height of the last view rect, plus the vertical interspace.
Example:
    «» hi::setRect 20 150 60 25
    «» hi::lf
    20 120 60 25
    «» hi::lf 80 35
    20 80 80 35

hi::margin

Get or set the margin variable. The syntax is:
hi::margin ?value ""?

Example:
    «» hi::margin 
    13
    «» hi::margin 8
    8
    «» hi::margin 
    8

hi::marginPoint

Given a rectangle, return the position of a point on the inner margin. The syntax is:
hi::marginPoint where ?rectName ""?
The where argument has the same meaning as with the
hi::adjust proc. This is useful to find the coordinates of the corner points taking the margins into account or the coordinates of the center.
Example:
    «» hi::setRect 200 300 50 70
    «» hi::marginPoint tl 
    213 357
    «» hi::marginPoint tc
    225.0 357
    «» hi::marginPoint tr
    237 357
    «» hi::marginPoint cc
    225.0 335.0
    «» hi::marginPoint br
    237 313
    «» hi::marginPoint bl
    213 313

hi::origin

Get the coordinates of the origin of a rectangle. The syntax is:
hi::origin ?rect ""?
The proc returns a two-element list containing the x-coord and y-coord of the lower left corner of the rectangle. The rect argument can be specified in any of the forms explained in the
Rectangles specification section.
Example:
    «» hi::setRect 20 150 60 25
    «» hi::origin 
    20 150

hi::rect

Get the list of the {x y w h} coords of a rectangle. The syntax is:
hi::rect ?rectName ""?

Example:
    «» hi::setRect 20 150 60 25
    «» hi::rect
    20 150 60 25

hi::relativeRect

Build a rectangle given an anchor point {x y}, a size {w h} and a position indicating where the anchor point should be located in the rectangle. The syntax is:
hi::relativeRect where anchor size
The syntax of the where argument is the same as with the
hi::adjust proc. The proc returns a rectangle in the usual representation (first two coordinates corresponding to the lower left corner). Width and height are unchanged.
Example:
    «» set anchor [list 120 160]
    «» set size [list 50 30]
    «» hi::relativeRect tl $anchor $size
    120 130 50 30
    «» hi::relativeRect tr $anchor $size
    70 130 50 30
    «» hi::relativeRect tc $anchor $size
    95.0 130 50 30
    «» hi::relativeRect bl $anchor $size
    120 160 50 30
    «» hi::relativeRect br $anchor $size
    70 160 50 30
    «» hi::relativeRect bc $anchor $size
    95.0 160 50 30
    «» hi::relativeRect lc $anchor $size
    120 145.0 50 30
    «» hi::relativeRect cc $anchor $size
    95.0 145.0 50 30
    «» hi::relativeRect rc $anchor $size
    70 145.0 50 30

hi::repositionRect

Given a named rectangle, reposition it relatively to a given anchor. The syntax is:
hi::repositionRect where anchor ?rectName ""?
The where and anchor arguments have the exact same meaning as with the
hi::relativeRect proc.
Example:
    «» hi::setRect 200 300 50 30
    «» set anchor [list 120 160]
    «» hi::repositionRect cc $anchor
    95.0 145.0 50 30
    «» hi::repositionRect tl $anchor
    120 130 50 30

hi::right

Get the right x-coord of a named rectangle. The syntax is:
hi::right ?rect ""?
The rect argument can be specified in any of the forms explained in the
Rectangles specification section.
Example:
    «» hi::setRect 20 150 60 25
    «» hi::right
    80

hi::setHeight

Resize a rectangle's height. The syntax is:
hi::setHeight ht ?rectName ""?
This does not modify the origin's coordinates, nor the width.
Example:
    «» hi::setRect 20 150 60 25
    «» hi::setHeight 300
    20 150 60 300

hi::setOrigin

Move the origin of a rectangle to a new position. The syntax is:
hi::setOrigin xcoord ycoord ?rectName ""?
The xcoord and ycoord arguments are the coordinates of the new bottom left corner for the rectangle.
Example:
    «» hi::setRect 20 150 60 25
    «» hi::setOrigin 10 90
    10 90 60 25

hi::setRect

Set the {x y w h} coordinates for a rectangle, i-e x-coord, y-coord, width and height. The syntax is:
hi::setRect x y w h ?rectName ""?
Note that the coordinates are specified as separate arguments and not as a four-elements list.
Example:
    «» hi::setRect 20 150 60 25
    20 150 60 25

hi::setSize

Resize a rectangle. The syntax is:
hi::setSize wd ?ht ""? ?rectName ""?
If the ht argument is omitted, it is assumed equal to wd. This does not modify the origin's coordinates.
Example:
    «» hi::setRect 20 150 60 25
    «» hi::setSize 80 300
    20 150 80 300
    «» hi::setSize 50
    20 150 50 50

hi::setWidth

Resize a rectangle's width. The syntax is:
hi::setWidth wd ?rectName ""?
This does not modify the origin's coordinates, nor the height.
Example:
    «» hi::setRect 20 150 60 25
    «» hi::setWidth 80
    20 150 80 25

hi::shift

Move a rectangle by the specified amount relatively to the current position. The syntax is:
hi::shift xdelta ?ydelta ""? ?rectName ""?
The xdelta and ydelta arguments are the horizontal and vertical offsets in pixels respectively. A positive value for xdelta moves the rectangle to the right. A positive value for ydelta moves the rectangle upwards. If the ydelta argument is omitted, it is assumed equal to xdelta.
If you need to move the rectangle in one direction only, you can also use
hi::hskip or hi::vskip.
Example:
    «» hi::setRect 20 150 60 25
    «» hi::shift 10 20
    30 170 60 25
    «» hi::shift -5 -10
    25 160 60 25
    «» hi::shift 15
    40 175 60 25

hi::size

Get the dimensions of a rectangle. The syntax is:
hi::size ?rect ""?
The proc returns a two-element list containing the width and height of the rectangle. The rect argument can be specified in any of the forms explained in the
Rectangles specification section.
Example:
    «» hi::setRect 20 150 60 25
    «» hi::size
    60 25

hi::top

Get the top y-coord of a named rectangle. The syntax is:
hi::top ?rect ""?
The rect argument can be specified in any of the forms explained in the
Rectangles specification section.
Example:
    «» hi::setRect 20 150 60 25
    «» hi::top
    175

hi::union

Return the smallest rectangle containing the union of all the given rectangles. The syntax is:
hi::union rectList
The rectangles in the rectList argument may be expressed in any form accepted by the
hi::adjust proc.
Example:
    «» hi::setRect 200 250 60 70 r1
    «» hi::setRect 150 260 100 90 r2
    «» hi::union r1 r2
    150 250 110 100

hi::vskip

Move the view rectangle vertically by the specified amount. The syntax is:
hi::vskip offset ?rectName ""?
This is synonym of:
        hi::shift 0 $offset
The idea is that a view layout is composed from top to bottom, so a positive value of the offset moves the rectangle downwards. Specify a negative offset to move the rectangle upwards.
Example:
    «» hi::setRect 20 150 60 25
    «» hi::vskip 10
    20 140 60 25
    «» hi::vskip -20
    20 160 60 25

hi::vspace

Get or set the vspace variable which defines the vertical spacing between items. The syntax is:
hi::vspace ?value ""?
The vertical spacing is automatically inserted by the
hi::cr and hi::lf procs.
Example:
    «» hi::vspace 
    5
    «» hi::vspace 8
    8
    «» hi::vspace 
    8

hi::width

Get the width of a named rectangle. The syntax is:
hi::width ?rect ""?
The rect argument can be specified in any of the forms explained in the
Rectangles specification section.
Example:
    «» hi::setRect 20 150 60 25
    «» hi::width
    60

Complete example


Here is a complete example taken from the in the
ViewRects package section of the [view] command's documentation. The default rectangle here is named vb (which stands for view bounds) and it is successively resized or moved for each subview contained in the root window. The subviews are thus positionned relatively to each other:
hi::setRect 250 250 360 380 wb
hi::defaultRect vb
hi::initRect 340 180
hi::adjust tl [hi::size wb]

# Create a root window
set root [view root Document -frame $wb]
view configure $root -title "View Sample"

# Create some subviews
# A scroll view
set scrTkn [view create ScrollView -frame [hi::rect] -parent $root]
view configure $scrTkn -attr 11

# An image view
set imgFile [file join $APPLICATION Contents Resources about.png]
set img [imageRef create -file $imgFile]
set imgTkn [view create ImageView -frame [imageRef size $img]]
view configure $imgTkn -image $img

# Let the scroll view control the image
view configure $scrTkn -view $imgTkn

# A combo box
hi::setSize 180 25
hi::lf
set cbTkn [view create ComboBox -frame [hi::center [hi::width wb]] -parent $root]
view configure $cbTkn -items [list item1 item2 item3]

# A text edit field
hi::lf
set tfTkn [view create TextField -frame [hi::rect] -parent $root]
view configure $tfTkn -placeholder "Enter something"

# A date picker
hi::lf
set dpTkn [view create DatePicker -frame [hi::rect] -parent $root]
view configure $dpTkn -dateValue [clock seconds]

# A checkbox
hi::cr
set chTkn [view create Button -frame [hi::rect] -parent $root]
view configure $chTkn -type 3 -title "I like!" -value 1

# # A button
hi::addOkCancelButtons $root [list "Click here"]

# Display the window
view show $root




Last updated 2016-11-23 15:54:13