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