Last uiList patch (for now!): filtering and reordering of shown elements.
[blender.git] / release / scripts / startup / bl_ui / __init__.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8 compliant>
20
21 # note, properties_animviz is a helper module only.
22
23 if "bpy" in locals():
24     from imp import reload as _reload
25     for val in _modules_loaded.values():
26         _reload(val)
27 _modules = [
28     "properties_animviz",
29     "properties_constraint",
30     "properties_data_armature",
31     "properties_data_bone",
32     "properties_data_camera",
33     "properties_data_curve",
34     "properties_data_empty",
35     "properties_data_lamp",
36     "properties_data_lattice",
37     "properties_data_mesh",
38     "properties_data_metaball",
39     "properties_data_modifier",
40     "properties_data_speaker",
41     "properties_game",
42     "properties_mask_common",
43     "properties_material",
44     "properties_object",
45     "properties_particle",
46     "properties_physics_cloth",
47     "properties_physics_common",
48     "properties_physics_dynamicpaint",
49     "properties_physics_field",
50     "properties_physics_fluid",
51     "properties_physics_rigidbody",
52     "properties_physics_rigidbody_constraint",
53     "properties_physics_smoke",
54     "properties_physics_softbody",
55     "properties_render",
56     "properties_render_layer",
57     "properties_scene",
58     "properties_texture",
59     "properties_world",
60     "space_clip",
61     "space_console",
62     "space_dopesheet",
63     "space_filebrowser",
64     "space_graph",
65     "space_image",
66     "space_info",
67     "space_logic",
68     "space_nla",
69     "space_node",
70     "space_outliner",
71     "space_properties",
72     "space_sequencer",
73     "space_text",
74     "space_time",
75     "space_userpref",
76     "space_view3d",
77     "space_view3d_toolbar",
78 ]
79
80 import bpy
81
82 if bpy.app.build_options.freestyle:
83     _modules.append("properties_freestyle")
84 __import__(name=__name__, fromlist=_modules)
85 _namespace = globals()
86 _modules_loaded = {name: _namespace[name] for name in _modules if name != 'bpy'}
87 del _namespace
88
89
90 def register():
91     bpy.utils.register_module(__name__)
92
93     # space_userprefs.py
94     from bpy.props import StringProperty, EnumProperty
95     from bpy.types import WindowManager
96
97     def addon_filter_items(self, context):
98         import addon_utils
99
100         items = [('All', "All", "All Addons"),
101                  ('User', "User", "All Addons Installed by User"),
102                  ('Enabled', "Enabled", "All Enabled Addons"),
103                  ('Disabled', "Disabled", "All Disabled Addons"),
104                  ]
105
106         items_unique = set()
107
108         for mod in addon_utils.modules(refresh=False):
109             info = addon_utils.module_bl_info(mod)
110             items_unique.add(info["category"])
111
112         items.extend([(cat, cat, "") for cat in sorted(items_unique)])
113         return items
114
115     WindowManager.addon_search = StringProperty(
116             name="Search",
117             description="Search within the selected filter",
118             )
119     WindowManager.addon_filter = EnumProperty(
120             items=addon_filter_items,
121             name="Category",
122             description="Filter addons by category",
123             )
124
125     WindowManager.addon_support = EnumProperty(
126             items=[('OFFICIAL', "Official", "Officially supported"),
127                    ('COMMUNITY', "Community", "Maintained by community developers"),
128                    ('TESTING', "Testing", "Newly contributed scripts (excluded from release builds)")
129                    ],
130             name="Support",
131             description="Display support level",
132             default={'OFFICIAL', 'COMMUNITY'},
133             options={'ENUM_FLAG'},
134             )
135     # done...
136
137
138 def unregister():
139     bpy.utils.unregister_module(__name__)
140
141
142 # Define a default UIList, when a list does not need any custom drawing...
143 # Keep in sync with its #defined name in UI_interface.h
144 class UI_UL_list(bpy.types.UIList):
145     # These are common filtering or ordering operations (same as the default C ones!).
146     @staticmethod
147     def filter_items_by_name(pattern, bitflag, items, propname="name", flags=None, reverse=False):
148         """
149         Set FILTER_ITEM for items which name matches filter_name one (case-insensitive).
150         pattern is the filtering pattern.
151         propname is the name of the string property to use for filtering.
152         flags must be a list of integers the same length as items, or None!
153         return a list of flags (based on given flags if not None),
154                or an empty list if no flags were given and no filtering has been done.
155         """
156         import fnmatch
157
158         if not pattern:  # Empty pattern = no filtering!
159             return flags or []
160
161         if flags is None:
162             flags = [0] * len(items)
163         for idx, it in enumerate(items):
164             name = getattr(it, propname, None)
165             # Implicitly add heading/trailing wildcards if needed.
166             if pattern[0] != "*":
167                 pattern = "*" + pattern
168             if pattern[-1] != "*":
169                 pattern = pattern + "*"
170             # This is similar to a logical xor
171             if bool(name and fnmatch.fnmatch(name.lower(), pattern.lower())) is not bool(reverse):
172                 flags[idx] |= bitflag
173         return flags
174
175     @staticmethod
176     def sort_items_helper(sort_data, key, reverse=False):
177         """
178         Common sorting utility. Returns a neworder list mapping org_idx -> new_idx.
179         sort_data must be an (unordered) list of tuples [(org_idx, ...), (org_idx, ...), ...].
180         key must be the same kind of callable you would use for sorted() builtin function.
181         reverse will reverse the sorting!
182         """
183         sort_data.sort(key=key, reverse=reverse)
184         neworder = [None] * len(sort_data)
185         for newidx, (orgidx, *_) in enumerate(sort_data):
186             neworder[orgidx] = newidx
187         return neworder
188
189     @classmethod
190     def sort_items_by_name(cls, items, propname="name"):
191         """
192         Re-order items using their names (case-insensitive).
193         propname is the name of the string property to use for sorting.
194         return a list mapping org_idx -> new_idx,
195                or an empty list if no sorting has been done.
196         """
197         neworder = [None] * len(items)
198         _sort = [(idx, getattr(it, propname, "")) for idx, it in enumerate(items)]
199         return cls.sort_items_helper(_sort, lambda e: e[1].lower())
200
201
202 bpy.utils.register_class(UI_UL_list)