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
21bl_info = {
22    "name": "Hotkey: 'Ctrl Tab'",
23    "description": "Switch between 3d view object/edit modes",
24    "author": "pitiwazou, meta-androcto, italic",
25    "version": (0, 1, 2),
26    "blender": (2, 80, 0),
27    "location": "3D View",
28    "warning": "",
29    "doc_url": "",
30    "category": "Mode Switch Pie"
31}
32
33import bpy
34from bpy.types import (
35    Menu,
36    Operator
37)
38
39
40class PIE_OT_ClassObject(Operator):
41    bl_idname = "class.object"
42    bl_label = "Class Object"
43    bl_description = "Edit/Object Mode Switch"
44    bl_options = {'REGISTER', 'UNDO'}
45
46    def execute(self, context):
47        if context.object.mode == "OBJECT":
48            bpy.ops.object.mode_set(mode="EDIT")
49        else:
50            bpy.ops.object.mode_set(mode="OBJECT")
51        return {'FINISHED'}
52
53
54class PIE_OT_ClassTexturePaint(Operator):
55    bl_idname = "class.pietexturepaint"
56    bl_label = "Class Texture Paint"
57    bl_description = "Texture Paint"
58    bl_options = {'REGISTER', 'UNDO'}
59
60    def execute(self, context):
61        if context.object.mode == "EDIT":
62            bpy.ops.object.mode_set(mode="OBJECT")
63            bpy.ops.paint.texture_paint_toggle()
64        else:
65            bpy.ops.paint.texture_paint_toggle()
66        return {'FINISHED'}
67
68
69class PIE_OT_ClassWeightPaint(Operator):
70    bl_idname = "class.pieweightpaint"
71    bl_label = "Class Weight Paint"
72    bl_description = "Weight Paint"
73    bl_options = {'REGISTER', 'UNDO'}
74
75    def execute(self, context):
76        if context.object.mode == "EDIT":
77            bpy.ops.object.mode_set(mode="OBJECT")
78            bpy.ops.paint.weight_paint_toggle()
79        else:
80            bpy.ops.paint.weight_paint_toggle()
81        return {'FINISHED'}
82
83
84class PIE_OT_ClassVertexPaint(Operator):
85    bl_idname = "class.pievertexpaint"
86    bl_label = "Class Vertex Paint"
87    bl_description = "Vertex Paint"
88    bl_options = {'REGISTER', 'UNDO'}
89
90    def execute(self, context):
91        if context.object.mode == "EDIT":
92            bpy.ops.object.mode_set(mode="OBJECT")
93            bpy.ops.paint.vertex_paint_toggle()
94        else:
95            bpy.ops.paint.vertex_paint_toggle()
96        return {'FINISHED'}
97
98
99class PIE_OT_ClassParticleEdit(Operator):
100    bl_idname = "class.pieparticleedit"
101    bl_label = "Class Particle Edit"
102    bl_description = "Particle Edit (must have active particle system)"
103    bl_options = {'REGISTER', 'UNDO'}
104
105    def execute(self, context):
106        if context.object.mode == "EDIT":
107            bpy.ops.object.mode_set(mode="OBJECT")
108            bpy.ops.particle.particle_edit_toggle()
109        else:
110            bpy.ops.particle.particle_edit_toggle()
111        return {'FINISHED'}
112
113
114# Set Mode Operator #
115class PIE_OT_SetObjectModePie(Operator):
116    bl_idname = "object.set_object_mode_pie"
117    bl_label = "Set the object interactive mode"
118    bl_description = "I set the interactive mode of object"
119    bl_options = {'REGISTER'}
120
121    mode: bpy.props.StringProperty(name="Interactive mode", default="OBJECT")
122
123    def execute(self, context):
124        if (context.active_object):
125            try:
126                bpy.ops.object.mode_set(mode=self.mode)
127            except TypeError:
128                msg = context.active_object.name + " It is not possible to enter into the interactive mode"
129                self.report(type={"WARNING"}, message=msg)
130        else:
131            self.report(type={"WARNING"}, message="There is no active object")
132        return {'FINISHED'}
133
134
135# Edit Selection Modes
136class PIE_OT_ClassVertex(Operator):
137    bl_idname = "class.vertex"
138    bl_label = "Class Vertex"
139    bl_description = "Vert Select Mode"
140    bl_options = {'REGISTER', 'UNDO'}
141
142    def execute(self, context):
143        if context.object.mode != "EDIT":
144            bpy.ops.object.mode_set(mode="EDIT")
145            bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='VERT')
146        if bpy.ops.mesh.select_mode != "EDGE, FACE":
147            bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='VERT')
148            return {'FINISHED'}
149
150
151class PIE_OT_ClassEdge(Operator):
152    bl_idname = "class.edge"
153    bl_label = "Class Edge"
154    bl_description = "Edge Select Mode"
155    bl_options = {'REGISTER', 'UNDO'}
156
157    def execute(self, context):
158        if context.object.mode != "EDIT":
159            bpy.ops.object.mode_set(mode="EDIT")
160            bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='EDGE')
161        if bpy.ops.mesh.select_mode != "VERT, FACE":
162            bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='EDGE')
163            return {'FINISHED'}
164
165
166class PIE_OT_ClassFace(Operator):
167    bl_idname = "class.face"
168    bl_label = "Class Face"
169    bl_description = "Face Select Mode"
170    bl_options = {'REGISTER', 'UNDO'}
171
172    def execute(self, context):
173        if context.object.mode != "EDIT":
174            bpy.ops.object.mode_set(mode="EDIT")
175            bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='FACE')
176        if bpy.ops.mesh.select_mode != "VERT, EDGE":
177            bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='FACE')
178            return {'FINISHED'}
179
180
181class PIE_OT_VertsEdgesFaces(Operator):
182    bl_idname = "verts.edgesfaces"
183    bl_label = "Verts Edges Faces"
184    bl_description = "Vert/Edge/Face Select Mode"
185    bl_options = {'REGISTER', 'UNDO'}
186
187    def execute(self, context):
188        if context.object.mode != "EDIT":
189            bpy.ops.object.mode_set(mode="EDIT")
190            bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='VERT')
191        if bpy.ops.mesh.select_mode != "VERT, EDGE, FACE":
192            bpy.ops.object.mode_set(mode="EDIT")
193            bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='VERT')
194            bpy.ops.mesh.select_mode(use_extend=True, use_expand=False, type='EDGE')
195            bpy.ops.mesh.select_mode(use_extend=True, use_expand=False, type='FACE')
196            return {'FINISHED'}
197
198
199# Menus
200class PIE_MT_ObjectEditotherModes(Menu):
201    """Edit/Object Others modes"""
202    bl_idname = "MENU_MT_objecteditmodeothermodes"
203    bl_label = "Edit Selection Modes"
204
205    def draw(self, context):
206        layout = self.layout
207        pie = layout.menu_pie()
208        box = pie.split().column()
209
210        box.operator("class.vertex", text="Vertex", icon='VERTEXSEL')
211        box.operator("class.edge", text="Edge", icon='EDGESEL')
212        box.operator("class.face", text="Face", icon='FACESEL')
213        box.operator("verts.edgesfaces", text="Vertex/Edges/Faces", icon='OBJECT_DATAMODE')
214
215
216class PIE_MT_ObjectEditMode(Menu):
217    """Modes Switch"""
218    bl_idname = "PIE_MT_objecteditmode"
219    bl_label = "Mode Switch (Ctrl Tab)"
220
221    def draw(self, context):
222        layout = self.layout
223        ob = context.object
224        # No Object Selected #
225        if not ob or not ob.select_get():
226            message = "No Active Object Selected"
227            pie = layout.menu_pie()
228            pie.separator()
229            pie.separator()
230            pie.separator()
231            box = pie.box()
232            box.label(text=message, icon="INFO")
233
234        elif ob and ob.type == 'MESH' and ob.mode in {'OBJECT', 'SCULPT', 'VERTEX_PAINT',
235                                                    'WEIGHT_PAINT', 'TEXTURE_PAINT',
236                                                    'PARTICLE_EDIT', 'GPENCIL_EDIT'}:
237            pie = layout.menu_pie()
238            # 4 - LEFT
239            pie.operator("class.pieweightpaint", text="Weight Paint", icon='WPAINT_HLT')
240            # 6 - RIGHT
241            pie.operator("class.pietexturepaint", text="Texture Paint", icon='TPAINT_HLT')
242            # 2 - BOTTOM
243            pie.menu("MENU_MT_objecteditmodeothermodes", text="Edit Modes", icon='EDITMODE_HLT')
244            # 8 - TOP
245            pie.operator("class.object", text="Object/Edit Toggle", icon='OBJECT_DATAMODE')
246            # 7 - TOP - LEFT
247            pie.operator("sculpt.sculptmode_toggle", text="Sculpt", icon='SCULPTMODE_HLT')
248            # 9 - TOP - RIGHT
249            pie.operator("class.pievertexpaint", text="Vertex Paint", icon='VPAINT_HLT')
250            # 1 - BOTTOM - LEFT
251            pie.separator()
252            # 3 - BOTTOM - RIGHT
253            if context.object.particle_systems:
254                pie.operator("class.pieparticleedit", text="Particle Edit", icon='PARTICLEMODE')
255            else:
256                pie.separator()
257
258        elif ob and ob.type == 'MESH' and ob.mode in {'EDIT'}:
259            pie = layout.menu_pie()
260            # 4 - LEFT
261            pie.operator("class.pieweightpaint", text="Weight Paint", icon='WPAINT_HLT')
262            # 6 - RIGHT
263            pie.operator("class.pietexturepaint", text="Texture Paint", icon='TPAINT_HLT')
264            # 2 - BOTTOM
265            pie.menu("MENU_MT_objecteditmodeothermodes", text="Edit Modes", icon='EDITMODE_HLT')
266            # 8 - TOP
267            pie.operator("class.object", text="Edit/Object Toggle", icon='OBJECT_DATAMODE')
268            # 7 - TOP - LEFT
269            pie.operator("sculpt.sculptmode_toggle", text="Sculpt", icon='SCULPTMODE_HLT')
270            # 9 - TOP - RIGHT
271            pie.operator("class.pievertexpaint", text="Vertex Paint", icon='VPAINT_HLT')
272            # 1 - BOTTOM - LEFT
273            pie.separator()
274            # 3 - BOTTOM - RIGHT
275            if context.object.particle_systems:
276                pie.operator("class.pieparticleedit", text="Particle Edit", icon='PARTICLEMODE')
277            else:
278                pie.separator()
279
280        elif ob and ob.type == 'CURVE':
281            pie = layout.menu_pie()
282            # 4 - LEFT
283            pie.separator()
284            # 6 - RIGHT
285            pie.separator()
286            # 2 - BOTTOM
287            pie.separator()
288            # 8 - TOP
289            pie.operator("object.editmode_toggle", text="Edit/Object", icon='OBJECT_DATAMODE')
290            # 7 - TOP - LEFT
291            pie.separator()
292            # 9 - TOP - RIGHT
293            pie.separator()
294            # 1 - BOTTOM - LEFT
295            pie.separator()
296            # 3 - BOTTOM - RIGHT
297            pie.separator()
298
299        elif ob and ob.type == 'ARMATURE':
300            pie = layout.menu_pie()
301            # 4 - LEFT
302            pie.operator(PIE_OT_SetObjectModePie.bl_idname, text="Object", icon="OBJECT_DATAMODE").mode = "OBJECT"
303            # 6 - RIGHT
304            pie.operator(PIE_OT_SetObjectModePie.bl_idname, text="Pose", icon="POSE_HLT").mode = "POSE"
305            # 2 - BOTTOM
306            pie.operator(PIE_OT_SetObjectModePie.bl_idname, text="Edit", icon="EDITMODE_HLT").mode = "EDIT"
307            # 8 - TOP
308            pie.operator("object.editmode_toggle", text="Edit Mode", icon='OBJECT_DATAMODE')
309            # 7 - TOP - LEFT
310            pie.separator()
311            # 9 - TOP - RIGHT
312            pie.separator()
313            # 1 - BOTTOM - LEFT
314            pie.separator()
315            # 3 - BOTTOM - RIGHT
316            pie.separator()
317
318        elif ob and ob.type == 'FONT':
319            pie = layout.menu_pie()
320            pie.separator()
321            pie.separator()
322            pie.separator()
323            pie.operator("object.editmode_toggle", text="Edit/Object Toggle", icon='OBJECT_DATAMODE')
324            pie.separator()
325            pie.separator()
326            pie.separator()
327            # 3 - BOTTOM - RIGHT
328            pie.separator()
329
330        elif ob and ob.type == 'SURFACE':
331            pie = layout.menu_pie()
332            pie.separator()
333            pie.separator()
334            pie.separator()
335            pie.operator("object.editmode_toggle", text="Edit/Object Toggle", icon='OBJECT_DATAMODE')
336            pie.separator()
337            pie.separator()
338            pie.separator()
339            # 3 - BOTTOM - RIGHT
340            pie.separator()
341
342        elif ob and ob.type == 'META':
343            pie = layout.menu_pie()
344            pie.separator()
345            pie.separator()
346            pie.separator()
347            pie.operator("object.editmode_toggle", text="Edit/Object Toggle", icon='OBJECT_DATAMODE')
348            pie.separator()
349            pie.separator()
350            pie.separator()
351            # 3 - BOTTOM - RIGHT
352            pie.separator()
353
354        elif ob and ob.type == 'LATTICE':
355            pie = layout.menu_pie()
356            pie.separator()
357            pie.separator()
358            pie.separator()
359            pie.operator("object.editmode_toggle", text="Edit/Object Toggle", icon='OBJECT_DATAMODE')
360            pie.separator()
361            pie.separator()
362            pie.separator()
363
364        if ob and ob.type == 'GPENCIL':
365            pie = layout.menu_pie()
366            # 4 - LEFT
367            pie.operator(PIE_OT_SetObjectModePie.bl_idname, text="Sculpt", icon="SCULPTMODE_HLT").mode = "SCULPT_GPENCIL"
368            # 6 - RIGHT
369            pie.operator(PIE_OT_SetObjectModePie.bl_idname, text="Draw", icon="GREASEPENCIL").mode = "PAINT_GPENCIL"
370            # 2 - BOTTOM
371            pie.operator(PIE_OT_SetObjectModePie.bl_idname, text="Edit", icon="EDITMODE_HLT").mode = "EDIT_GPENCIL"
372            # 8 - TOP
373            pie.operator(PIE_OT_SetObjectModePie.bl_idname, text="Object", icon="OBJECT_DATAMODE").mode = "OBJECT"
374            # 7 - TOP - LEFT
375            pie.separator()
376            # 9 - TOP - RIGHT
377            pie.separator()
378            # 1 - BOTTOM - LEFT
379            pie.separator()
380            # 3 - BOTTOM - RIGHT
381            pie.operator(PIE_OT_SetObjectModePie.bl_idname, text="Weight Paint", icon="WPAINT_HLT").mode = "WEIGHT_GPENCIL"
382
383
384
385        elif ob and ob.type in {"LIGHT", "CAMERA", "EMPTY", "SPEAKER"}:
386            message = "Active Object has only Object Mode available"
387            pie = layout.menu_pie()
388            pie.separator()
389            pie.separator()
390            pie.separator()
391            box = pie.box()
392            box.label(text=message, icon="INFO")
393
394
395classes = (
396    PIE_MT_ObjectEditMode,
397    PIE_OT_ClassObject,
398    PIE_OT_ClassVertex,
399    PIE_OT_ClassEdge,
400    PIE_OT_ClassFace,
401    PIE_MT_ObjectEditotherModes,
402    PIE_OT_ClassTexturePaint,
403    PIE_OT_ClassWeightPaint,
404    PIE_OT_ClassVertexPaint,
405    PIE_OT_ClassParticleEdit,
406    PIE_OT_VertsEdgesFaces,
407    PIE_OT_SetObjectModePie,
408    )
409
410addon_keymaps = []
411
412
413def register():
414    for cls in classes:
415        bpy.utils.register_class(cls)
416
417    wm = bpy.context.window_manager
418    if wm.keyconfigs.addon:
419        # Select Mode
420        km = wm.keyconfigs.addon.keymaps.new(name='Object Non-modal')
421        kmi = km.keymap_items.new('wm.call_menu_pie', 'TAB', 'PRESS', ctrl=True)
422        kmi.properties.name = "PIE_MT_objecteditmode"
423        addon_keymaps.append((km, kmi))
424
425        km = wm.keyconfigs.addon.keymaps.new(name='Grease Pencil Stroke Edit Mode')
426        kmi = km.keymap_items.new('wm.call_menu_pie', 'TAB', 'PRESS', ctrl=True)
427        kmi.properties.name = "PIE_MT_objecteditmode"
428        addon_keymaps.append((km, kmi))
429
430
431def unregister():
432    for cls in classes:
433        bpy.utils.unregister_class(cls)
434
435    wm = bpy.context.window_manager
436    kc = wm.keyconfigs.addon
437    if kc:
438        for km, kmi in addon_keymaps:
439            km.keymap_items.remove(kmi)
440    addon_keymaps.clear()
441
442
443if __name__ == "__main__":
444    register()
445