source: trunk/gui/scripts/molvisviewer.tcl @ 1277

Last change on this file since 1277 was 1277, checked in by gah, 15 years ago

scripts/xyresult.tcl:

Changed buttons to use new icons (reset and wrench for legend).

scripts/nanovisviewer.tcl:

Changed buttons to use new icons (reset, zoom-in, and zoom-out).

scripts/visviewer.tcl:

Added drawer (pandedwindow) to base class so derived classes
could use it if so desired.

scripts/scroller.tcl:

Added scrollwheel binds for vertical scrolling.

scripts/images/atom-label.gif, scripts/images/reset-view.gif,
scripts/images/wrench.gif, scripts/images/zoom-in.gif,
scripts/images/zoom-out.gif, scripts/images/rock-view.gif:

Added new icons.

scripts/molvisviewer.tcl:

Added projection button to change between orthoscopic and perspective
projects.
Changed settings button into icon.
Moved settings menu into settings drawer.
Added scale to adjust atom scale.
Fixed representation method to not call rebuild when setting
"ballnstick", "lines", or "spheres" representations.
Requires new pymolproxy.

(gah@…)

File size: 46.6 KB
Line 
1
2# ----------------------------------------------------------------------
3#  COMPONENT: molvisviewer - view a molecule in 3D
4#
5#  This widget brings up a 3D representation of a molecule
6#  It connects to the Molvis server running on a rendering farm,
7#  transmits data, and displays the results.
8# ======================================================================
9#  AUTHOR:  Michael McLennan, Purdue University
10#  Copyright (c) 2004-2005  Purdue Research Foundation
11#
12#  See the file "license.terms" for information on usage and
13#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14# ======================================================================
15package require Itk
16package require BLT
17package require Img
18
19option add *MolvisViewer.width 4i widgetDefault
20option add *MolvisViewer.height 4i widgetDefault
21option add *MolvisViewer.foreground black widgetDefault
22option add *MolvisViewer.controlBackground gray widgetDefault
23option add *MolvisViewer.controlDarkBackground #999999 widgetDefault
24option add *MolvisViewer.font -*-helvetica-medium-r-normal-*-12-* widgetDefault
25
26# must use this name -- plugs into Rappture::resources::load
27proc MolvisViewer_init_resources {} {
28    Rappture::resources::register \
29        molvis_server Rappture::MolvisViewer::SetServerList
30}
31
32set debug 0
33proc debug { args } {
34    global debug
35    if { $debug } {
36        puts stderr "[info level -1]: $args"
37    }
38}
39
40itcl::class Rappture::MolvisViewer {
41    inherit Rappture::VisViewer
42
43    itk_option define -device device Device ""
44
45    constructor { hostlist args } {
46        Rappture::VisViewer::constructor $hostlist
47    } {
48        # defined below
49    }
50    destructor {
51        # defined below
52    }
53    public proc SetServerList { namelist } {
54        Rappture::VisViewer::SetServerList "pymol" $namelist
55    }
56    public method Connect {}
57    public method Disconnect {}
58    public method isconnected {}
59    public method download {option args}
60
61    public method add {dataobj {options ""}}
62    public method get {}
63    public method delete {args}
64    public method parameters {title args} { # do nothing }
65
66    public method emblems {option}
67    public method projection {option}
68    public method rock {option}
69    public method representation {option {model "all"} }
70    public method atomscale {option {model "all"} }
71    public method ResetView {}
72    public method settings {option args}
73
74    protected method _send {args}
75    protected method _update { args }
76    protected method _rebuild { }
77    protected method _zoom {option {factor 10}}
78    protected method _pan {option x y}
79    protected method _rotate {option x y}
80    protected method _configure {w h}
81    protected method _unmap {}
82    protected method _map {}
83    protected method _vmouse2 {option b m x y}
84    protected method _vmouse  {option b m x y}
85    private method _ReceiveImage { size cacheid frame rock }
86    private method _BuildSettingsDrawer {}
87    private variable _inrebuild 0
88
89    private variable _mevent       ;# info used for mouse event operations
90    private variable _rocker       ;# info used for rock operations
91    private variable _dlist ""    ;# list of dataobj objects
92    private variable _dataobjs     ;# data objects on server
93    private variable _dobj2transparency  ;# maps dataobj => transparency
94    private variable _dobj2raise  ;# maps dataobj => raise flag 0/1
95    private variable _dobj2ghost
96
97    private variable view_
98    private variable click_
99
100    private variable _model
101    private variable _mlist
102    private variable _mrepresentation "ballnstick"
103
104    private variable _imagecache
105    private variable _state
106    private variable _labels  "default"
107    private variable _cacheid ""
108    private variable _cacheimage ""
109
110    private variable delta1_ 10
111    private variable delta2_ 2
112
113    private common _settings  ;# array of settings for all known widgets
114    private variable initialized_ "no";
115}
116
117itk::usual MolvisViewer {
118    keep -background -foreground -cursor -font
119}
120
121# ----------------------------------------------------------------------
122# CONSTRUCTOR
123# ----------------------------------------------------------------------
124itcl::body Rappture::MolvisViewer::constructor {hostlist args} {
125    # Register events to the dispatcher.  Base class expects !rebuild
126    # event to be registered.
127
128    # Rebuild
129    $_dispatcher register !rebuild
130    $_dispatcher dispatch $this !rebuild "[itcl::code $this _rebuild]; list"
131    # Rocker
132    $_dispatcher register !rocker
133    $_dispatcher dispatch $this !rocker "[itcl::code $this rock step]; list"
134    # Mouse Event
135    $_dispatcher register !mevent
136    $_dispatcher dispatch $this !mevent "[itcl::code $this _mevent]; list"
137
138    # Populate the slave interpreter with commands to handle responses from
139    # the visualization server.
140    $_parser alias image [itcl::code $this _ReceiveImage]
141
142    set _rocker(dir) 1
143    set _rocker(client) 0
144    set _rocker(server) 0
145    set _rocker(on) 0
146    set _state(server) 1
147    set _state(client) 1
148    set _hostlist $hostlist
149
150    array set view_ {
151        theta   45
152        phi     45
153        psi     0
154        vx 0
155        vy 0
156        vz 0
157        zoom 0
158        mx 0
159        my 0
160        mz 0
161        x  0
162        y  0
163        z  0
164        width 0
165        height 0
166    }
167
168    # Setup default settings for widget.
169    array set _settings [subst {
170        $this-model             ballnstick
171        $this-modelimg          [Rappture::icon ballnstick]
172        $this-emblems           no
173        $this-rock              no
174        $this-ortho             no
175        $this-atomscale         0.25
176    }]
177
178    #
179    # Set up the widgets in the main body
180    #
181    itk_component add reset {
182        button $itk_component(controls).reset \
183            -borderwidth 1 -padx 1 -pady 1 \
184            -image [Rappture::icon reset-view] \
185            -command [itcl::code $this ResetView]
186    } {
187        usual
188        ignore -borderwidth
189        rename -highlightbackground -controlbackground controlBackground \
190            Background
191    }
192    pack $itk_component(reset) -padx 1 -pady 2
193    Rappture::Tooltip::for $itk_component(reset) \
194        "Reset the view to the default zoom level"
195
196    itk_component add zoomin {
197        button $itk_component(controls).zin \
198            -borderwidth 1 -padx 1 -pady 1 \
199            -image [Rappture::icon zoom-in] \
200            -command [itcl::code $this _zoom in]
201    } {
202        usual
203        ignore -borderwidth
204        rename -highlightbackground -controlbackground \
205            controlBackground Background
206    }
207    pack $itk_component(zoomin) -padx 2 -pady { 0 2 }
208    Rappture::Tooltip::for $itk_component(zoomin) "Zoom in"
209
210    itk_component add zoomout {
211        button $itk_component(controls).zout \
212            -borderwidth 1 -padx 1 -pady 1 \
213            -image [Rappture::icon zoom-out] \
214            -command [itcl::code $this _zoom out]
215    } {
216        usual
217        ignore -borderwidth
218        rename -highlightbackground -controlbackground controlBackground \
219            Background
220    }
221    pack $itk_component(zoomout) -padx 2 -pady { 0 2 }
222    Rappture::Tooltip::for $itk_component(zoomout) "Zoom out"
223   
224    #
225    # Shortcuts
226    #
227    itk_component add shortcuts {
228        frame $itk_component(controls).shortcuts
229    } {
230        usual
231        rename -background -controlbackground controlBackground Background
232    }
233    pack $itk_component(shortcuts) -side top
234
235    itk_component add labels {
236        label $itk_component(shortcuts).labels \
237            -borderwidth 1 -padx 1 -pady 1 \
238            -relief "raised" -image [Rappture::icon atom-label]
239    } {
240        usual
241        ignore -borderwidth
242        rename -highlightbackground -controlbackground controlBackground \
243            Background
244    }
245    pack $itk_component(labels) -padx 2 -pady { 0 2} -ipadx 1 -ipady 1
246    Rappture::Tooltip::for $itk_component(labels) \
247        "Show/hide the labels on atoms"
248    bind $itk_component(labels) <ButtonPress> \
249        [itcl::code $this emblems toggle]
250
251    itk_component add rock {
252        label $itk_component(shortcuts).rock \
253            -borderwidth 1 -padx 1 -pady 1 \
254            -relief "raised" -image [Rappture::icon rock-view]
255    } {
256        usual
257        ignore -borderwidth
258        rename -highlightbackground -controlbackground controlBackground \
259            Background
260    }
261    pack $itk_component(rock) -padx 2 -pady { 0 2 } -ipadx 1 -ipady 1
262    Rappture::Tooltip::for $itk_component(rock) "Rock model back and forth"
263
264    bind $itk_component(rock) <ButtonPress> \
265        [itcl::code $this rock toggle]
266
267
268    itk_component add ortho {
269        label $itk_component(shortcuts).ortho \
270            -borderwidth 1 -padx 1 -pady 1 \
271            -relief "raised" -image [Rappture::icon 3dpers]
272    } {
273        usual
274        ignore -borderwidth
275        rename -highlightbackground -controlbackground controlBackground \
276            Background
277    }
278    pack $itk_component(ortho) -padx 2 -pady { 0 2 } -ipadx 1 -ipady 1
279    Rappture::Tooltip::for $itk_component(ortho) \
280        "Change to orthoscopic projection"
281
282    bind $itk_component(ortho) <ButtonPress> \
283        [itcl::code $this projection toggle]
284    $this projection perspective
285
286    itk_component add settings_button {
287        label $itk_component(controls).settingsbutton \
288            -borderwidth 1 -padx 1 -pady 1 \
289            -relief "raised" -image [Rappture::icon wrench]
290    } {
291        usual
292        ignore -borderwidth
293        rename -highlightbackground -controlbackground controlBackground \
294            Background
295    }
296    pack $itk_component(settings_button) -padx 2 -pady { 0 2 } -ipadx 1 -ipady 1
297    Rappture::Tooltip::for $itk_component(settings_button) \
298        "Configure settings"
299    bind $itk_component(settings_button) <ButtonPress> \
300        [itcl::code $this settings toggle]
301    pack $itk_component(settings_button) -side bottom \
302        -padx 2 -pady 2 -anchor e
303
304    _BuildSettingsDrawer
305
306    #
307    # RENDERING AREA
308    #
309
310    set _image(id) ""
311
312    # set up bindings for rotation
313    if 0 {
314        bind $itk_component(3dview) <ButtonPress-1> \
315            [itcl::code $this _rotate click %x %y]
316        bind $itk_component(3dview) <B1-Motion> \
317            [itcl::code $this _rotate drag %x %y]
318        bind $itk_component(3dview) <ButtonRelease-1> \
319            [itcl::code $this _rotate release %x %y]
320    } else {
321        bind $itk_component(3dview) <ButtonPress-1> \
322            [itcl::code $this _vmouse click %b %s %x %y]
323        bind $itk_component(3dview) <B1-Motion> \
324            [itcl::code $this _vmouse drag 1 %s %x %y]
325        bind $itk_component(3dview) <ButtonRelease-1> \
326            [itcl::code $this _vmouse release %b %s %x %y]
327    }
328
329    bind $itk_component(3dview) <ButtonPress-2> \
330        [itcl::code $this _pan click %x %y]
331    bind $itk_component(3dview) <B2-Motion> \
332        [itcl::code $this _pan drag %x %y]
333    bind $itk_component(3dview) <ButtonRelease-2> \
334        [itcl::code $this _pan release %x %y]
335
336    bind $itk_component(3dview) <KeyPress-Left> \
337        [itcl::code $this _pan set -10 0]
338    bind $itk_component(3dview) <KeyPress-Right> \
339        [itcl::code $this _pan set 10 0]
340    bind $itk_component(3dview) <KeyPress-Up> \
341        [itcl::code $this _pan set 0 -10]
342    bind $itk_component(3dview) <KeyPress-Down> \
343        [itcl::code $this _pan set 0 10]
344    bind $itk_component(3dview) <Shift-KeyPress-Left> \
345        [itcl::code $this _pan set -50 0]
346    bind $itk_component(3dview) <Shift-KeyPress-Right> \
347        [itcl::code $this _pan set 50 0]
348    bind $itk_component(3dview) <Shift-KeyPress-Up> \
349        [itcl::code $this _pan set 0 -50]
350    bind $itk_component(3dview) <Shift-KeyPress-Down> \
351        [itcl::code $this _pan set 0 50]
352    bind $itk_component(3dview) <KeyPress-Prior> \
353        [itcl::code $this _zoom out 2]
354    bind $itk_component(3dview) <KeyPress-Next> \
355        [itcl::code $this _zoom in 2]
356
357    bind $itk_component(3dview) <Enter> "focus $itk_component(3dview)"
358
359
360    if {[string equal "x11" [tk windowingsystem]]} {
361        bind $itk_component(3dview) <4> [itcl::code $this _zoom out 2]
362        bind $itk_component(3dview) <5> [itcl::code $this _zoom in 2]
363    }
364
365    # set up bindings to bridge mouse events to server
366    #bind $itk_component(3dview) <ButtonPress> \
367    #   [itcl::code $this _vmouse2 click %b %s %x %y]
368    #bind $itk_component(3dview) <ButtonRelease> \
369    #    [itcl::code $this _vmouse2 release %b %s %x %y]
370    #bind $itk_component(3dview) <B1-Motion> \
371    #    [itcl::code $this _vmouse2 drag 1 %s %x %y]
372    #bind $itk_component(3dview) <B2-Motion> \
373    #    [itcl::code $this _vmouse2 drag 2 %s %x %y]
374    #bind $itk_component(3dview) <B3-Motion> \
375    #    [itcl::code $this _vmouse2 drag 3 %s %x %y]
376    #bind $itk_component(3dview) <Motion> \
377    #    [itcl::code $this _vmouse2 move 0 %s %x %y]
378
379    bind $itk_component(3dview) <Configure> \
380        [itcl::code $this _configure %w %h]
381    bind $itk_component(3dview) <Unmap> \
382        [itcl::code $this _unmap]
383    bind $itk_component(3dview) <Map> \
384        [itcl::code $this _map]
385
386    eval itk_initialize $args
387    Connect
388}
389
390itcl::body Rappture::MolvisViewer::_BuildSettingsDrawer {} {
391
392    itk_component add settings {
393        Rappture::Scroller $itk_component(drawer).scrl \
394            -xscrollmode auto -yscrollmode auto \
395            -width 200 -height 100
396    }
397
398    itk_component add settings_canvas {
399        canvas $itk_component(settings).canvas
400    }
401    $itk_component(settings) contents $itk_component(settings_canvas)
402
403    itk_component add settings_frame {
404        frame $itk_component(settings_canvas).frame -bg white
405    }
406    $itk_component(settings_canvas) create window 0 0 \
407        -anchor nw -window $itk_component(settings_frame)
408    bind $itk_component(settings_frame) <Configure> \
409        [itcl::code $this settings resize]
410
411    set fg [option get $itk_component(hull) font Font]
412
413    set inner $itk_component(settings_frame)
414    label $inner.drawinglabel -text "Drawing Method:" -font "Arial 9 bold"
415
416    label $inner.pict -image $_settings($this-modelimg)
417    radiobutton $inner.bstick -text "Balls and sticks" \
418        -command [itcl::code $this representation ballnstick all] \
419        -variable Rappture::MolvisViewer::_settings($this-model) \
420        -value ballnstick -font "Arial 9" -pady 0
421    radiobutton $inner.spheres -text "Spheres" \
422        -command [itcl::code $this representation spheres all] \
423        -variable Rappture::MolvisViewer::_settings($this-model) \
424        -value spheres -font "Arial 9" -pady 0
425    radiobutton $inner.lines -text "Lines" \
426        -command [itcl::code $this representation lines all] \
427        -variable Rappture::MolvisViewer::_settings($this-model) \
428        -value lines -font "Arial 9" -pady 0
429
430    label $inner.sizelabel -text "Atom Scale:" -font "Arial 9 bold"
431    scale $inner.atomscale \
432        -from 0.1 -to 2.0 -resolution 0.05 \
433        -showvalue true -orient horizontal \
434        -command [itcl::code $this atomscale] \
435        -variable Rappture::MolvisViewer::_settings($this-atomscale)
436    $inner.atomscale set $_settings($this-atomscale)
437
438    checkbutton $inner.labels -text "Show labels on atoms" \
439        -command [itcl::code $this emblems update] \
440        -variable Rappture::MolvisViewer::_settings($this-emblems) \
441        -font "Arial 9 bold"
442    checkbutton $inner.rock -text "Rock model back and forth" \
443        -command [itcl::code $this rock toggle] \
444        -variable Rappture::MolvisViewer::_settings($this-rock) \
445        -font "Arial 9 bold"
446    checkbutton $inner.ortho -text "Orthoscopic projection" \
447        -command [itcl::code $this projection update] \
448        -variable Rappture::MolvisViewer::_settings($this-ortho) \
449         -font "Arial 9 bold"
450    blt::table $inner \
451        0,0 $inner.drawinglabel -anchor w -columnspan 4 \
452        1,1 $inner.pict -anchor w -rowspan 3 \
453        1,2 $inner.spheres -anchor w -columnspan 2 \
454        2,2 $inner.lines -anchor w -columnspan 2 \
455        3,2 $inner.bstick -anchor w -columnspan 2 \
456        4,0 $inner.sizelabel -columnspan 4 -anchor w \
457        5,1 $inner.atomscale -anchor w -columnspan 3 \
458        8,0 $inner.labels -anchor w -columnspan 4 \
459        9,0 $inner.rock -anchor w -columnspan 4 \
460        10,0 $inner.ortho -anchor w -columnspan 4
461
462    blt::table configure $inner c0 -resize expand -width 2
463    blt::table configure $inner c1 c2 -resize none
464    blt::table configure $inner c3 -resize expand
465}
466
467
468# ----------------------------------------------------------------------
469# DESTRUCTOR
470# ----------------------------------------------------------------------
471itcl::body Rappture::MolvisViewer::destructor {} {
472    VisViewer::Disconnect
473
474    image delete $_image(plot)
475    array unset _settings $this-*
476}
477
478# ----------------------------------------------------------------------
479# USAGE: download coming
480# USAGE: download controls <downloadCommand>
481# USAGE: download now
482#
483# Clients use this method to create a downloadable representation
484# of the plot.  Returns a list of the form {ext string}, where
485# "ext" is the file extension (indicating the type of data) and
486# "string" is the data itself.
487# ----------------------------------------------------------------------
488itcl::body Rappture::MolvisViewer::download {option args} {
489    switch $option {
490        coming {}
491        controls {}
492        now {
493            return [list .jpg [Rappture::encoding::decode -as b64 [$_image(plot) data -format jpeg]]]
494        }
495        default {
496            error "bad option \"$option\": should be coming, controls, now"
497        }
498    }
499}
500
501#
502# isconnected --
503#
504#       Indicates if we are currently connected to the visualization server.
505#
506itcl::body Rappture::MolvisViewer::isconnected {} {
507    return [VisViewer::IsConnected]
508}
509
510
511#
512# Connect --
513#
514#       Establishes a connection to a new visualization server.
515#
516itcl::body Rappture::MolvisViewer::Connect {} {
517    if { [isconnected] } {
518        return 1
519    }
520    set hosts [GetServerList "pymol"]
521    if { "" == $hosts } {
522        return 0
523    }
524    set result [VisViewer::Connect $hosts]
525    if { $result } {
526        set _rocker(server) 0
527        set _cacheid 0
528        _send "raw -defer {set auto_color,0}"
529        _send "raw -defer {set auto_show_lines,0}"
530    }
531    return $result
532}
533
534#
535# Disconnect --
536#
537#       Clients use this method to disconnect from the current rendering
538#       server.
539#
540itcl::body Rappture::MolvisViewer::Disconnect {} {
541    VisViewer::Disconnect
542
543    # disconnected -- no more data sitting on server
544    catch { after cancel $_rocker(afterid) }
545    catch { after cancel $_mevent(afterid) }
546    array unset _dataobjs
547    array unset _model
548    array unset _mlist
549    array unset _imagecache
550
551    set _state(server) 1
552    set _state(client) 1
553    set _outbuf ""
554}
555
556itcl::body Rappture::MolvisViewer::_send { args } {
557    debug "_send $args"
558    if { $_state(server) != $_state(client) } {
559        if { ![SendBytes "frame -defer $_state(client)"] } {
560            set _state(server) $_state(client)
561        }
562    }
563
564    if { $_rocker(server) != $_rocker(client) } {
565        if { ![SendBytes "rock -defer $_rocker(client)"] } {
566            set _rocker(server) $_rocker(client)
567        }
568    }
569    eval SendBytes $args
570}
571
572#
573# _ReceiveImage -bytes <size>
574#
575#     Invoked automatically whenever the "image" command comes in from
576#     the rendering server.  Indicates that binary image data with the
577#     specified <size> will follow.
578#
579set count 0
580itcl::body Rappture::MolvisViewer::_ReceiveImage { size cacheid frame rock } {
581    set tag "$frame,$rock"
582    global count
583    incr count
584    debug "$count: cacheid=$cacheid frame=$frame\n"
585    if { $cacheid != $_cacheid } {
586        array unset _imagecache
587        set _cacheid $cacheid
588    }
589#    debug "reading $size bytes from proxy\n"
590    set _imagecache($tag) [ReceiveBytes $size]
591#    debug "success: reading $size bytes from proxy\n"
592 
593    #debug "CACHED: $tag,$cacheid"
594    $_image(plot) configure -data $_imagecache($tag)
595    set _image(id) $tag
596}
597
598
599# ----------------------------------------------------------------------
600# USAGE: _rebuild
601#
602# Called automatically whenever something changes that affects the
603# data in the widget.  Clears any existing data and rebuilds the
604# widget to display new data.
605# ----------------------------------------------------------------------
606itcl::body Rappture::MolvisViewer::_rebuild {} {
607    if { $_inrebuild } {
608        # don't allow overlapping rebuild calls
609        return
610    }
611    debug "in rebuild"
612    #set _inrebuild 1
613    set changed 0
614
615    $itk_component(3dview) configure -cursor watch
616
617    # refresh GUI (primarily to make pending cursor changes visible)
618    #update idletasks
619    set dlist [get]
620    foreach dev $dlist {
621        set model [$dev get components.molecule.model]
622        set state [$dev get components.molecule.state]
623       
624        if {"" == $model } {
625            set model "molecule"
626            scan $dev "::libraryObj%d" suffix
627            set model $model$suffix
628        }
629
630        if {"" == $state} { set state $_state(server) }
631
632        if { ![info exists _mlist($model)] } { # new, turn on
633            set _mlist($model) 2
634        } elseif { $_mlist($model) == 1 } { # on, leave on
635            set _mlist($model) 3
636        } elseif { $_mlist($model) == 0 } { # off, turn on
637            set _mlist($model) 2
638        }
639        if { ![info exists _dataobjs($model-$state)] } {
640            set data1      ""
641            set serial    0
642
643            foreach _atom [$dev children -type atom components.molecule] {
644                set symbol [$dev get components.molecule.$_atom.symbol]
645                set xyz [$dev get components.molecule.$_atom.xyz]
646                regsub {,} $xyz {} xyz
647                scan $xyz "%f %f %f" x y z
648                set recname  "ATOM  "
649                set altLoc   ""
650                set resName  ""
651                set chainID  ""
652                set Seqno    ""
653                set occupancy  1
654                set tempFactor 0
655                set recID      ""
656                set segID      ""
657                set element    ""
658                set charge     ""
659                set atom $symbol
660                set line [format "%6s%5d %4s%1s%3s %1s%5s   %8.3f%8.3f%8.3f%6.2f%6.2f%8s\n" $recname $serial $atom $altLoc $resName $chainID $Seqno $x $y $z $occupancy $tempFactor $recID]
661                append data1 $line
662                incr serial
663            }
664            set data2 [$dev get components.molecule.pdb]
665            if {"" != $data1} {
666                _send "loadpdb -defer \"$data1\" $model $state"
667                set _dataobjs($model-$state)  1
668            }
669            if {"" != $data2} {
670                _send "loadpdb -defer \"$data2\" $model $state"
671                set _dataobjs($model-$state)  1
672            }
673        }
674        if { ![info exists _model($model-transparency)] } {
675            set _model($model-transparency) "undefined"
676        }
677        if { ![info exists _model($model-representation)] } {
678            set _model($model-representation) "undefined"
679            set _model($model-newrepresentation) $_mrepresentation
680        }
681        if { $_model($model-transparency) != $_dobj2transparency($dev) } {
682            set _model($model-newtransparency) $_dobj2transparency($dev)
683        }
684    }
685
686    # enable/disable models as required (0=off->off, 1=on->off, 2=off->on,
687    # 3=on->on)
688
689    foreach obj [array names _mlist] {
690        if { $_mlist($obj) == 1 } {
691            _send "disable -defer $obj"
692            set _mlist($obj) 0
693            set changed 1
694        } elseif { $_mlist($obj) == 2 } {
695            set _mlist($obj) 1
696            _send "enable -defer $obj"
697            if { $_labels } {
698                _send "label -defer on"
699            } else {
700                _send "label -defer off"
701            }
702            set changed 1
703        } elseif { $_mlist($obj) == 3 } {
704            set _mlist($obj) 1
705        }
706
707        if { $_mlist($obj) == 1 } {
708            if {  [info exists _model($obj-newtransparency)] ||
709                  [info exists _model($obj-newrepresentation)] } {
710                if { ![info exists _model($obj-newrepresentation)] } {
711                    set _model($obj-newrepresentation) $_model($obj-representation)
712                }
713                if { ![info exists _model($obj-newtransparency)] } {
714                    set _model($obj-newtransparency) $_model($obj-transparency)
715                }
716                set rep $_model($obj-newrepresentation)
717                set transp $_model($obj-newtransparency)
718                _send "$_model($obj-newrepresentation) -defer -model $obj -$_model($obj-newtransparency)"
719                set changed 1
720                set _model($obj-transparency) $_model($obj-newtransparency)
721                set _model($obj-representation) $_model($obj-newrepresentation)
722                catch {
723                    unset _model($obj-newtransparency)
724                    unset _model($obj-newrepresentation)
725                }
726            }
727        }
728
729    }
730
731    if { $changed } {
732        array unset _imagecache
733    }
734    if { $dlist == "" } {
735        set _state(server) 1
736        set _state(client) 1
737        _send "frame 1"
738    } elseif { ![info exists _imagecache($state,$_rocker(client))] } {
739        set _state(server) $state
740        set _state(client) $state
741        _send "frame $state"
742    } else {
743        set _state(client) $state
744        _update
745    }
746    # Reset viewing parameters
747    set w  [winfo width $itk_component(3dview)]
748    set h  [winfo height $itk_component(3dview)]
749    _send [subst {
750        reset
751        screen $w $h
752        rotate $view_(mx) $view_(my) $view_(mz)
753        pan $view_(x) $view_(y)
754        zoom $view_(zoom)
755    }]
756    debug "rebuild: rotate $view_(mx) $view_(my) $view_(mz)"
757
758    $this projection update
759    $this atomscale update
760    $this emblems update
761
762    set _inrebuild 0
763    $itk_component(3dview) configure -cursor ""
764    debug "exiting rebuild"
765}
766
767itcl::body Rappture::MolvisViewer::_unmap { } {
768    #pause rocking loop while unmapped (saves CPU time)
769    rock pause
770
771    # Blank image, mark current image dirty
772    # This will force reload from cache, or remain blank if cache is cleared
773    # This prevents old image from briefly appearing when a new result is added
774    # by result viewer
775
776    #$_image(plot) blank
777    set _image(id) ""
778}
779
780itcl::body Rappture::MolvisViewer::_map { } {
781    if { [isconnected] } {
782        # resume rocking loop if it was on
783        rock unpause
784        # rebuild image if modified, or redisplay cached image if not
785        $_dispatcher event -idle !rebuild
786    }
787}
788
789itcl::body Rappture::MolvisViewer::_configure { w h } {
790    debug "in _configure $w $h"
791    $_image(plot) configure -width $w -height $h
792    # immediately invalidate cache, defer update until mapped
793    array unset _imagecache
794    _send "screen $w $h"
795}
796
797# ----------------------------------------------------------------------
798# USAGE: $this _pan click x y
799#        $this _pan drag x y
800#        $this _pan release x y
801#
802# Called automatically when the user clicks on one of the zoom
803# controls for this widget.  Changes the zoom for the current view.
804# ----------------------------------------------------------------------
805itcl::body Rappture::MolvisViewer::_pan {option x y} {
806    if { $option == "set" } {
807        set dx $x
808        set dy $y
809        set view_(x) [expr $view_(x) + $dx]
810        set view_(y) [expr $view_(y) + $dy]
811        _send "pan $dx $dy"
812        return
813    }
814    if { ![info exists _mevent(x)] } {
815        set option "click"
816    }
817    if { $option == "click" } {
818        $itk_component(3dview) configure -cursor hand1
819    }
820    if { $option == "drag" || $option == "release" } {
821        set dx [expr $x - $_mevent(x)]
822        set dy [expr $y - $_mevent(y)]
823        set view_(x) [expr $view_(x) + $dx]
824        set view_(y) [expr $view_(y) + $dy]
825        _send "pan $dx $dy"
826    }
827    set _mevent(x) $x
828    set _mevent(y) $y
829    if { $option == "release" } {
830        $itk_component(3dview) configure -cursor ""
831    }
832}
833
834# ----------------------------------------------------------------------
835# USAGE: _zoom in
836# USAGE: _zoom out
837# USAGE: _zoom reset
838#
839# Called automatically when the user clicks on one of the zoom
840# controls for this widget.  Changes the zoom for the current view.
841# ----------------------------------------------------------------------
842itcl::body Rappture::MolvisViewer::_zoom {option {factor 10}} {
843    switch -- $option {
844        "in" {
845            set view_(zoom) [expr $view_(zoom) + $factor]
846            _send "zoom $factor"
847        }
848        "out" {
849            set view_(zoom) [expr $view_(zoom) - $factor]
850            _send "zoom -$factor"
851        }
852        "reset" {
853            set view_(zoom) 0
854            _send "reset"
855        }
856    }
857}
858
859itcl::body Rappture::MolvisViewer::_update { args } {
860    set tag "$_state(client),$_rocker(client)"
861    if { $_image(id) != "$tag" } {
862        if { [info exists _imagecache($tag)] } {
863            #puts stderr "DISPLAYING CACHED IMAGE"
864            $_image(plot) configure -data $_imagecache($tag)
865            set _image(id) "$tag"
866        }
867    }
868}
869
870# ----------------------------------------------------------------------
871# USAGE: rock on|off|toggle
872# USAGE: rock pause|unpause|step
873#
874# Used to control the "rocking" model for the molecule being displayed.
875# Clients should use only the on/off/toggle options; the rest are for
876# internal control of the rocking motion.
877# ----------------------------------------------------------------------
878itcl::body Rappture::MolvisViewer::rock { option } {
879    # cancel any pending rocks
880    if { [info exists _rocker(afterid)] } {
881        after cancel $_rocker(afterid)
882        unset _rocker(afterid)
883    }
884
885    if { $option == "toggle" } {
886        if { $_rocker(on) } {
887            set option "off"
888        } else {
889            set option "on"
890        }
891    }
892    if { $option == "on" || ($option == "toggle" && !$_rocker(on)) } {
893        set _rocker(on) 1
894        set _settings($this-rock) 1
895        $itk_component(rock) configure -relief sunken
896    } elseif { $option == "off" || ($option == "toggle" && $_rocker(on)) } {
897        set _rocker(on) 0
898        set _settings($this-rock) 0
899        $itk_component(rock) configure -relief raised
900    } elseif { $option == "step"} {
901        if { $_rocker(client) >= 10 } {
902            set _rocker(dir) -1
903        } elseif { $_rocker(client) <= -10 } {
904            set _rocker(dir) 1
905        }
906        set _rocker(client) [expr {$_rocker(client) + $_rocker(dir)}]
907        if { ![info exists _imagecache($_state(server),$_rocker(client))] } {
908            set _rocker(server) $_rocker(client)
909            _send "rock $_rocker(client)"
910        }
911        _update
912    }
913    if { $_rocker(on) && $option != "pause" } {
914         set _rocker(afterid) [after 200 [itcl::code $this rock step]]
915    }
916}
917
918
919itcl::body Rappture::MolvisViewer::_vmouse2 {option b m x y} {
920    set now [clock clicks -milliseconds]
921    set vButton [expr $b - 1]
922    set vModifier 0
923    set vState 1
924
925    if { $m & 1 }      { set vModifier [expr $vModifier | 1 ] }
926    if { $m & 4 }      { set vModifier [expr $vModifier | 2 ] }
927    if { $m & 131072 } { set vModifier [expr $vModifier | 4 ] }
928
929    if { $option == "click"   } { set vState 0 }
930    if { $option == "release" } { set vState 1 }
931    if { $option == "drag"    } { set vState 2 }
932    if { $option == "move"    } { set vState 3 }
933
934    if { $vState == 2 || $vState == 3} {
935        set diff 0
936
937        catch { set diff [expr $now - $_mevent(time)] }
938        if {$diff < 75} { # 75ms between motion updates
939            return
940        }
941    }
942    _send "vmouse $vButton $vModifier $vState $x $y"
943    set _mevent(time) $now
944}
945
946itcl::body Rappture::MolvisViewer::_vmouse {option b m x y} {
947    set now  [clock clicks -milliseconds]
948    # cancel any pending delayed dragging events
949    if { [info exists _mevent(afterid)] } {
950        after cancel $_mevent(afterid)
951        unset _mevent(afterid)
952    }
953
954    if { ![info exists _mevent(x)] } {
955        set option "click"
956    }
957    if { $option == "click" } {
958        $itk_component(3dview) configure -cursor fleur
959    }
960    if { $option == "drag" || $option == "release" } {
961        set diff 0
962         catch { set diff [expr $now - $_mevent(time) ] }
963         if {$diff < 25 && $option == "drag" } { # 75ms between motion updates
964             set _mevent(afterid) [after [expr 25 - $diff] [itcl::code $this _vmouse drag $b $m $x $y]]
965             return
966         }
967        set w [winfo width $itk_component(3dview)]
968        set h [winfo height $itk_component(3dview)]
969        if {$w <= 0 || $h <= 0} {
970            return
971        }
972        set x1 [expr double($w) / 3]
973        set x2 [expr $x1 * 2]
974        set y1 [expr double($h) / 3]
975        set y2 [expr $y1 * 2]
976        set dx [expr $x - $_mevent(x)]
977        set dy [expr $y - $_mevent(y)]
978        set mx 0
979        set my 0
980        set mz 0
981
982        if { $_mevent(x) < $x1 } {
983            set mz $dy
984        } elseif { $_mevent(x) < $x2 } {
985            set mx $dy
986        } else {
987            set mz [expr -$dy]
988        }
989
990        if { $_mevent(y) < $y1 } {
991            set mz [expr -$dx]
992        } elseif { $_mevent(y) < $y2 } {
993            set my $dx
994        } else {
995            set mz $dx
996        }
997        # Accumlate movements
998        set view_(mx) [expr {$view_(mx) + $mx}]
999        set view_(my) [expr {$view_(my) + $my}]
1000        set view_(mz) [expr {$view_(mz) + $mz}]
1001        _send "rotate $mx $my $mz"
1002        debug "_vmmouse: rotate $view_(mx) $view_(my) $view_(mz)"
1003    }
1004    set _mevent(x) $x
1005    set _mevent(y) $y
1006    set _mevent(time) $now
1007    if { $option == "release" } {
1008        $itk_component(3dview) configure -cursor ""
1009    }
1010}
1011
1012# ----------------------------------------------------------------------
1013# USAGE: _rotate click <x> <y>
1014# USAGE: _rotate drag <x> <y>
1015# USAGE: _rotate release <x> <y>
1016#
1017# Called automatically when the user clicks/drags/releases in the
1018# plot area.  Moves the plot according to the user's actions.
1019# ----------------------------------------------------------------------
1020itcl::body Rappture::MolvisViewer::_rotate {option x y} {
1021    set now  [clock clicks -milliseconds]
1022    update idletasks
1023    # cancel any pending delayed dragging events
1024    if { [info exists _mevent(afterid)] } {
1025        after cancel $_mevent(afterid)
1026        unset _mevent(afterid)
1027    }
1028    switch -- $option {
1029        click {
1030            $itk_component(3dview) configure -cursor fleur
1031            set click_(x) $x
1032            set click_(y) $y
1033            set click_(theta) $view_(theta)
1034            set click_(phi) $view_(phi)
1035        }
1036        drag {
1037            if {[array size click_] == 0} {
1038                _rotate click $x $y
1039            } else {
1040                set w [winfo width $itk_component(3dview)]
1041                set h [winfo height $itk_component(3dview)]
1042                if {$w <= 0 || $h <= 0} {
1043                    return
1044                }
1045#         set diff 0
1046#          catch { set diff [expr $now - $_mevent(time) ] }
1047#          if {$diff < 175 && $option == "drag" } { # 75ms between motion updates
1048#              set _mevent(afterid) [after [expr 175 - $diff] [itcl::code $this _rotate drag $x $y]]
1049#              return
1050#          }
1051
1052                if {[catch {
1053                    # this fails sometimes for no apparent reason
1054                    set dx [expr {double($x-$click_(x))/$w}]
1055                    set dy [expr {double($y-$click_(y))/$h}]
1056                }]} {
1057                    return
1058                }
1059
1060                #
1061                # Rotate the camera in 3D
1062                #
1063                if {$view_(psi) > 90 || $view_(psi) < -90} {
1064                    # when psi is flipped around, theta moves backwards
1065                    set dy [expr {-$dy}]
1066                }
1067                set theta [expr {$view_(theta) - $dy*180}]
1068                while {$theta < 0} { set theta [expr {$theta+180}] }
1069                while {$theta > 180} { set theta [expr {$theta-180}] }
1070
1071                if {abs($theta) >= 30 && abs($theta) <= 160} {
1072                    set phi [expr {$view_(phi) - $dx*360}]
1073                    while {$phi < 0} { set phi [expr {$phi+360}] }
1074                    while {$phi > 360} { set phi [expr {$phi-360}] }
1075                    set psi $view_(psi)
1076                } else {
1077                    set phi $view_(phi)
1078                    set psi [expr {$view_(psi) - $dx*360}]
1079                    while {$psi < -180} { set psi [expr {$psi+360}] }
1080                    while {$psi > 180} { set psi [expr {$psi-360}] }
1081                }
1082                array set view_ [subst {
1083                    theta $theta
1084                    phi $phi
1085                    psi $psi
1086                }]
1087                foreach { vx vy vz } [Euler2XYZ $theta $phi $psi] break
1088                set a [expr $vx - $view_(vx)]
1089                set a [expr -$a]
1090                set b [expr $vy - $view_(vy)]
1091                set c [expr $vz - $view_(vz)]
1092                array set view_ [subst {
1093                    vx $vx
1094                    vy $vy
1095                    vz $vz
1096                }]
1097                _send "rotate $a $b $c"
1098                debug "_rotate $x $y: rotate $view_(vx) $view_(vy) $view_(vz)"
1099                set click_(x) $x
1100                set click_(y) $y
1101            }
1102        }
1103        release {
1104            _rotate drag $x $y
1105            $itk_component(3dview) configure -cursor ""
1106            catch {unset click_}
1107        }
1108        default {
1109            error "bad option \"$option\": should be click, drag, release"
1110        }
1111    }
1112    set _mevent(time) $now
1113}
1114
1115# ----------------------------------------------------------------------
1116# USAGE: representation spheres
1117# USAGE: representation ballnstick
1118# USAGE: representation lines
1119#
1120# Used internally to change the molecular representation used to render
1121# our scene.
1122# ----------------------------------------------------------------------
1123itcl::body Rappture::MolvisViewer::representation {option {model "all"} } {
1124    if { $option == $_mrepresentation } {
1125        return
1126    }
1127    set _settings($this-modelimg) [Rappture::icon $option]
1128    #@set inner [$itk_component(controls).panel component inner]
1129    set inner $itk_component(settings_frame)
1130    $inner.pict configure -image $_settings($this-modelimg)
1131
1132    # Save the current option to set all radiobuttons -- just in case.
1133    # This method gets called without the user clicking on a radiobutton.
1134    set _settings($this-model) $option
1135    set _mrepresentation $option
1136
1137    if { $model == "all" } {
1138        set models [array names _mlist]
1139    } else {
1140        set models $model
1141    }
1142
1143    foreach obj $models {
1144        if { [info exists _model($obj-representation)] } {
1145            if { $_model($obj-representation) != $option } {
1146                set _model($obj-newrepresentation) $option
1147            } else {
1148                catch { unset _model($obj-newrepresentation) }
1149            }
1150        }
1151    }
1152    if { [isconnected] } {
1153        _send "$option -model $model"
1154        #$_dispatcher event -idle !rebuild
1155    }
1156}
1157
1158# ----------------------------------------------------------------------
1159# USAGE: emblems on|off|toggle
1160# USAGE: emblems update
1161#
1162# Used internally to turn labels associated with atoms on/off, and to
1163# update the positions of the labels so they sit on top of each atom.
1164# ----------------------------------------------------------------------
1165itcl::body Rappture::MolvisViewer::emblems {option} {
1166    switch -- $option {
1167        on {
1168            set emblem 1
1169        }
1170        off {
1171            set emblem 0
1172        }
1173        toggle {
1174            if {$_settings($this-emblems)} {
1175                set emblem 0
1176            } else {
1177                set emblem 1
1178            }
1179        }
1180        update {
1181            set emblem $_settings($this-emblems)
1182        }
1183        default {
1184            error "bad option \"$option\": should be on, off, toggle, or update"
1185        }
1186    }
1187    set _labels $emblem
1188    if {$emblem == $_settings($this-emblems) && $option != "update"} {
1189        # nothing to do
1190        return
1191    }
1192
1193    if {$emblem} {
1194        $itk_component(labels) configure -relief sunken
1195        set _settings($this-emblems) 1
1196        _send "label on"
1197    } else {
1198        $itk_component(labels) configure -relief raised
1199        set _settings($this-emblems) 0
1200        _send "label off"
1201    }
1202}
1203
1204# ----------------------------------------------------------------------
1205# USAGE: projection on|off|toggle
1206# USAGE: projection update
1207#
1208# Used internally to turn labels associated with atoms on/off, and to
1209# update the positions of the labels so they sit on top of each atom.
1210# ----------------------------------------------------------------------
1211itcl::body Rappture::MolvisViewer::projection {option} {
1212    switch -- $option {
1213        "orthoscopic" {
1214            set ortho 1
1215        }
1216        "perspective" {
1217            set ortho 0
1218        }
1219        "toggle" {
1220            set ortho [expr {$_settings($this-ortho) == 0}]
1221        }
1222        "update" {
1223            set ortho $_settings($this-ortho)
1224        }
1225        default {
1226            error "bad option \"$option\": should be on, off, toggle, or update"
1227        }
1228    }
1229    if { $ortho == $_settings($this-ortho) && $option != "update"} {
1230        # nothing to do
1231        return
1232    }
1233    if { $ortho } {
1234        $itk_component(ortho) configure -image [Rappture::icon 3dorth]
1235        Rappture::Tooltip::for $itk_component(ortho) \
1236            "Change to perspective projection"
1237        set _settings($this-ortho) 1
1238        _send "orthoscopic on"
1239    } else {
1240        $itk_component(ortho) configure -image [Rappture::icon 3dpers]
1241        Rappture::Tooltip::for $itk_component(ortho) \
1242            "Change to orthoscopic projection"
1243        set _settings($this-ortho) 0
1244        _send "orthoscopic off"
1245    }
1246}
1247
1248# ----------------------------------------------------------------------
1249# USAGE: atomscale scale ?model?
1250#        atomscale update
1251#
1252# Used internally to change the molecular representation used to render
1253# our scene.
1254# ----------------------------------------------------------------------
1255
1256itcl::body Rappture::MolvisViewer::atomscale { option {model "all"} } {
1257    if { $option == "update" } {
1258        set scale $_settings($this-atomscale)
1259    } elseif { [string is double $option] } {
1260        set scale $option
1261        if { ($scale < 0.1) || ($scale > 2.0) } {
1262            error "bad atom size \"$scale\""
1263        }
1264    } else {
1265        error "bad option \"$option\""
1266    }
1267    set _settings($this-atomscale) $scale
1268    if { [isconnected] } {
1269        _send "atomscale -model $model $scale"
1270    }
1271}
1272
1273# ----------------------------------------------------------------------
1274# USAGE: add <dataobj> ?<settings>?
1275#
1276# Clients use this to add a data object to the plot.  The optional
1277# <settings> are used to configure the plot.  Allowed settings are
1278# -color, -brightness, -width, -linestyle, and -raise. Only
1279# -brightness and -raise do anything.
1280# ----------------------------------------------------------------------
1281itcl::body Rappture::MolvisViewer::add { dataobj {options ""}} {
1282    array set params {
1283        -color          auto
1284        -brightness     0
1285        -width          1
1286        -raise          0
1287        -linestyle      solid
1288        -description    ""
1289        -param          ""
1290    }
1291
1292    foreach {opt val} $options {
1293        if {![info exists params($opt)]} {
1294            error "bad settings \"$opt\": should be [join [lsort [array names params]] {, }]"
1295        }
1296        set params($opt) $val
1297    }
1298 
1299    set pos [lsearch -exact $dataobj $_dlist]
1300
1301    if {$pos < 0} {
1302        if {![Rappture::library isvalid $dataobj]} {
1303            error "bad value \"$dataobj\": should be Rappture::library object"
1304        }
1305   
1306        if { $_labels == "default" } {
1307            set emblem [$dataobj get components.molecule.about.emblems]
1308
1309            if {$emblem == "" || ![string is boolean $emblem] || !$emblem} {
1310                emblems off
1311            } else {
1312                emblems on
1313            }
1314        }
1315
1316        lappend _dlist $dataobj
1317        if { $params(-brightness) >= 0.5 } {
1318            set _dobj2transparency($dataobj) "ghost"
1319        } else {
1320            set _dobj2transparency($dataobj) "normal"
1321        }
1322        set _dobj2raise($dataobj) $params(-raise)
1323
1324        if { [isconnected] } {
1325            $_dispatcher event -idle !rebuild
1326        }
1327    }
1328}
1329
1330#
1331# ResetView
1332#
1333itcl::body Rappture::MolvisViewer::ResetView {} {
1334    array set view_ {
1335        theta   45
1336        phi     45
1337        psi     0
1338        mx 0
1339        my 0
1340        mz 0
1341        x 0
1342        y 0
1343        z 0
1344        zoom 0
1345        width 0
1346        height 0
1347    }
1348    _send "reset"
1349    _send "rotate $view_(mx) $view_(my) $view_(mz)"
1350    debug "ResetView: rotate $view_(mx) $view_(my) $view_(mz)"
1351    _send "pan $view_(x) $view_(y)"
1352    _send "zoom $view_(zoom)"
1353}
1354
1355# ----------------------------------------------------------------------
1356# USAGE: get
1357#
1358# Clients use this to query the list of objects being plotted, in
1359# order from bottom to top of this result.
1360# ----------------------------------------------------------------------
1361itcl::body Rappture::MolvisViewer::get {} {
1362    # put the dataobj list in order according to -raise options
1363    set dlist $_dlist
1364    foreach obj $dlist {
1365        if {[info exists _dobj2raise($obj)] && $_dobj2raise($obj)} {
1366            set i [lsearch -exact $dlist $obj]
1367            if {$i >= 0} {
1368                set dlist [lreplace $dlist $i $i]
1369                lappend dlist $obj
1370            }
1371        }
1372    }
1373    return $dlist
1374}
1375
1376# ----------------------------------------------------------------------
1377# USAGE: delete ?<dataobj> <dataobj> ...?
1378#
1379# Clients use this to delete a dataobj from the plot. If no dataobjs
1380# are specified, then all dataobjs are deleted.
1381# ----------------------------------------------------------------------
1382itcl::body Rappture::MolvisViewer::delete {args} {
1383    if {[llength $args] == 0} {
1384        set args $_dlist
1385    }
1386
1387    # delete all specified dataobjs
1388    set changed 0
1389    foreach dataobj $args {
1390        set pos [lsearch -exact $_dlist $dataobj]
1391        if {$pos >= 0} {
1392            set _dlist [lreplace $_dlist $pos $pos]
1393            catch {unset _dobj2transparency($dataobj)}
1394            catch {unset _dobj2color($dataobj)}
1395            catch {unset _dobj2width($dataobj)}
1396            catch {unset _dobj2dashes($dataobj)}
1397            catch {unset _dobj2raise($dataobj)}
1398            set changed 1
1399        }
1400    }
1401
1402    # if anything changed, then rebuild the plot
1403    if {$changed} {
1404        if { [isconnected] } {
1405            $_dispatcher event -idle !rebuild
1406        }
1407    }
1408}
1409
1410# ----------------------------------------------------------------------
1411# OPTION: -device
1412# ----------------------------------------------------------------------
1413itcl::configbody Rappture::MolvisViewer::device {
1414    if {$itk_option(-device) != "" } {
1415
1416        if {![Rappture::library isvalid $itk_option(-device)]} {
1417            error "bad value \"$itk_option(-device)\": should be Rappture::library object"
1418        }
1419        $this delete
1420        $this add $itk_option(-device)
1421    } else {
1422        $this delete
1423    }
1424
1425    if { [isconnected] } {
1426        $_dispatcher event -idle !rebuild
1427    }
1428}
1429
1430
1431itcl::body Rappture::MolvisViewer::settings { what args } {
1432    switch -- ${what} {
1433        "activate" {
1434            $itk_component(drawer) add $itk_component(settings) -sticky nsew
1435            after idle [list focus $itk_component(settings)]
1436            if { !$initialized_ } {
1437                set w [winfo width $itk_component(drawer)]
1438                set x [expr $w - 100]
1439                $itk_component(drawer) sash place 0 $x 0
1440                set initialized_ 1
1441            }
1442            $itk_component(settings_button) configure -relief sunken
1443        }
1444        "deactivate" {
1445            $itk_component(drawer) forget $itk_component(settings)
1446            $itk_component(settings_button) configure -relief raised
1447        }
1448        "toggle" {
1449            set slaves [$itk_component(drawer) panes]
1450            if { [lsearch $slaves $itk_component(settings)] >= 0 } {
1451                settings deactivate
1452            } else {
1453                settings activate
1454            }
1455        }
1456        "resize" {
1457            set bbox [$itk_component(settings_canvas) bbox all]
1458            set wid [winfo width $itk_component(settings_frame)]
1459            $itk_component(settings_canvas) configure -width $wid \
1460                -scrollregion $bbox -yscrollincrement 0.1i
1461        }
1462    }
1463}
1464
1465
1466
Note: See TracBrowser for help on using the repository browser.