9388128a4e2f271fdce72991586a4695d2bc80d5
[blender-addons-contrib.git] / space_view3d_stored_views / ui.py
1 # gpl authors: nfloyd, Francesco Siddi
2
3 import logging
4 module_logger = logging.getLogger(__name__)
5
6 import bpy
7 import blf
8 from . import core
9 from bpy.types import (
10     Operator,
11     Panel,
12 )
13
14 """
15   If view name display is enabled,
16   it will check periodically if the view has been modified
17   since last set.
18   get_preferences_timer() is the time in seconds between these checks.
19   It can be increased, if the view become sluggish
20   It is set in the add-on preferences
21 """
22
23
24 # Utility function get_preferences_timer for update of 3d view draw
25 def get_preferences_timer():
26     # replace the key if the add-on name changes
27     # TODO: expose refresh rate to ui???
28     addon = bpy.context.preferences.addons[__package__]
29     timer_update = (addon.preferences.view_3d_update_rate if addon else False)
30
31     return timer_update
32
33
34 def init_draw(context=None):
35     if context is None:
36         context = bpy.context
37
38     if "stored_views_osd" not in context.window_manager:
39         context.window_manager["stored_views_osd"] = False
40
41     if not context.window_manager["stored_views_osd"]:
42         context.window_manager["stored_views_osd"] = True
43         bpy.ops.stored_views.draw()
44
45
46 def _draw_callback_px(self, context):
47     if context.area and context.area.type == 'VIEW_3D':
48         r_width = text_location = context.region.width
49         r_height = context.region.height
50         font_id = 0  # TODO: need to find out how best to get font_id
51
52         blf.size(font_id, 11, context.preferences.system.dpi)
53         text_size = blf.dimensions(0, self.view_name)
54
55         # compute the text location
56         text_location = 0
57         overlap = context.preferences.system.use_region_overlap
58         if overlap:
59             for region in context.area.regions:
60                 if region.type == "UI":
61                     text_location = r_width - region.width
62
63         text_x = text_location - text_size[0] - 10
64         text_y = r_height - text_size[1] - 8
65         blf.position(font_id, text_x, text_y, 0)
66         blf.draw(font_id, self.view_name)
67
68
69 class VIEW3D_stored_views_draw(Operator):
70     bl_idname = "stored_views.draw"
71     bl_label = "Show current"
72     bl_description = "Toggle the display current view name in the view 3D"
73
74     _handle = None
75     _timer = None
76
77     @staticmethod
78     def handle_add(self, context):
79         VIEW3D_stored_views_draw._handle = bpy.types.SpaceView3D.draw_handler_add(
80             _draw_callback_px, (self, context), 'WINDOW', 'POST_PIXEL')
81         VIEW3D_stored_views_draw._timer = \
82             context.window_manager.event_timer_add(get_preferences_timer(), context.window)
83
84     @staticmethod
85     def handle_remove(context):
86         if VIEW3D_stored_views_draw._handle is not None:
87             bpy.types.SpaceView3D.draw_handler_remove(VIEW3D_stored_views_draw._handle, 'WINDOW')
88         if VIEW3D_stored_views_draw._timer is not None:
89             context.window_manager.event_timer_remove(VIEW3D_stored_views_draw._timer)
90         VIEW3D_stored_views_draw._handle = None
91         VIEW3D_stored_views_draw._timer = None
92
93     @classmethod
94     def poll(cls, context):
95         # return context.mode == 'OBJECT'
96         return True
97
98     def modal(self, context, event):
99         if context.area:
100             context.area.tag_redraw()
101
102         if not context.area or context.area.type != "VIEW_3D":
103             return {"PASS_THROUGH"}
104
105         data = core.DataStore()
106         stored_views = context.scene.stored_views
107
108         if len(data.list) > 0 and \
109            data.current_index >= 0 and \
110            not stored_views.view_modified:
111
112             if not stored_views.view_modified:
113                 sv = data.list[data.current_index]
114                 self.view_name = sv.name
115                 if event.type == 'TIMER':
116                     is_modified = False
117                     if data.mode == 'VIEW':
118                         is_modified = core.View.is_modified(context, sv)
119                     elif data.mode == 'POV':
120                         is_modified = core.POV.is_modified(context, sv)
121                     elif data.mode == 'LAYERS':
122                         is_modified = core.Layers.is_modified(context, sv)
123                     elif data.mode == 'DISPLAY':
124                         is_modified = core.Display.is_modified(context, sv)
125                     if is_modified:
126                         module_logger.debug(
127                                 'view modified - index: %s name: %s' % (data.current_index, sv.name)
128                                 )
129                         self.view_name = ""
130                         stored_views.view_modified = is_modified
131
132                 return {"PASS_THROUGH"}
133         else:
134             module_logger.debug('exit')
135             context.window_manager["stored_views_osd"] = False
136             VIEW3D_stored_views_draw.handle_remove(context)
137
138             return {'FINISHED'}
139
140     def execute(self, context):
141         if context.area.type == "VIEW_3D":
142             self.view_name = ""
143             VIEW3D_stored_views_draw.handle_add(self, context)
144             context.window_manager.modal_handler_add(self)
145
146             return {"RUNNING_MODAL"}
147         else:
148             self.report({"WARNING"}, "View3D not found. Operation Cancelled")
149
150             return {"CANCELLED"}
151
152
153 class VIEW3D_PT_properties_stored_views(Panel):
154     bl_label = "Stored Views"
155     bl_space_type = "VIEW_3D"
156     bl_region_type = "UI"
157
158     def draw(self, context):
159         self.logger = logging.getLogger('%s Properties panel' % __name__)
160         layout = self.layout
161
162         if bpy.ops.view3d.stored_views_initialize.poll():
163             layout.operator("view3d.stored_views_initialize")
164             return
165
166         stored_views = context.scene.stored_views
167
168         # UI : mode
169         col = layout.column(align=True)
170         col.prop_enum(stored_views, "mode", 'VIEW')
171         row = layout.row(align=True)
172         row.operator("view3d.camera_to_view", text="Camera To view")
173         row.operator("stored_views.newcamera")
174
175         row = col.row(align=True)
176         row.prop_enum(stored_views, "mode", 'POV')
177         row.prop_enum(stored_views, "mode", 'LAYERS')
178         row.prop_enum(stored_views, "mode", 'DISPLAY')
179
180         # UI : operators
181         row = layout.row()
182         row.operator("stored_views.save").index = -1
183
184         # IO Operators
185         if core.get_preferences():
186             row = layout.row(align=True)
187             row.operator("stored_views.import_from_scene", text="Import from Scene")
188             row.operator("stored_views.import_blsv", text="", icon="IMPORT")
189             row.operator("stored_views.export_blsv", text="", icon="EXPORT")
190
191         data_store = core.DataStore()
192         list = data_store.list
193         # UI : items list
194         if len(list) > 0:
195             row = layout.row()
196             box = row.box()
197             # items list
198             mode = stored_views.mode
199             for i in range(len(list)):
200                 # associated icon
201                 icon_string = "MESH_CUBE"  # default icon
202                 # TODO: icons for view
203                 if mode == 'POV':
204                     persp = list[i].perspective
205                     if persp == 'PERSP':
206                         icon_string = "MESH_CUBE"
207                     elif persp == 'ORTHO':
208                         icon_string = "MESH_PLANE"
209                     elif persp == 'CAMERA':
210                         if list[i].camera_type != 'CAMERA':
211                             icon_string = 'OBJECT_DATAMODE'
212                         else:
213                             icon_string = "OUTLINER_DATA_CAMERA"
214                 if mode == 'LAYERS':
215                     if list[i].lock_camera_and_layers is True:
216                         icon_string = 'SCENE_DATA'
217                     else:
218                         icon_string = 'RENDERLAYERS'
219                 if mode == 'DISPLAY':
220                     shade = list[i].viewport_shade
221                     if shade == 'TEXTURED':
222                         icon_string = 'TEXTURE_SHADED'
223                     if shade == 'MATERIAL':
224                         icon_string = 'MATERIAL_DATA'
225                     elif shade == 'SOLID':
226                         icon_string = 'SOLID'
227                     elif shade == 'WIREFRAME':
228                         icon_string = "WIRE"
229                     elif shade == 'BOUNDBOX':
230                         icon_string = 'BBOX'
231                     elif shade == 'RENDERED':
232                         icon_string = 'MATERIAL'
233                 # stored view row
234                 subrow = box.row(align=True)
235                 # current view indicator
236                 if data_store.current_index == i and context.scene.stored_views.view_modified is False:
237                     subrow.label(text="", icon='SMALL_TRI_RIGHT_VEC')
238                 subrow.operator("stored_views.set",
239                                 text="", icon=icon_string).index = i
240                 subrow.prop(list[i], "name", text="")
241                 subrow.operator("stored_views.save",
242                                 text="", icon="REC").index = i
243                 subrow.operator("stored_views.delete",
244                                 text="", icon="PANEL_CLOSE").index = i
245
246         layout = self.layout
247         scene = context.scene
248         layout.label(text="Camera Selector")
249         cameras = sorted([o for o in scene.objects if o.type == 'CAMERA'],
250                          key=lambda o: o.name)
251
252         if len(cameras) > 0:
253             for camera in cameras:
254                 row = layout.row(align=True)
255                 row.context_pointer_set("active_object", camera)
256                 row.operator("cameraselector.set_scene_camera",
257                                    text=camera.name, icon='OUTLINER_DATA_CAMERA')
258                 row.operator("cameraselector.preview_scene_camera",
259                                    text='', icon='RESTRICT_VIEW_OFF')
260                 row.operator("cameraselector.add_camera_marker",
261                                    text='', icon='MARKER')
262         else:
263             layout.label(text="No cameras in this scene")