1# Copyright 2004-2021 Tom Rothamel <pytom@bishoujo.us>
2#
3# Permission is hereby granted, free of charge, to any person
4# obtaining a copy of this software and associated documentation files
5# (the "Software"), to deal in the Software without restriction,
6# including without limitation the rights to use, copy, modify, merge,
7# publish, distribute, sublicense, and/or sell copies of the Software,
8# and to permit persons to whom the Software is furnished to do so,
9# subject to the following conditions:
10#
11# The above copyright notice and this permission notice shall be
12# included in all copies or substantial portions of the Software.
13#
14# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22init python in navigation:
23    import store.interface as interface
24    import store.project as project
25    import store.editor as editor
26    from store import persistent, Action
27
28
29    # The last navigation screen we've seen. This is the scree we try to go
30    # to the next time we enter navigation. (We may not be able to go there,
31    # if the screen is empty.)
32    if persistent.navigation is None:
33        persistent.navigation = "label"
34
35    # A map from a kind of information, to how we should sort it. Possible
36    # sorts are alphabetical, by-file, natural.
37    if persistent.navigation_sort is None:
38        persistent.navigation_sort = { }
39
40    if persistent.navigate_private is None:
41        persistent.navigate_private = False
42
43    if persistent.navigate_library is None:
44        persistent.navigate_library = False
45
46    # A list of kinds of navigation we support.
47    KINDS = [ "file", "label", "define", "transform", "screen", "callable", "todo" ]
48
49    # A map from kind name to adjustment.
50    adjustments = { }
51
52    for i in KINDS:
53        persistent.navigation_sort.setdefault(i, "by-file")
54        adjustments[i] = ui.adjustment()
55
56    def group_and_sort(kind):
57        """
58        This is responsible for pulling navigation information of the
59        appropriate kind out of project.current.dump, grouping it,
60        and sorting it.
61
62        This returns a list of (group, list of (name, filename, line)). The
63        group may be a string or None.
64        """
65
66        project.current.update_dump()
67
68        sort = persistent.navigation_sort[kind]
69
70        name_map = project.current.dump.get("location", {}).get(kind, { })
71
72        groups = { }
73
74        for name, loc in name_map.items():
75            filename, line = loc
76            filename = filename.replace("\\", "/")
77
78            if sort == "alphabetical":
79                group = None
80            else:
81                group = filename
82                if group.startswith("game/"):
83                    group = group[5:]
84
85            g = groups.get(group, None)
86            if g is None:
87                groups[group] = g = [ ]
88
89            g.append((name, filename, line))
90
91        for g in groups.values():
92            if sort == "natural":
93                g.sort(key=lambda a : a[2])
94            else:
95                g.sort(key=lambda a : a[0].lower())
96
97        rv = list(groups.items())
98        rv.sort()
99
100        return rv
101
102    def group_and_sort_files():
103
104        rv = [ ]
105
106        for fn in project.current.script_files():
107            shortfn = fn
108            shortfn = shortfn.replace("\\", "/")
109
110            if shortfn.startswith("game/"):
111                shortfn = fn[5:]
112
113            rv.append((shortfn, fn, None))
114
115        rv.sort()
116
117        return [ (None, rv) ]
118
119    class ChangeKind(Action):
120        """
121        Changes the kind of thing we're navigating over.
122        """
123
124        def __init__(self, kind):
125            self.kind = kind
126
127        def get_selected(self):
128            return persistent.navigation == self.kind
129
130        def __call__(self):
131            if persistent.navigation == self.kind:
132                return
133
134            persistent.navigation = self.kind
135            renpy.jump("navigation_loop")
136
137    class ChangeSort(Action):
138        """
139        Changes the sort order.
140        """
141
142        def __init__(self, sort):
143            self.sort = sort
144
145        def get_selected(self):
146            return persistent.navigation_sort[persistent.navigation] == self.sort
147
148        def __call__(self):
149            if self.get_selected():
150                return
151
152            persistent.navigation_sort[persistent.navigation] = self.sort
153            renpy.jump("navigation_loop")
154
155
156screen navigation:
157
158    frame:
159        style_group "l"
160        style "l_root"
161
162        window:
163
164            has vbox
165
166            frame style "l_label":
167                has hbox xfill True
168                text _("Navigate: [project.current.display_name!q]") style "l_label_text"
169                alt _("Navigate Script")
170
171                frame:
172                    style "l_alternate"
173                    style_group "l_small"
174
175                    has hbox
176
177                    if persistent.navigation != "file":
178                        text _("Order: ")
179                        textbutton _("alphabetical") action navigation.ChangeSort("alphabetical")
180                        text " | "
181                        textbutton _("by-file") action navigation.ChangeSort("by-file")
182                        text " | "
183                        textbutton _("natural") action navigation.ChangeSort("natural")
184
185                        null width HALF_INDENT
186
187                    textbutton _("refresh") action Jump("navigation_refresh")
188
189
190            add HALF_SPACER
191
192            frame style "l_indent":
193                hbox:
194                    spacing HALF_INDENT
195                    text _("Category:")
196                    alt ""
197
198                    textbutton _("files") action navigation.ChangeKind("file")
199                    textbutton _("labels") action navigation.ChangeKind("label")
200                    textbutton _("defines") action navigation.ChangeKind("define")
201                    textbutton _("transforms") action navigation.ChangeKind("transform")
202                    textbutton _("screens") action navigation.ChangeKind("screen")
203                    textbutton _("callables") action navigation.ChangeKind("callable")
204                    textbutton _("TODOs") action navigation.ChangeKind("todo")
205
206            add SPACER
207            add SEPARATOR
208
209            frame style "l_indent_margin":
210
211                if groups:
212
213                    viewport:
214                        mousewheel True
215                        scrollbars "vertical"
216                        yadjustment navigation.adjustments[persistent.navigation]
217
218                        vbox:
219                            style_group "l_navigation"
220
221                            for group_name, group in groups:
222
223                                if group_name is not None:
224                                    text "[group_name!q]"
225
226                                if persistent.navigation == "todo":
227                                    vbox:
228                                        for name, filename, line in group:
229                                            textbutton "[name!q]" action editor.Edit(filename, line)
230
231                                else:
232                                    hbox:
233                                        box_wrap True
234
235                                        for name, filename, line in group:
236                                            textbutton "[name!q]" action editor.Edit(filename, line)
237
238                                if group_name is not None:
239                                    add SPACER
240
241                            if persistent.navigation == "file":
242                                add SPACER
243                                textbutton _("+ Add script file") action Jump("add_file") style "l_button"
244
245                else:
246
247                    fixed:
248
249                        if persistent.navigation == "todo":
250
251                            text _("No TODO comments found.\n\nTo create one, include \"# TODO\" in your script."):
252                                text_align 0.5
253                                xalign 0.5
254                                yalign 0.5
255
256                        else:
257
258                            text _("The list of names is empty."):
259                                xalign 0.5
260                                yalign 0.5
261
262    textbutton _("Return") action Jump("front_page") style "l_left_button"
263    textbutton _("Launch Project") action project.Launch() style "l_right_button"
264
265label navigation:
266label navigation_loop:
267
268    python in navigation:
269
270        kind = persistent.navigation
271
272        if kind == "file":
273            groups = group_and_sort_files()
274        else:
275            groups = group_and_sort(kind)
276
277        renpy.call_screen("navigation", groups=groups)
278
279label navigation_refresh:
280    $ project.current.update_dump(True)
281    jump navigation_loop
282
283