summaryrefslogtreecommitdiff
path: root/cad/src/graphics/display_styles/displaymodes.py
blob: e178437fb4780fed69343eb84740fd3dab88ada5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
# Copyright 2006-2009 Nanorex, Inc.  See LICENSE file for details.
"""
displaymodes.py -- support for new modular display modes.

@author: Bruce
@version: $Id$
@copyright: 2006-2009 Nanorex, Inc.  See LICENSE file for details.

Initially this only handles ChunkDisplayModes, which draw entire chunks (even if they are
highlighted or selected) without drawing their atoms or bonds in the usual way. (Such a
display mode might draw the atoms and bonds in its own custom way, or not at all.)

Someday it may also handle AtomDisplayModes, which (like our original hardcoded display modes)
draw atoms and bonds in their own way, but draw chunks as not much more than their set
of atoms and bonds. These are not yet implemented.

To make a new chunk display mode, make a new file which defines and registers a subclass
of our class ChunkDisplayMode. For an example, see CylinderChunks.py.
"""

# USAGE: to make a new display style for whole chunks,
# see the instructions in the module docstring above.

from utilities.constants import _f_add_display_style_code

from utilities.debug import register_debug_menu_command
import foundation.env as env

# global variables and accessors

_display_mode_handlers = {}
    # maps disp_name, and also its index in constants.dispNames,
    # to a DisplayMode instance used for drawing

def get_display_mode_handler(disp):
    return _display_mode_handlers.get(disp)

# ==

class DisplayMode:
    """
    abstract class for any sort of display mode
    (except the 6 original built-in ones defined in constants.py)
    """

    # Note: some subclasses assign a class constant featurename,
    # but as far as I know, this is not yet used. TODO: add a
    # get_featurename method like in class Command (or basicCommand)
    # and add code to use it (not sure where in the UI).
    # [bruce 071227 comment]
    featurename = ""

    chunk_only = False # default value of class constant; some subclasses override this (in fact, not doing so is not yet supported)

    def __init__(self, ind):
        self.ind = ind
        self._icon_name = getattr(self, "icon_name", "modeltree/junk.png")
        self._hide_icon_name = getattr(self, "hide_icon_name", self._icon_name)

    def get_icon(self, hidden):
        from utilities.icon_utilities import imagename_to_pixmap
        if hidden:
            return imagename_to_pixmap( self._hide_icon_name)
        else:
            return imagename_to_pixmap( self._icon_name)
        pass

    def _register_for_readmmp(clas): # staticmethod; name is misleading, but required by other code in this file
        """
        private method called when setting up mmp reading code:
        register this class as a readmmp-helper,
        in whatever way differs for different classes of helpers

        [update, bruce 071017: this docstring is misleading,
        since so far this has nothing to do with mmp reading;
        see code comments for details]
        """
        disp_name = clas.mmp_code # we treat this as a unique index; error if not unique, unless classname is the same
        disp_label = clas.disp_label

        #bruce 080324 refactored this:
        allowed_for_atoms = not clas.chunk_only
        ind = _f_add_display_style_code( disp_name, disp_label, allowed_for_atoms )
        inst = clas(ind)
        _display_mode_handlers[disp_name] = inst
        _display_mode_handlers[ind] = inst #k are both of these needed??
        return
    _register_for_readmmp = staticmethod( _register_for_readmmp)

    #e some of ChunkDisplayMode's code probably belongs in this class

    pass # end of class DisplayMode

class ChunkDisplayMode(DisplayMode):
    """
    abstract class for display modes which only work for entire chunks
    """
    chunk_only = True

    def register_display_mode_class(clas): # staticmethod
        """
        Register the given subclass of ChunkDisplayMode as a new display mode for whole chunks,
        able to be drawn with, read/written in mmp files, and offered in the UI.

        @warning: The order in which this is called for different display styles
                  must correspond with the order in which disp index constants (diWhatever)
                  are defined for them in constants.py. (This needs cleanup.)
                  [bruce 080212 comment; related code has comments with same signature]
        """
        # Make sure we can read mmp files which refer to it as clas.mmp_code.
        # This also covers statusbar display and writemmp.
        # This works by calling _register_for_readmmp, which also stores clas.disp_label as needed
        # to show this display mode in the statusbar, when it's in use for the entire GLPane.
        # (As of 060608 there is no good reason to go through files_mmp to do this,
        #  but in the future it might need to keep its own record of this use of mmp_code.)
        # (It's probably a kluge that the registering of clas.disp_label goes through files_mmp in any way.
        #  The fault for that lies mainly with the requirement for constants.dispLabel and constants.dispNames
        #  to be lists with corresponding indices. If they were revamped, this could be cleaned up.
        #  But doing so is not trivial, assuming their ordering by bond cylinder radius is required.)

        # bruce 071017 -- remove register_for_readmmp and replace it with what it did,
        # since it's more confusing than worthwhile
        # (but see above comment for the motivation of the old way, which had some legitimacy):
        ## import files_mmp
        ## files_mmp.register_for_readmmp( clas)
        smethod = clas._register_for_readmmp
        smethod(clas)

        # The above also made it possible to draw chunks in this display mode, via _display_mode_handlers
        # and special cases in Chunk draw-related methods.
        ###e highlighting not yet done
        # Now add something to the UI so users can change chunks, or the GLPane, to this display mode.
        # (As a quick hack, just add a debug menu command (always visible) which acts like pressing a toolbutton for this mode.)
        disp_name = clas.mmp_code
        inst = _display_mode_handlers[disp_name]
        register_debug_menu_command("setDisplayStyle_of_selection(%s)" % clas.disp_label, inst.setDisplay_command)
        ##e should we do something so users can use it as a prefs value for preferred display mode, too?
        return
    register_display_mode_class = staticmethod( register_display_mode_class)

    def setDisplay_command(self, widget):
        win = env.mainwindow()
        win.setDisplayStyle_of_selection(self.ind)
            #bruce 080910 comment, upon renaming that method from setDisplay:
            # the new name says what it does, but when originally coded
            # this would set global style if nothing was selected
            # (since that method used to act that way).
        if env.debug():
            print "setDisplayStyle_of_selection to %r.ind == %r" % (self, self.ind)
        return

    def _f_drawchunk(self, glpane, chunk, highlighted = False):
        """
        [private method for use only by Chunk.draw]
        Call the subclass's drawchunk method, with the supplied arguments
        (but supplying memo ourselves), in the proper way (whatever is useful
        for Chunk.draw).

        In the calling code as it was when this method was written,
        the chunk has already done its pushMatrix and gotten us inside its
        OpenGL display list compiling/executing call, but it hasn't done a
        pushName. [later: chunks now do a pushName, probably (guess) before
        this call.]

        @note: possible future revisions: it might do pushName, or it might not
               put us in the display list unless we say that's ok.
        """
        memo = self.getmemo(chunk)
        #e exceptions?
        #e pushname
        self.drawchunk( glpane, chunk, memo, highlighted = highlighted)
        return

    def _f_drawchunk_selection_frame(self, glpane, chunk, selection_frame_color, highlighted = False):
        """
        Call the subclass's drawchunk_selection_frame method,
        with the supplied arguments (but supplying memo ourselves),
        in the proper way.
        """
        # note: as of long before now, this is never called. But it should
        # remain in the API in case we want to revive its use.
        # [bruce 081001 comment]
        memo = self.getmemo(chunk)
        #e exceptions
        #e pushname
        #e highlight color for selection frame itself??
        self.drawchunk_selection_frame( glpane, chunk, selection_frame_color, memo, highlighted = highlighted)
        return

    def _f_drawchunk_realtime(self, glpane, chunk, highlighted = False):
        """
        Call the drawing method that may depend on current view settings,
        e.g. orientation.
        """
        # piotr 080313
        # added the highlighted = False argument - piotr 080521
        # assume we don't need memo here
        ### memo = self.getmemo(chunk)
        self.drawchunk_realtime(glpane, chunk, highlighted)
        return

    def _writepov(self, chunk, file):
        """
        piotr 080314
        Render the chunk to a POV-Ray file
        """
        memo = self.getmemo(chunk)
        self.writepov(chunk, memo, file)

    def getmemo(self, chunk): # refactored, bruce 090213
        """
        [needs doc]
        """
        # Note: This is not yet optimized to let the memo be remade less often
        # than the display list is, but that can still mean we remake it a lot
        # less often than we're called for drawing the selection frame,
        # since that is drawn outside the display list and the chunk might get
        # drawn selected many times without having to remake the display list.

        our_key_in_chunk = id(self)
            # safer than using mmp_code, in case a developer reloads the class
            # at runtime and the memo algorithm changed
        counter = chunk.changeapp_counter()
        memo_validity_data = (counter,)
            # a tuple of everything which has to remain the same, for the memo
            # data to remain valid; if invalid, the following calls compute_memo
        memo = chunk.find_or_recompute_memo(
                         our_key_in_chunk,
                         memo_validity_data,
                         self.compute_memo )
        return memo

    pass # end of class ChunkDisplayMode

# end