source: trunk/gui/scripts/gauge.tcl @ 738

Last change on this file since 738 was 738, checked in by mmc, 17 years ago

Fixed a problem recently introduced with device structures on the
input side. app-rtd was having trouble changing the structure when
you changed devices via the loader.

Fix for support ticket #1631 'can't read "_axis(click-x)": no such
variable'. Added some code to guard against the case when release
gets called somehow before click.

Fix for support ticket #1688 'can't use empty string as operand of "-"'
Fix for support ticket #1689 'divide by zero'
Fix for support ticket #1707 'can't read "_dobj2cols(-energy)":
no such element in array'
All of these fixes had to do with the energy viewer, particularly
in the case where there was only 1 energy level, so the homo/lumo
levels could not be displayed.

Fix for support ticket #1704 'impossible limits (min 1.58489 >=
max 6.30957e-05)'
Added some code to guard against setting limits where min >= max.

File size: 24.6 KB
Line 
1# ----------------------------------------------------------------------
2#  COMPONENT: gauge - compact readout for real values
3#
4#  This widget is a readout for a real value.  It has a little glyph
5#  filled with color according to the value, followed by a numeric
6#  representation of the value itself.  The value can be edited, and
7#  a list of predefined values can be associated with a menu that
8#  drops down from the value.
9# ======================================================================
10#  AUTHOR:  Michael McLennan, Purdue University
11#  Copyright (c) 2004-2005  Purdue Research Foundation
12#
13#  See the file "license.terms" for information on usage and
14#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
15# ======================================================================
16package require Itk
17package require BLT
18
19option add *Gauge.sampleWidth 30 widgetDefault
20option add *Gauge.sampleHeight 20 widgetDefault
21option add *Gauge.valuePosition "right" widgetDefault
22option add *Gauge.textBackground #cccccc widgetDefault
23option add *Gauge.editable yes widgetDefault
24
25itcl::class Rappture::Gauge {
26    inherit itk::Widget
27
28    itk_option define -editable editable Editable ""
29    itk_option define -state state State "normal"
30    itk_option define -spectrum spectrum Spectrum ""
31    itk_option define -type type Type "real"
32    itk_option define -units units Units ""
33    itk_option define -minvalue minValue MinValue ""
34    itk_option define -maxvalue maxValue MaxValue ""
35    itk_option define -presets presets Presets ""
36    itk_option define -valueposition valuePosition ValuePosition ""
37    itk_option define -image image Image ""
38    itk_option define -samplewidth sampleWidth SampleWidth 0
39    itk_option define -sampleheight sampleHeight SampleHeight 0
40
41    constructor {args} { # defined below }
42
43    public method value {args}
44    public method edit {option}
45    public method bump {delta}
46
47    protected method _redraw {}
48    protected method _resize {}
49    protected method _hilite {comp state}
50    protected method _editor {option args}
51    protected method _presets {option}
52    protected method _layout {}
53
54    private variable _value 0  ;# value for this widget
55
56    blt::bitmap define GaugeArrow-up {
57        #define up_width 8
58        #define up_height 4
59        static unsigned char up_bits[] = {
60           0x10, 0x38, 0x7c, 0xfe};
61    }
62    blt::bitmap define GaugeArrow-down {
63        #define arrow_width 8
64        #define arrow_height 4
65        static unsigned char arrow_bits[] = {
66           0xfe, 0x7c, 0x38, 0x10};
67    }
68
69    blt::bitmap define GaugeArrow {
70        #define arrow_width 9
71        #define arrow_height 4
72        static unsigned char arrow_bits[] = {
73           0x7f, 0x00, 0x3e, 0x00, 0x1c, 0x00, 0x08, 0x00};
74    }
75}
76                                                                               
77itk::usual Gauge {
78    keep -cursor -font -foreground -background
79    keep -selectbackground -selectforeground -selectborderwidth
80}
81
82# ----------------------------------------------------------------------
83# CONSTRUCTOR
84# ----------------------------------------------------------------------
85itcl::body Rappture::Gauge::constructor {args} {
86    itk_component add icon {
87        canvas $itk_interior.icon -width 1 -height 1 \
88            -borderwidth 0 -highlightthickness 0
89    } {
90        usual
91        ignore -highlightthickness -highlightbackground -highlightcolor
92    }
93    pack $itk_component(icon) -side left
94    bind $itk_component(icon) <Configure> [itcl::code $this _redraw]
95
96    itk_component add -protected vframe {
97        frame $itk_interior.vframe
98    }
99
100    itk_component add value {
101        label $itk_component(vframe).value -borderwidth 1 -width 7 \
102            -textvariable [itcl::scope _value]
103    } {
104        rename -background -textbackground textBackground Background
105    }
106    pack $itk_component(value) -side left -expand yes -fill both
107
108    bind $itk_component(value) <Enter> [itcl::code $this _hilite value on]
109    bind $itk_component(value) <Leave> [itcl::code $this _hilite value off]
110
111    bind $itk_component(value) <<Cut>> [itcl::code $this edit cut]
112    bind $itk_component(value) <<Copy>> [itcl::code $this edit copy]
113    bind $itk_component(value) <<Paste>> [itcl::code $this edit paste]
114
115    itk_component add emenu {
116        menu $itk_component(value).menu -tearoff 0
117    } {
118        usual
119        ignore -tearoff
120    }
121    $itk_component(emenu) add command -label "Cut" -accelerator "^X" \
122        -command [list event generate $itk_component(value) <<Cut>>]
123    $itk_component(emenu) add command -label "Copy" -accelerator "^C" \
124        -command [list event generate $itk_component(value) <<Copy>>]
125    $itk_component(emenu) add command -label "Paste" -accelerator "^V" \
126        -command [list event generate $itk_component(value) <<Paste>>]
127    bind $itk_component(value) <<PopupMenu>> \
128        [itcl::code $this _editor menu %X %Y]
129
130    itk_component add editor {
131        Rappture::Editor $itk_interior.editor \
132            -activatecommand [itcl::code $this _editor activate] \
133            -validatecommand [itcl::code $this _editor validate] \
134            -applycommand [itcl::code $this _editor apply]
135    }
136    bind $itk_component(value) <ButtonPress> \
137        [itcl::code $this _editor popup]
138
139
140    itk_component add spinner {
141        frame $itk_component(vframe).spinner
142    }
143
144    itk_component add spinup {
145        button $itk_component(spinner).up -bitmap GaugeArrow-up \
146            -borderwidth 1 -relief raised -highlightthickness 0 \
147            -command [itcl::code $this bump 1]
148    } {
149        usual
150        ignore -borderwidth -highlightthickness
151    }
152    pack $itk_component(spinup) -side top -expand yes -fill both
153
154    itk_component add spindn {
155        button $itk_component(spinner).down -bitmap GaugeArrow-down \
156            -borderwidth 1 -relief raised -highlightthickness 0 \
157            -command [itcl::code $this bump -1]
158    } {
159        usual
160        ignore -borderwidth -highlightthickness
161    }
162    pack $itk_component(spindn) -side bottom -expand yes -fill both
163
164
165    itk_component add presets {
166        button $itk_component(vframe).psbtn -bitmap GaugeArrow \
167            -borderwidth 1 -highlightthickness 0 -relief flat
168    } {
169        usual
170        ignore -borderwidth -relief -highlightthickness
171        rename -background -textbackground textBackground Background
172    }
173
174    bind $itk_component(presets) <Enter> [itcl::code $this _hilite presets on]
175    bind $itk_component(presets) <Leave> [itcl::code $this _hilite presets off]
176
177    itk_component add presetlist {
178        Rappture::Dropdownlist $itk_component(presets).plist \
179            -postcommand [itcl::code $this _presets post] \
180            -unpostcommand [itcl::code $this _presets unpost] \
181    }
182
183    bind $itk_component(presetlist) <<DropdownlistSelect>> \
184        [itcl::code $this _presets select]
185
186    $itk_component(presets) configure -command \
187        [list $itk_component(presetlist) post $itk_component(vframe) left]
188
189    eval itk_initialize $args
190}
191
192# ----------------------------------------------------------------------
193# USAGE: value ?-check? ?<newval>?
194#
195# Clients use this to query/set the value for this widget.  With
196# no args, it returns the current value for the widget.  If the
197# <newval> is specified, it sets the value of the widget and
198# sends a <<Value>> event.  If the -check flag is included, the
199# new value is not actually applied, but just checked for correctness.
200# ----------------------------------------------------------------------
201itcl::body Rappture::Gauge::value {args} {
202    set onlycheck 0
203    set i [lsearch -exact $args -check]
204    if {$i >= 0} {
205        set onlycheck 1
206        set args [lreplace $args $i $i]
207    }
208
209    if {[llength $args] == 1} {
210        #
211        # If this gauge has -units, try to convert the incoming
212        # value to that system of units.  Also, make sure that
213        # the value is bound by any min/max value constraints.
214        #
215        # Keep track of the inputted units so we can give a
216        # response about min and max values in familiar units.
217        #
218        set newval [set nv [lindex $args 0]]
219        set units $itk_option(-units)
220        if {"" != $units} {
221            set newval [Rappture::Units::convert $newval -context $units]
222            set nvUnits [Rappture::Units::Search::for $newval]
223            if { "" == $nvUnits} {
224                set msg [Rappture::Units::description $units]
225                error "Unrecognized units: $newval\nEnter value with units of $msg"
226            }
227            set nv [Rappture::Units::convert $nv \
228                -context $units -to $units -units off]
229
230            # Normalize the units name
231            set newval [Rappture::Units::convert $newval -units off]$nvUnits
232        }
233
234        switch -- $itk_option(-type) {
235            integer {
236                if {![string is integer $nv]} {
237                    error "Should be an integer value"
238                }
239            }
240            real {
241                if {![string is double $nv]
242                      || [regexp -nocase {^(inf|nan)$} $nv]} {
243                    error "Should be a real number"
244                }
245            }
246        }
247
248        if {"" != $itk_option(-minvalue)} {
249            set convMinVal [set minv $itk_option(-minvalue)]
250            if {"" != $units} {
251                set minv [Rappture::Units::convert $minv \
252                    -context $units -to $units -units off]
253                set convMinVal [Rappture::Units::convert \
254                    $itk_option(-minvalue) -context $units -to $nvUnits]
255            } else {
256                set newval [format "%g" $newval]
257            }
258
259            # fix for the case when the user tries to
260            # compare values like minv=-500 nv=-0600
261            set nv [format "%g" $nv]
262            set minv [format "%g" $minv]
263
264            if {$nv < $minv} {
265                error "minimum value allowed here is $convMinVal"
266            }
267        }
268
269        if {"" != $itk_option(-maxvalue)} {
270            set convMaxVal [set maxv $itk_option(-maxvalue)]
271            if {"" != $units} {
272                set maxv [Rappture::Units::convert $maxv \
273                    -context $units -to $units -units off]
274                set convMaxVal [Rappture::Units::convert \
275                    $itk_option(-maxvalue) -context $units -to $nvUnits]
276            } else {
277                set newval [format "%g" $newval]
278            }
279
280            # fix for the case when the user tries to
281            # compare values like maxv=500 nv=0600
282            set nv [format "%g" $nv]
283            set maxv [format "%g" $maxv]
284
285            if {$nv > $maxv} {
286                error "maximum value allowed here is $convMaxVal"
287            }
288        }
289
290        if {$onlycheck} {
291            return
292        }
293
294        set _value $newval
295
296        _redraw
297        event generate $itk_component(hull) <<Value>>
298
299    } elseif {[llength $args] != 0} {
300        error "wrong # args: should be \"value ?-check? ?newval?\""
301    }
302    return $_value
303}
304
305# ----------------------------------------------------------------------
306# USAGE: edit cut
307# USAGE: edit copy
308# USAGE: edit paste
309#
310# Used internally to handle cut/copy/paste operations for the current
311# value.  Usually invoked by <<Cut>>, <<Copy>>, <<Paste>> events, but
312# can also be called directly through this method.
313# ----------------------------------------------------------------------
314itcl::body Rappture::Gauge::edit {option} {
315    if {$itk_option(-state) == "disabled"} {
316        return  ;# disabled? then bail out here!
317    }
318    switch -- $option {
319        cut {
320            edit copy
321            _editor popup
322            $itk_component(editor) value ""
323            $itk_component(editor) deactivate
324        }
325        copy {
326            clipboard clear
327            clipboard append $_value
328        }
329        paste {
330            _editor popup
331            $itk_component(editor) value [clipboard get]
332            $itk_component(editor) deactivate
333        }
334        default {
335            error "bad option \"$option\": should be cut, copy, paste"
336        }
337    }
338}
339
340# ----------------------------------------------------------------------
341# USAGE: bump <delta>
342#
343# Changes the current value up/down by the <delta> value.  Used
344# internally by the up/down spinner buttons when the value is
345# -type integer.
346# ----------------------------------------------------------------------
347itcl::body Rappture::Gauge::bump {delta} {
348    set val $_value
349    if {$val == ""} {
350        set val 0
351    }
352    if {[catch {value [expr {$val+$delta}]} result]} {
353        if {[regexp {allowed here is (.+)} $result match newval]} {
354            set _value $newval
355            $itk_component(value) configure -text $newval
356        }
357        if {[regexp {^bad.*: +(.)(.+)} $result match first tail]
358              || [regexp {(.)(.+)} $result match first tail]} {
359            set result "[string toupper $first]$tail"
360        }
361        bell
362        Rappture::Tooltip::cue $itk_component(value) $result
363        return 0
364    }
365}
366
367# ----------------------------------------------------------------------
368# USAGE: _redraw
369#
370# Used internally to redraw the gauge on the internal canvas based
371# on the current value and the size of the widget.  In this simple
372# base class, the gauge is drawn as a colored block, with an optional
373# image in the middle of it.
374# ----------------------------------------------------------------------
375itcl::body Rappture::Gauge::_redraw {} {
376    set c $itk_component(icon)
377    set w [winfo width $c]
378    set h [winfo height $c]
379
380    if {"" == [$c find all]} {
381        # first time around, create the items
382        $c create rectangle 0 0 1 1 -outline black -tags block
383        $c create image 0 0 -anchor center -image "" -tags bimage
384        $c create rectangle 0 0 1 1 -outline "" -fill "" -stipple gray50 -tags screen
385    }
386
387    if {"" != $itk_option(-spectrum)} {
388        set color [$itk_option(-spectrum) get $_value]
389    } else {
390        set color ""
391    }
392
393    # update the items based on current values
394    $c coords block 0 0 [expr {$w-1}] [expr {$h-1}]
395    $c coords screen 0 0 $w $h
396    $c itemconfigure block -fill $color
397
398    $c coords bimage [expr {0.5*$w}] [expr {0.5*$h}]
399
400    if {$itk_option(-state) == "disabled"} {
401        $c itemconfigure screen -fill white
402    } else {
403        $c itemconfigure screen -fill ""
404    }
405}
406
407# ----------------------------------------------------------------------
408# USAGE: _resize
409#
410# Used internally to resize the internal canvas based on the -image
411# option or the size of the text.
412# ----------------------------------------------------------------------
413itcl::body Rappture::Gauge::_resize {} {
414    set w 0
415    set h 0
416
417    if {"" != $itk_option(-image) || "" != $itk_option(-spectrum)} {
418        if {$itk_option(-samplewidth) > 0} {
419            set w $itk_option(-samplewidth)
420        } else {
421            if {$itk_option(-image) != ""} {
422                set w [expr {[image width $itk_option(-image)]+4}]
423            } else {
424                set w [winfo reqheight $itk_component(value)]
425            }
426        }
427
428        if {$itk_option(-sampleheight) > 0} {
429            set h $itk_option(-sampleheight)
430        } else {
431            if {$itk_option(-image) != ""} {
432                set h [expr {[image height $itk_option(-image)]+4}]
433            } else {
434                set h [winfo reqheight $itk_component(value)]
435            }
436        }
437    }
438
439    if {$w > 0 && $h > 0} {
440        $itk_component(icon) configure -width $w -height $h
441    }
442}
443
444# ----------------------------------------------------------------------
445# USAGE: _hilite <component> <state>
446#
447# Used internally to resize the internal canvas based on the -image
448# option or the size of the text.
449# ----------------------------------------------------------------------
450itcl::body Rappture::Gauge::_hilite {comp state} {
451    if {$itk_option(-state) == "disabled"} {
452        set state 0  ;# disabled? then don't hilite
453    }
454    if {$comp == "value" && !$itk_option(-editable)} {
455        $itk_component(value) configure -relief flat
456        return
457    }
458
459    if {$state} {
460        $itk_component($comp) configure -relief solid
461    } else {
462        $itk_component($comp) configure -relief flat
463    }
464}
465
466# ----------------------------------------------------------------------
467# USAGE: _editor popup
468# USAGE: _editor activate
469# USAGE: _editor validate <value>
470# USAGE: _editor apply <value>
471# USAGE: _editor menu <rootx> <rooty>
472#
473# Used internally to handle the various functions of the pop-up
474# editor for the value of this gauge.
475# ----------------------------------------------------------------------
476itcl::body Rappture::Gauge::_editor {option args} {
477    if {$itk_option(-state) == "disabled"} {
478        return  ;# disabled? then bail out here!
479    }
480    switch -- $option {
481        popup {
482            if {$itk_option(-editable)} {
483                $itk_component(editor) activate
484            }
485        }
486        activate {
487            return [list text $_value \
488                x [winfo rootx $itk_component(value)] \
489                y [winfo rooty $itk_component(value)] \
490                w [winfo width $itk_component(value)] \
491                h [winfo height $itk_component(value)]]
492        }
493        validate {
494            if {[llength $args] != 1} {
495                error "wrong # args: should be \"_editor validate val\""
496            }
497            set val [lindex $args 0]
498
499            if {[catch {value -check $val} result]} {
500                if {[regexp {allowed here is (.+)} $result match newval]} {
501                    $itk_component(editor) value $newval
502                }
503                if {[regexp {^bad.*: +(.)(.+)} $result match first tail]
504                      || [regexp {(.)(.+)} $result match first tail]} {
505                    set result "[string toupper $first]$tail"
506                }
507                bell
508                Rappture::Tooltip::cue $itk_component(editor) $result
509                return 0
510            }
511        }
512        apply {
513            if {[llength $args] != 1} {
514                error "wrong # args: should be \"_editor apply val\""
515            }
516            value [lindex $args 0]
517        }
518        menu {
519            eval tk_popup $itk_component(emenu) $args
520        }
521        default {
522            error "bad option \"$option\": should be popup, activate, validate, apply, and menu"
523        }
524    }
525}
526
527# ----------------------------------------------------------------------
528# USAGE: _presets post
529# USAGE: _presets unpost
530# USAGE: _presets select
531#
532# Used internally to handle the list of presets for this gauge.  The
533# post/unpost options are invoked when the list is posted or unposted
534# to manage the relief of the controlling button.  The select option
535# is invoked whenever there is a selection from the list, to assign
536# the value back to the gauge.
537# ----------------------------------------------------------------------
538itcl::body Rappture::Gauge::_presets {option} {
539    switch -- $option {
540        post {
541            set i [$itk_component(presetlist) index $_value]
542            if {$i >= 0} {
543                $itk_component(presetlist) select clear 0 end
544                $itk_component(presetlist) select set $i
545            }
546            after 10 [list $itk_component(presets) configure -relief sunken]
547        }
548        unpost {
549            $itk_component(presets) configure -relief flat
550        }
551        select {
552            set val [$itk_component(presetlist) current]
553            if {"" != $val} {
554                value $val
555            }
556        }
557        default {
558            error "bad option \"$option\": should be post, unpost, select"
559        }
560    }
561}
562
563# ----------------------------------------------------------------------
564# USAGE: _layout
565#
566# Used internally to fix the layout of widgets whenever there is a
567# change in the options that affect layout.  Puts the value in the
568# proper position according to the -valueposition option.  Also,
569# adds or removes the icon if it needs to be shown.
570# ----------------------------------------------------------------------
571itcl::body Rappture::Gauge::_layout {} {
572    foreach w [pack slaves $itk_component(hull)] {
573        pack forget $w
574    }
575
576    array set side2anchor {
577        left   e
578        right  w
579        top    s
580        bottom n
581    }
582    set pos $itk_option(-valueposition)
583    pack $itk_component(vframe) -side $pos \
584        -expand yes -fill both -ipadx 2
585    $itk_component(value) configure -anchor $side2anchor($pos)
586
587    if {"" != $itk_option(-image) || "" != $itk_option(-spectrum)} {
588        pack $itk_component(icon) -side $pos
589    }
590}
591
592# ----------------------------------------------------------------------
593# CONFIGURATION OPTION: -editable
594# ----------------------------------------------------------------------
595itcl::configbody Rappture::Gauge::editable {
596    if {![string is boolean -strict $itk_option(-editable)]} {
597        error "bad value \"$itk_option(-editable)\": should be boolean"
598    }
599    if {!$itk_option(-editable) && [winfo ismapped $itk_component(editor)]} {
600        $itk_component(editor) deactivate -abort
601    }
602}
603
604# ----------------------------------------------------------------------
605# CONFIGURATION OPTION: -state
606# ----------------------------------------------------------------------
607itcl::configbody Rappture::Gauge::state {
608    set valid {normal disabled}
609    if {[lsearch -exact $valid $itk_option(-state)] < 0} {
610        error "bad value \"$itk_option(-state)\": should be [join $valid {, }]"
611    }
612    $itk_component(value) configure -state $itk_option(-state)
613    $itk_component(spinup) configure -state $itk_option(-state)
614    $itk_component(spindn) configure -state $itk_option(-state)
615    $itk_component(presets) configure -state $itk_option(-state)
616    _redraw  ;# fix gauge
617}
618
619# ----------------------------------------------------------------------
620# CONFIGURATION OPTION: -spectrum
621# ----------------------------------------------------------------------
622itcl::configbody Rappture::Gauge::spectrum {
623    if {$itk_option(-spectrum) != ""
624          && ([catch {$itk_option(-spectrum) isa ::Rappture::Spectrum} valid]
625               || !$valid)} {
626        error "bad option \"$itk_option(-spectrum)\": should be Rappture::Spectrum object"
627    }
628    _resize
629    _layout
630    _redraw
631}
632
633# ----------------------------------------------------------------------
634# CONFIGURATION OPTION: -image
635# ----------------------------------------------------------------------
636itcl::configbody Rappture::Gauge::image {
637    if {$itk_option(-image) != ""
638          && [catch {image width $itk_option(-image)}]} {
639        error "bad value \"$itk_option(-image)\": should be Tk image"
640    }
641    _resize
642    _layout
643    $itk_component(icon) itemconfigure bimage -image $itk_option(-image)
644}
645
646# ----------------------------------------------------------------------
647# CONFIGURATION OPTION: -units
648# ----------------------------------------------------------------------
649itcl::configbody Rappture::Gauge::units {
650    if {$itk_option(-units) != ""
651          && [::Rappture::Units::System::for $itk_option(-units)] == ""} {
652        error "unrecognized system of units \"$itk_option(-units)\""
653    }
654}
655
656# ----------------------------------------------------------------------
657# CONFIGURATION OPTION: -valueposition
658# ----------------------------------------------------------------------
659itcl::configbody Rappture::Gauge::valueposition {
660    set pos $itk_option(-valueposition)
661    set opts {left right top bottom}
662    if {[lsearch -exact $opts $pos] < 0} {
663        error "bad value \"$pos\": should be [join $opts {, }]"
664    }
665    _layout
666}
667
668# ----------------------------------------------------------------------
669# CONFIGURATION OPTION: -presets
670# ----------------------------------------------------------------------
671itcl::configbody Rappture::Gauge::presets {
672    if {"" == $itk_option(-presets)} {
673        pack forget $itk_component(presets)
674    } else {
675        if {$itk_option(-valueposition) == "left"} {
676            set s "left"
677        } else {
678            set s "right"
679        }
680        set first [lindex [pack slaves $itk_component(vframe)] 0]
681        pack $itk_component(presets) -before $first -side $s -fill y
682
683        $itk_component(presetlist) delete 0 end
684        $itk_component(presetlist) insert end $itk_option(-presets)
685    }
686}
687
688# ----------------------------------------------------------------------
689# CONFIGURATION OPTION: -type
690# ----------------------------------------------------------------------
691itcl::configbody Rappture::Gauge::type {
692    switch -- $itk_option(-type) {
693        integer {
694            set first [lindex [pack slaves $itk_component(vframe)] 0]
695            if {$first == $itk_component(presets)} {
696                pack $itk_component(spinner) -after $first -side left -fill y
697            } else {
698                pack $itk_component(spinner) -before $first -side right -fill y
699            }
700        }
701        real {
702            pack forget $itk_component(spinner)
703        }
704        default {
705            error "bad number type \"$itk_option(-type)\": should be integer or real"
706        }
707    }
708}
Note: See TracBrowser for help on using the repository browser.