2021-07-20 18:53:39 +02:00
|
|
|
from os import error
|
2021-06-25 18:17:51 +02:00
|
|
|
import bpy
|
|
|
|
import re
|
2021-10-28 14:33:37 +02:00
|
|
|
|
2021-06-25 18:17:51 +02:00
|
|
|
from bpy.types import Operator
|
|
|
|
from bpy.props import StringProperty, BoolProperty, EnumProperty
|
2021-07-20 18:53:39 +02:00
|
|
|
from bpy.app.handlers import persistent
|
2021-10-28 14:33:37 +02:00
|
|
|
from .utils import get_addon_prefs, is_vector_close
|
2021-06-25 18:17:51 +02:00
|
|
|
|
|
|
|
|
|
|
|
# --- OPS ---
|
|
|
|
|
2021-07-27 18:48:38 +02:00
|
|
|
# PATTERN = r'([A-Z]{2})?_?([A-Z]{2})?_?(.*)' # bad ! match whithout separator
|
2021-06-25 18:17:51 +02:00
|
|
|
# pattern = r'(?:(^[A-Z]{2})_)?(?:([A-Z]{2})_)?(.*)' # matching only two letter
|
|
|
|
# pattern = r'^([A-Z]{2}_)?([A-Z]{2}_)?(.*)' # matching letters with separator
|
|
|
|
|
2021-07-20 18:53:39 +02:00
|
|
|
# pattern = r'^([A-Z]{1,6}_)?([A-Z]{1,6}_)?(.*)' # matching capital letters from one to six
|
2021-07-27 18:48:38 +02:00
|
|
|
# pattern = r'^([A-Z]{1,6}_)?([A-Z]{1,6}_)?(.*?)(_[A-Z]{2})?$' # 2 letter suffix
|
|
|
|
# pattern = r'^(?P<tag>[A-Z]{1,6}_)?(?P<tag2>[A-Z]{1,6}_)?(?P<name>.*?)(?P<sfix>_[A-Z]{2})?$' # named
|
|
|
|
# pattern = r'^(?P<grp>-\s)?(?P<tag>[A-Z]{2}_)?(?P<tag2>[A-Z]{1,6}_)?(?P<name>.*?)(?P<sfix>_[A-Z]{2})?$' # group start ' - '
|
|
|
|
# PATTERN = r'^(?P<grp>-\s)?(?P<tag>[A-Z]{2}_)?(?P<tag2>[A-Z]{1,6}_)?(?P<name>.*?)(?P<sfix>_[A-Z]{2})?(?P<inc>\.\d{3})?$' # numering
|
|
|
|
PATTERN = r'^(?P<grp>-\s)?(?P<tag>[A-Z]{2}_)?(?P<name>.*?)(?P<sfix>_[A-Z]{2})?(?P<inc>\.\d{3})?$' # numering
|
2021-07-20 18:53:39 +02:00
|
|
|
|
2021-07-27 18:48:38 +02:00
|
|
|
|
|
|
|
def layer_name_build(layer, prefix='', desc='', suffix=''):
|
2022-01-25 15:13:08 +01:00
|
|
|
'''GET a layer and argument to build and assign name
|
|
|
|
return new name
|
|
|
|
'''
|
2021-07-27 18:48:38 +02:00
|
|
|
|
|
|
|
|
|
|
|
prefs = get_addon_prefs()
|
|
|
|
sep = prefs.separator
|
2022-01-25 15:13:08 +01:00
|
|
|
name = old = layer.info
|
2023-02-07 12:03:00 +01:00
|
|
|
|
2021-07-27 18:48:38 +02:00
|
|
|
pattern = PATTERN.replace('_', sep) # set separator
|
2023-02-07 12:03:00 +01:00
|
|
|
|
2021-07-27 18:48:38 +02:00
|
|
|
res = re.search(pattern, name.strip())
|
|
|
|
|
|
|
|
# prefix -> tag
|
|
|
|
# prefix2 -> tag2
|
|
|
|
# desc -> name
|
|
|
|
# suffix -> sfix
|
|
|
|
grp = '' if res.group('grp') is None else res.group('grp')
|
|
|
|
tag = '' if res.group('tag') is None else res.group('tag')
|
|
|
|
# tag2 = '' if res.group('tag2') is None else res.group('tag2')
|
|
|
|
name = '' if res.group('name') is None else res.group('name')
|
|
|
|
sfix = '' if res.group('sfix') is None else res.group('sfix')
|
|
|
|
inc = '' if res.group('inc') is None else res.group('inc')
|
|
|
|
|
|
|
|
if grp:
|
|
|
|
grp = ' ' + grp # name is strip(), so grp first spaces are gones.
|
|
|
|
|
|
|
|
if prefix:
|
|
|
|
if prefix == 'prefixkillcode':
|
|
|
|
tag = ''
|
|
|
|
else:
|
|
|
|
tag = prefix.upper().strip() + sep
|
|
|
|
# if prefix2:
|
|
|
|
# tag2 = prefix2.upper().strip() + sep
|
|
|
|
if desc:
|
|
|
|
name = desc
|
2023-02-07 12:03:00 +01:00
|
|
|
|
2021-07-27 18:48:38 +02:00
|
|
|
if suffix:
|
|
|
|
if suffix == 'suffixkillcode':
|
|
|
|
sfix = ''
|
|
|
|
else:
|
|
|
|
sfix = sep + suffix.upper().strip()
|
|
|
|
|
|
|
|
# check if name is available without the increment ending
|
|
|
|
new = f'{grp}{tag}{name}{sfix}'
|
|
|
|
|
|
|
|
layer.info = new
|
2023-02-07 12:03:00 +01:00
|
|
|
|
2022-01-25 15:13:08 +01:00
|
|
|
## update name in modifier targets
|
|
|
|
if old != new:
|
|
|
|
# find objects using this GP datablock
|
|
|
|
for ob_user in [o for o in bpy.data.objects if o.data == layer.id_data]: # bpy.context.scene.objects
|
|
|
|
# maybe a more elegant way exists to find all objects users ?
|
2023-02-07 12:03:00 +01:00
|
|
|
|
2022-01-25 15:13:08 +01:00
|
|
|
# update Gpencil modifier targets
|
|
|
|
for mod in ob_user.grease_pencil_modifiers:
|
|
|
|
if not hasattr(mod, 'layer'):
|
|
|
|
continue
|
|
|
|
if mod.layer == old:
|
|
|
|
mod.layer = new
|
2021-07-27 18:48:38 +02:00
|
|
|
|
2023-02-07 12:03:00 +01:00
|
|
|
"""
|
2021-07-20 18:53:39 +02:00
|
|
|
def layer_name_build(layer, prefix='', prefix2='', desc='', suffix=''):
|
2021-06-25 18:17:51 +02:00
|
|
|
'''GET a layer and infos to build name
|
2021-07-20 18:53:39 +02:00
|
|
|
Can take one or two prefix and description/name of the layer)
|
2021-06-25 18:17:51 +02:00
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
|
|
prefs = get_addon_prefs()
|
|
|
|
sep = prefs.separator
|
|
|
|
name = layer.info
|
2023-02-07 12:03:00 +01:00
|
|
|
|
2021-06-25 18:17:51 +02:00
|
|
|
pattern = pattern.replace('_', sep) # set separator
|
2023-02-07 12:03:00 +01:00
|
|
|
|
2021-07-20 18:53:39 +02:00
|
|
|
res = re.search(pattern, name.strip())
|
|
|
|
p1 = '' if res.group(1) is None else res.group(1)
|
|
|
|
p2 = '' if res.group(2) is None else res.group(2)
|
|
|
|
p3 = '' if res.group(3) is None else res.group(3)
|
|
|
|
p4 = '' if res.group(4) is None else res.group(4)
|
2021-06-25 18:17:51 +02:00
|
|
|
|
|
|
|
if prefix:
|
|
|
|
if prefix == 'prefixkillcode':
|
|
|
|
p1 = ''
|
|
|
|
else:
|
2021-07-20 18:53:39 +02:00
|
|
|
p1 = prefix.upper().strip() + sep
|
2021-06-25 18:17:51 +02:00
|
|
|
|
|
|
|
if prefix2:
|
2021-07-20 18:53:39 +02:00
|
|
|
p2 = prefix2.upper().strip() + sep
|
2023-02-07 12:03:00 +01:00
|
|
|
|
2021-06-25 18:17:51 +02:00
|
|
|
if desc:
|
|
|
|
p3 = desc
|
2023-02-07 12:03:00 +01:00
|
|
|
|
2021-07-20 18:53:39 +02:00
|
|
|
if suffix:
|
|
|
|
if suffix == 'suffixkillcode':
|
|
|
|
p4 = ''
|
|
|
|
else:
|
|
|
|
p4 = sep + suffix.upper().strip()
|
2021-06-25 18:17:51 +02:00
|
|
|
|
2021-07-20 18:53:39 +02:00
|
|
|
new = f'{p1}{p2}{p3}{p4}'
|
2021-06-25 18:17:51 +02:00
|
|
|
layer.info = new
|
2021-07-27 18:48:38 +02:00
|
|
|
"""
|
2021-06-25 18:17:51 +02:00
|
|
|
|
|
|
|
## multi-prefix solution (Caps letters)
|
|
|
|
class GPTB_OT_layer_name_build(Operator):
|
|
|
|
bl_idname = "gp.layer_name_build"
|
|
|
|
bl_label = "Layer Name Build"
|
|
|
|
bl_description = "Change prefix of layer name"
|
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def poll(cls, context):
|
|
|
|
return True
|
2022-10-08 01:15:34 +02:00
|
|
|
|
2021-06-25 18:17:51 +02:00
|
|
|
prefix : StringProperty(default='', options={'SKIP_SAVE'})
|
2021-07-27 18:48:38 +02:00
|
|
|
# prefix2 : StringProperty(default='', options={'SKIP_SAVE'})
|
2021-06-25 18:17:51 +02:00
|
|
|
desc : StringProperty(default='', options={'SKIP_SAVE'})
|
2021-07-20 18:53:39 +02:00
|
|
|
suffix : StringProperty(default='', options={'SKIP_SAVE'})
|
2022-10-09 00:28:14 +02:00
|
|
|
tooltip : StringProperty(default='', options={'SKIP_SAVE'})
|
2021-06-25 18:17:51 +02:00
|
|
|
|
2022-10-08 01:15:34 +02:00
|
|
|
@classmethod
|
|
|
|
def description(cls, context, properties):
|
2022-10-09 00:28:14 +02:00
|
|
|
tag = properties.prefix if properties.prefix else properties.suffix
|
|
|
|
if properties.tooltip:
|
|
|
|
return f"Use prefix: {tag} ({properties.tooltip})"
|
2022-10-08 01:15:34 +02:00
|
|
|
else:
|
2022-10-09 00:28:14 +02:00
|
|
|
return f"Use prefix: {tag}"
|
2022-10-08 01:15:34 +02:00
|
|
|
|
2021-06-25 18:17:51 +02:00
|
|
|
def execute(self, context):
|
|
|
|
ob = context.object
|
|
|
|
gpl = ob.data.layers
|
|
|
|
act = gpl.active
|
|
|
|
if not act:
|
|
|
|
self.report({'ERROR'}, 'no layer active')
|
|
|
|
return {"CANCELLED"}
|
2021-07-20 18:53:39 +02:00
|
|
|
|
2021-09-09 12:23:14 +02:00
|
|
|
layer_name_build(act, prefix=self.prefix, desc=self.desc, suffix=self.suffix)
|
|
|
|
|
|
|
|
## Deactivate multi-selection on layer !
|
|
|
|
## somethimes it affect a random layer that is still considered selected
|
|
|
|
# for l in gpl:
|
|
|
|
# if l.select or l == act:
|
|
|
|
# layer_name_build(l, prefix=self.prefix, desc=self.desc, suffix=self.suffix)
|
|
|
|
|
2021-07-27 18:48:38 +02:00
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
2021-07-29 11:20:43 +02:00
|
|
|
def grp_toggle(l, mode='TOGGLE'):
|
|
|
|
'''take mode in (TOGGLE, GROUP, UNGROUP) '''
|
2021-07-27 18:48:38 +02:00
|
|
|
grp_item_id = ' - '
|
|
|
|
res = re.search(r'^(\s{1,3}-\s{0,3})(.*)', l.info)
|
2021-07-29 11:20:43 +02:00
|
|
|
if not res and mode in ('TOGGLE', 'GROUP'):
|
|
|
|
# No gpr : add group prefix after stripping all space and dash
|
2021-07-27 18:48:38 +02:00
|
|
|
l.info = grp_item_id + l.info.lstrip(' -')
|
2023-02-07 12:03:00 +01:00
|
|
|
|
2021-07-29 11:20:43 +02:00
|
|
|
elif res and mode in ('TOGGLE', 'UNGROUP'):
|
|
|
|
# found : delete group prefix
|
2021-07-27 18:48:38 +02:00
|
|
|
l.info = res.group(2)
|
2023-02-07 12:03:00 +01:00
|
|
|
|
2021-07-27 18:48:38 +02:00
|
|
|
|
|
|
|
class GPTB_OT_layer_group_toggle(Operator):
|
|
|
|
bl_idname = "gp.layer_group_toggle"
|
|
|
|
bl_label = "Group Toggle"
|
|
|
|
bl_description = "Group or ungroup a layer"
|
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def poll(cls, context):
|
|
|
|
return True
|
2023-02-07 12:03:00 +01:00
|
|
|
|
2021-07-27 18:48:38 +02:00
|
|
|
# group : StringProperty(default='', options={'SKIP_SAVE'})
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
ob = context.object
|
|
|
|
gpl = ob.data.layers
|
|
|
|
act = gpl.active
|
|
|
|
if not act:
|
|
|
|
self.report({'ERROR'}, 'no layer active')
|
|
|
|
return {"CANCELLED"}
|
|
|
|
for l in gpl:
|
|
|
|
if l.select or l == act:
|
|
|
|
grp_toggle(l)
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
class GPTB_OT_layer_new_group(Operator):
|
|
|
|
bl_idname = "gp.layer_new_group"
|
|
|
|
bl_label = "New Group"
|
|
|
|
bl_description = "Create a group from active layer"
|
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
ob = context.object
|
|
|
|
gpl = ob.data.layers
|
|
|
|
act = gpl.active
|
|
|
|
if not act:
|
|
|
|
self.report({'ERROR'}, 'no layer active')
|
|
|
|
return {"CANCELLED"}
|
2021-07-29 11:20:43 +02:00
|
|
|
|
2021-07-27 18:48:38 +02:00
|
|
|
res = re.search(PATTERN, act.info)
|
|
|
|
if not res:
|
|
|
|
self.report({'ERROR'}, 'Could not create a group name, create a layer manually')
|
|
|
|
return {"CANCELLED"}
|
2023-02-07 12:03:00 +01:00
|
|
|
|
2021-07-29 11:20:43 +02:00
|
|
|
name = res.group('name').strip(' -')
|
2021-07-27 18:48:38 +02:00
|
|
|
if not name:
|
|
|
|
self.report({'ERROR'}, f'No name found in {act.info}')
|
|
|
|
return {"CANCELLED"}
|
2023-02-07 12:03:00 +01:00
|
|
|
|
2021-07-29 11:20:43 +02:00
|
|
|
if name in [l.info.strip(' -') for l in gpl]:
|
2021-07-27 18:48:38 +02:00
|
|
|
self.report({'WARNING'}, f'Name already exists: {act.info}')
|
|
|
|
return {"FINISHED"}
|
2021-07-29 11:20:43 +02:00
|
|
|
|
|
|
|
grp_toggle(act, mode='GROUP')
|
|
|
|
n = gpl.new(name, set_active=False)
|
|
|
|
n.use_onion_skinning = n.use_lights = False
|
|
|
|
n.hide = True
|
|
|
|
n.opacity = 0
|
2021-06-25 18:17:51 +02:00
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
|
|
#-## SELECTION MANAGEMENT ##-#
|
|
|
|
|
|
|
|
def activate_channel_group_color(context):
|
|
|
|
if not context.preferences.edit.use_anim_channel_group_colors:
|
|
|
|
context.preferences.edit.use_anim_channel_group_colors = True
|
|
|
|
|
|
|
|
def refresh_areas():
|
|
|
|
for area in bpy.context.screen.areas:
|
|
|
|
area.tag_redraw()
|
|
|
|
|
2021-10-28 14:33:37 +02:00
|
|
|
def build_layers_targets_from_dopesheet(context):
|
|
|
|
'''Return all selected layers on context GP dopesheet according to seelction and filters'''
|
|
|
|
ob = context.object
|
|
|
|
gpl = context.object.data.layers
|
|
|
|
act = gpl.active
|
|
|
|
dopeset = context.space_data.dopesheet
|
2023-02-07 12:03:00 +01:00
|
|
|
|
2021-10-28 14:33:37 +02:00
|
|
|
|
|
|
|
if dopeset.show_only_selected:
|
2024-11-11 15:35:39 +01:00
|
|
|
pool = [o for o in context.selected_objects if o.type == 'GREASEPENCIL']
|
2021-10-28 14:33:37 +02:00
|
|
|
else:
|
2024-11-11 15:35:39 +01:00
|
|
|
pool = [o for o in context.scene.objects if o.type == 'GREASEPENCIL']
|
2021-10-28 14:33:37 +02:00
|
|
|
if not dopeset.show_hidden:
|
|
|
|
pool = [o for o in pool if o.visible_get()]
|
|
|
|
|
|
|
|
layer_pool = [l for o in pool for l in o.data.layers]
|
|
|
|
layer_pool = list(set(layer_pool)) # remove dupli-layers from same data source with
|
2023-02-07 12:03:00 +01:00
|
|
|
|
2021-10-28 14:33:37 +02:00
|
|
|
# apply search filter
|
|
|
|
if dopeset.filter_text:
|
|
|
|
layer_pool = [l for l in layer_pool if (dopeset.filter_text.lower() in l.info.lower()) ^ dopeset.use_filter_invert]
|
|
|
|
|
|
|
|
return layer_pool
|
|
|
|
|
|
|
|
def build_dope_gp_list(layer_list):
|
|
|
|
'''Take a list of GP layers return a dict with pairs {gp data : own layer list}'''
|
|
|
|
from collections import defaultdict
|
|
|
|
gps = defaultdict(list)
|
|
|
|
for l in layer_list:
|
|
|
|
gps[l.id_data].append(l)
|
|
|
|
return gps
|
2021-06-25 18:17:51 +02:00
|
|
|
|
|
|
|
class GPTB_OT_select_set_same_prefix(Operator):
|
|
|
|
bl_idname = "gp.select_same_prefix"
|
|
|
|
bl_label = "Select Same Prefix"
|
|
|
|
bl_description = "Select layers that have the same prefix as active\nSet with ctrl+clic"
|
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def poll(cls, context):
|
2024-11-11 15:35:39 +01:00
|
|
|
return context.object and context.object.type == 'GREASEPENCIL'
|
2023-02-07 12:03:00 +01:00
|
|
|
|
2021-06-25 18:17:51 +02:00
|
|
|
mode : EnumProperty(default='SELECT', options={'SKIP_SAVE'},
|
|
|
|
items=(
|
|
|
|
('SELECT', "Select", "Select layer with same prefix as active"),
|
|
|
|
('SET', "Set", "Set prefix on selected layer to the same as active"),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
|
|
|
|
def invoke(self, context, event):
|
|
|
|
if event.ctrl:
|
|
|
|
self.mode = 'SET'
|
|
|
|
return self.execute(context)
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
prefs = get_addon_prefs()
|
|
|
|
sep = prefs.separator # '_'
|
2021-10-28 14:33:37 +02:00
|
|
|
|
|
|
|
gp = context.object.data
|
|
|
|
act = gp.layers.active
|
|
|
|
|
|
|
|
pool = build_layers_targets_from_dopesheet(context)
|
|
|
|
if not pool:
|
|
|
|
self.report({'ERROR'}, 'No layers found in current GP dopesheet')
|
|
|
|
return {"CANCELLED"}
|
|
|
|
|
|
|
|
gp_dic = build_dope_gp_list(pool)
|
|
|
|
if not act:
|
|
|
|
# Check in other displayed layer if there is an active one
|
|
|
|
for gp, _layer_list in gp_dic.items():
|
|
|
|
if gp.layers.active:
|
|
|
|
# overwrite gp variable at the same time
|
|
|
|
act = gp.layers.active
|
|
|
|
break
|
|
|
|
if not act:
|
|
|
|
self.report({'ERROR'}, 'No active layer to base action')
|
|
|
|
return {"CANCELLED"}
|
|
|
|
|
|
|
|
print(f'Select/Set ref layer: {gp.name} > {gp.layers.active.info}')
|
2021-06-25 18:17:51 +02:00
|
|
|
|
2021-07-27 18:48:38 +02:00
|
|
|
res = re.search(PATTERN, act.info)
|
2021-06-25 18:17:51 +02:00
|
|
|
if not res:
|
2021-10-28 14:33:37 +02:00
|
|
|
self.report({'ERROR'}, f'Error scanning {act.info}')
|
2021-06-25 18:17:51 +02:00
|
|
|
return {"CANCELLED"}
|
2021-10-28 14:33:37 +02:00
|
|
|
|
|
|
|
namespace = res.group('tag')
|
2021-06-25 18:17:51 +02:00
|
|
|
if not namespace:
|
|
|
|
self.report({'WARNING'}, f'No prefix detected in {act.info} with separator {sep}')
|
|
|
|
return {"CANCELLED"}
|
|
|
|
|
|
|
|
if self.mode == 'SELECT':
|
|
|
|
## with split
|
|
|
|
# namespace = act.info.split(sep,1)[0]
|
|
|
|
# namespace_bool_list = [l.info.split(sep,1)[0] == namespace for l in gpl]
|
2023-02-07 12:03:00 +01:00
|
|
|
|
2021-10-28 14:33:37 +02:00
|
|
|
## with reg # only active
|
|
|
|
# namespace_bool_list = [l.info.split(sep,1)[0] + sep == namespace for l in gpl]
|
|
|
|
# gpl.foreach_set('select', namespace_bool_list)
|
2023-02-07 12:03:00 +01:00
|
|
|
|
2021-10-28 14:33:37 +02:00
|
|
|
## don't work Need Foreach set per gp
|
|
|
|
# for l in pool:
|
|
|
|
# l.select = l.info.split(sep,1)[0] + sep == namespace
|
2023-02-07 12:03:00 +01:00
|
|
|
|
2021-10-28 14:33:37 +02:00
|
|
|
for gp, layers in gp_dic.items():
|
|
|
|
# check namespace + restrict selection to visible layers according to filters
|
|
|
|
# TODO : Should use the regex pattern to detect and compare r.group('tag')
|
|
|
|
namespace_bool_list = [(l in layers) and (l.info.split(sep,1)[0] + sep == namespace) for l in gp.layers]
|
|
|
|
gp.layers.foreach_set('select', namespace_bool_list)
|
2023-02-07 12:03:00 +01:00
|
|
|
|
2021-06-25 18:17:51 +02:00
|
|
|
elif self.mode == 'SET':
|
2023-02-07 12:03:00 +01:00
|
|
|
for l in pool:
|
2021-06-25 18:17:51 +02:00
|
|
|
if not l.select or l == act:
|
|
|
|
continue
|
|
|
|
layer_name_build(l, prefix=namespace.strip(sep))
|
|
|
|
|
|
|
|
refresh_areas()
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
2021-10-28 14:33:37 +02:00
|
|
|
|
2021-06-25 18:17:51 +02:00
|
|
|
class GPTB_OT_select_set_same_color(Operator):
|
|
|
|
bl_idname = "gp.select_same_color"
|
|
|
|
bl_label = "Select Same Color"
|
|
|
|
bl_description = "Select layers that have the same color as active\nSet with ctrl+clic"
|
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def poll(cls, context):
|
2024-11-11 15:35:39 +01:00
|
|
|
return context.object and context.object.type == 'GREASEPENCIL'
|
2023-02-07 12:03:00 +01:00
|
|
|
|
2021-06-25 18:17:51 +02:00
|
|
|
mode : EnumProperty(default='SELECT', options={'SKIP_SAVE'},
|
|
|
|
items=(
|
|
|
|
('SELECT', "Select", "Select layer with same prefix as active"),
|
|
|
|
('SET', "Set", "Set prefix on selected layer to the same as active"),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
|
|
|
|
def invoke(self, context, event):
|
|
|
|
if event.ctrl:
|
|
|
|
self.mode = 'SET'
|
|
|
|
return self.execute(context)
|
|
|
|
|
|
|
|
def execute(self, context):
|
2021-10-28 14:33:37 +02:00
|
|
|
gp = context.object.data
|
|
|
|
act = gp.layers.active
|
2021-06-25 18:17:51 +02:00
|
|
|
|
2021-10-28 14:33:37 +02:00
|
|
|
pool = build_layers_targets_from_dopesheet(context)
|
|
|
|
if not pool:
|
|
|
|
self.report({'ERROR'}, 'No layers found in current GP dopesheet')
|
|
|
|
return {"CANCELLED"}
|
|
|
|
|
|
|
|
gp_dic = build_dope_gp_list(pool)
|
|
|
|
if not act:
|
|
|
|
# Check in other displayed layer if there is an active one
|
|
|
|
for gp, _layer_list in gp_dic.items():
|
|
|
|
if gp.layers.active:
|
|
|
|
# overwrite gp variable at the same time
|
|
|
|
act = gp.layers.active
|
|
|
|
break
|
|
|
|
if not act:
|
|
|
|
self.report({'ERROR'}, 'No active layer to base action')
|
|
|
|
return {"CANCELLED"}
|
|
|
|
|
|
|
|
print(f'Select/Set ref layer: {gp.name} > {gp.layers.active.info}')
|
|
|
|
color = act.channel_color
|
2021-06-25 18:17:51 +02:00
|
|
|
if self.mode == 'SELECT':
|
2021-10-28 14:33:37 +02:00
|
|
|
## NEED FOREACH TO APPLY SELECT
|
2023-02-07 12:03:00 +01:00
|
|
|
|
2021-10-28 14:33:37 +02:00
|
|
|
## Only on active object
|
|
|
|
# same_color_bool = [l.channel_color == act.channel_color for l in gpl]
|
|
|
|
# gpl.foreach_set('select', same_color_bool) # only
|
|
|
|
|
|
|
|
# On multiple objects -- don't work, need foreach
|
|
|
|
# for l in pool:
|
|
|
|
# print(l.id_data.name, l.info, l.channel_color == act.channel_color)
|
|
|
|
# l.select = l.channel_color == act.channel_color
|
2023-02-07 12:03:00 +01:00
|
|
|
|
|
|
|
"""
|
2021-10-28 14:33:37 +02:00
|
|
|
gps = []
|
|
|
|
for l in pool:
|
|
|
|
if l.id_data not in gps:
|
|
|
|
gps.append(l.id_data)
|
|
|
|
for gp in gps:
|
|
|
|
same_color_bool = [(l in pool) and is_vector_close(l.channel_color, color) for l in gp.layers]
|
|
|
|
gp.layers.foreach_set('select', same_color_bool)
|
|
|
|
"""
|
|
|
|
for gp, layers in gp_dic.items():
|
|
|
|
# check color and restrict selection to visible layers according to filters
|
|
|
|
same_color_bool = [(l in layers) and is_vector_close(l.channel_color, color) for l in gp.layers]
|
|
|
|
gp.layers.foreach_set('select', same_color_bool)
|
2021-06-25 18:17:51 +02:00
|
|
|
|
|
|
|
elif self.mode == 'SET':
|
|
|
|
activate_channel_group_color(context)
|
2021-10-28 14:33:37 +02:00
|
|
|
for l in pool: # only on active object use gpl
|
2021-06-25 18:17:51 +02:00
|
|
|
if not l.select or l == act:
|
|
|
|
continue
|
2021-10-28 14:33:37 +02:00
|
|
|
l.channel_color = color
|
2021-06-25 18:17:51 +02:00
|
|
|
|
|
|
|
refresh_areas()
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
2021-07-13 16:23:39 +02:00
|
|
|
def replace_layer_name(target, replacement, selected_only=True, prefix_only=True, regex=False):
|
2021-06-25 18:17:51 +02:00
|
|
|
prefs = get_addon_prefs()
|
|
|
|
sep = prefs.separator
|
|
|
|
if not target:
|
|
|
|
return
|
2021-07-13 16:23:39 +02:00
|
|
|
|
2021-06-25 18:17:51 +02:00
|
|
|
gpl = bpy.context.object.data.layers
|
|
|
|
|
2021-07-13 16:23:39 +02:00
|
|
|
if selected_only:
|
2023-02-07 12:03:00 +01:00
|
|
|
lays = [l for l in gpl if l.select] # exclude : l.info != 'background'
|
2021-06-25 18:17:51 +02:00
|
|
|
else:
|
2021-07-13 16:23:39 +02:00
|
|
|
lays = [l for l in gpl] # exclude : if l.info != 'background'
|
2021-06-25 18:17:51 +02:00
|
|
|
|
|
|
|
ct = 0
|
|
|
|
for l in lays:
|
2021-07-13 16:23:39 +02:00
|
|
|
old = l.info
|
|
|
|
if regex:
|
|
|
|
new = re.sub(target, replacement, l.info)
|
|
|
|
if old != new:
|
|
|
|
l.info = new
|
|
|
|
print('rename:', old, '-->', new)
|
|
|
|
ct += 1
|
|
|
|
continue
|
|
|
|
|
|
|
|
if prefix_only:
|
|
|
|
if not sep in l.info:
|
|
|
|
# only if separator exists
|
|
|
|
continue
|
2021-06-25 18:17:51 +02:00
|
|
|
splited = l.info.split(sep)
|
|
|
|
prefix = splited[0]
|
|
|
|
new_prefix = prefix.replace(target, replacement)
|
|
|
|
if prefix != new_prefix:
|
|
|
|
splited[0] = new_prefix
|
|
|
|
l.info = sep.join(splited)
|
|
|
|
print('rename:', old, '-->', l.info)
|
2021-07-13 16:23:39 +02:00
|
|
|
ct += 1
|
2021-06-25 18:17:51 +02:00
|
|
|
|
|
|
|
else:
|
|
|
|
new = l.info.replace(target, replacement)
|
|
|
|
if old != new:
|
|
|
|
l.info = new
|
2021-07-13 16:23:39 +02:00
|
|
|
print('rename:', old, '-->', new)
|
|
|
|
ct += 1
|
2021-06-25 18:17:51 +02:00
|
|
|
return ct
|
|
|
|
|
|
|
|
class GPTB_OT_rename_gp_layer(Operator):
|
|
|
|
'''rename GP layers based on a search and replace'''
|
|
|
|
bl_idname = "gp.rename_gp_layers"
|
|
|
|
bl_label = "Rename Gp Layers"
|
|
|
|
bl_description = "Search/Replace string in all GP layers"
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def poll(cls, context):
|
2024-11-11 15:35:39 +01:00
|
|
|
return context.object and context.object.type == 'GREASEPENCIL'
|
2021-06-25 18:17:51 +02:00
|
|
|
|
|
|
|
find: StringProperty(name="Find", description="Name to replace", default="", maxlen=0, options={'ANIMATABLE'}, subtype='NONE')
|
|
|
|
replace: StringProperty(name="Repl", description="New name placed", default="", maxlen=0, options={'ANIMATABLE'}, subtype='NONE')
|
2021-07-13 16:23:39 +02:00
|
|
|
selected: BoolProperty(name="Selected Only", description="Affect only selected layers", default=False)
|
|
|
|
prefix: BoolProperty(name="Prefix Only", description="Affect only prefix of name (skip layer without separator in name)", default=False)
|
|
|
|
use_regex: BoolProperty(name="Regex", description="use regular expression (advanced), equivalent to python re.sub()", default=False)
|
2021-06-25 18:17:51 +02:00
|
|
|
|
|
|
|
def execute(self, context):
|
2021-07-13 16:23:39 +02:00
|
|
|
count = replace_layer_name(self.find, self.replace, selected_only=self.selected, prefix_only=self.prefix, regex=self.use_regex)
|
2021-06-25 18:17:51 +02:00
|
|
|
if count:
|
|
|
|
mess = str(count) + ' layers renamed'
|
|
|
|
self.report({'INFO'}, mess)
|
|
|
|
else:
|
|
|
|
self.report({'WARNING'}, 'No text found !')
|
|
|
|
|
|
|
|
return{'FINISHED'}
|
|
|
|
|
|
|
|
def invoke(self, context, event):
|
|
|
|
return context.window_manager.invoke_props_dialog(self)
|
|
|
|
|
|
|
|
def draw(self, context):
|
|
|
|
layout = self.layout
|
2021-07-13 16:23:39 +02:00
|
|
|
row = layout.row()
|
|
|
|
row_a= row.row()
|
|
|
|
row_a.prop(self, "selected")
|
2023-02-07 12:03:00 +01:00
|
|
|
|
2021-07-13 16:23:39 +02:00
|
|
|
row_b= row.row()
|
|
|
|
row_b.prop(self, "prefix")
|
|
|
|
row_c= row.row()
|
2023-02-07 12:03:00 +01:00
|
|
|
|
2021-07-13 16:23:39 +02:00
|
|
|
row_c.prop(self, "use_regex")
|
|
|
|
row_b.active = not self.use_regex
|
|
|
|
|
|
|
|
layout.prop(self, "find")
|
|
|
|
layout.prop(self, "replace")
|
2021-06-25 18:17:51 +02:00
|
|
|
|
|
|
|
|
|
|
|
## --- UI layer panel---
|
|
|
|
|
2021-07-27 18:48:38 +02:00
|
|
|
def layer_name_builder_ui(self, context):
|
2021-06-25 18:17:51 +02:00
|
|
|
'''appended to DATA_PT_gpencil_layers'''
|
2023-02-07 12:03:00 +01:00
|
|
|
|
2021-06-25 18:17:51 +02:00
|
|
|
prefs = get_addon_prefs()
|
2021-07-20 18:53:39 +02:00
|
|
|
if not prefs.show_prefix_buttons:
|
2021-06-25 18:17:51 +02:00
|
|
|
return
|
2022-10-09 00:28:14 +02:00
|
|
|
if not len(prefs.prefixes.namespaces) and not len(prefs.suffixes.namespaces):
|
2021-07-20 18:53:39 +02:00
|
|
|
return
|
|
|
|
|
2021-06-25 18:17:51 +02:00
|
|
|
layout = self.layout
|
2024-11-11 15:47:33 +01:00
|
|
|
# {'EDIT_GREASE_PENCIL', 'PAINT_GREASE_PENCIL','SCULPT_GREASE_PENCIL','WEIGHT_GREASE_PENCIL', 'VERTEX_GPENCIL'}
|
2021-06-25 18:17:51 +02:00
|
|
|
# layout.separator()
|
|
|
|
col = layout.column()
|
|
|
|
|
2022-10-08 01:15:34 +02:00
|
|
|
|
2021-06-25 18:17:51 +02:00
|
|
|
line_limit = 8
|
2023-02-07 12:03:00 +01:00
|
|
|
|
2022-10-08 01:15:34 +02:00
|
|
|
if len(prefs.prefixes.namespaces):
|
|
|
|
ct = 0
|
|
|
|
# can't use enumerate cause there can be hided prefix
|
|
|
|
for namespace in prefs.prefixes.namespaces:
|
|
|
|
if namespace.hide:
|
|
|
|
continue
|
|
|
|
if ct % line_limit == 0:
|
|
|
|
row = col.row(align=True)
|
|
|
|
ct += 1
|
2022-10-09 00:28:14 +02:00
|
|
|
op = row.operator("gp.layer_name_build", text=namespace.tag)
|
|
|
|
op.prefix = namespace.tag
|
|
|
|
op.tooltip = namespace.name
|
2023-02-07 12:03:00 +01:00
|
|
|
|
2022-10-08 01:15:34 +02:00
|
|
|
if ct > 0:
|
|
|
|
row.operator("gp.layer_name_build", text='', icon='X').prefix = 'prefixkillcode'
|
2021-06-25 18:17:51 +02:00
|
|
|
|
2022-10-08 01:15:34 +02:00
|
|
|
## old single string prefix method
|
|
|
|
"""
|
2021-07-20 18:53:39 +02:00
|
|
|
if prefs.prefixes:
|
2022-10-08 01:15:34 +02:00
|
|
|
p = prefs.prefixes.split(',')
|
2021-07-20 18:53:39 +02:00
|
|
|
for i, prefix in enumerate(all_prefixes):
|
|
|
|
if i % line_limit == 0:
|
|
|
|
row = col.row(align=True)
|
|
|
|
row.operator("gp.layer_name_build", text=prefix.upper() ).prefix = prefix
|
|
|
|
row.operator("gp.layer_name_build", text='', icon='X').prefix = 'prefixkillcode'
|
|
|
|
## secondary prefix ?
|
2021-06-25 18:17:51 +02:00
|
|
|
|
2022-10-09 00:28:14 +02:00
|
|
|
|
|
|
|
if prefs.suffixes:
|
|
|
|
all_suffixes = prefs.suffixes.split(',')
|
|
|
|
for i, suffix in enumerate(all_suffixes):
|
|
|
|
if i % line_limit == 0:
|
|
|
|
row = col.row(align=True)
|
|
|
|
row.operator("gp.layer_name_build", text=suffix.upper() ).suffix = suffix
|
|
|
|
row.operator("gp.layer_name_build", text='', icon='X').suffix = 'suffixkillcode'
|
|
|
|
"""
|
|
|
|
|
|
|
|
## name (description of layer content)
|
2021-06-25 18:17:51 +02:00
|
|
|
row = col.row(align=True)
|
|
|
|
row.prop(context.scene.gptoolprops, 'layer_name', text='')
|
2023-02-07 12:03:00 +01:00
|
|
|
|
2022-10-08 01:15:34 +02:00
|
|
|
## mimic groups using dash (disabled for now)
|
|
|
|
# row.operator("gp.layer_new_group", text='', icon='COLLECTION_NEW')
|
|
|
|
# row.operator("gp.layer_group_toggle", text='', icon='OUTLINER_OB_GROUP_INSTANCE')
|
2021-07-20 18:53:39 +02:00
|
|
|
## no need for desc ops, already trigerred from update
|
2023-02-07 12:03:00 +01:00
|
|
|
# row.operator("gp.layer_name_build", text='', icon='EVENT_RETURN').desc = context.scene.gptoolprops.layer_name
|
2021-07-20 18:53:39 +02:00
|
|
|
|
2022-10-09 00:28:14 +02:00
|
|
|
if len(prefs.suffixes.namespaces):
|
|
|
|
ct = 0
|
|
|
|
# can't use enumerate cause there can be hided prefix
|
|
|
|
for namespace in prefs.suffixes.namespaces:
|
|
|
|
if namespace.hide:
|
|
|
|
continue
|
|
|
|
if ct % line_limit == 0:
|
2021-07-20 18:53:39 +02:00
|
|
|
row = col.row(align=True)
|
2022-10-09 00:28:14 +02:00
|
|
|
ct += 1
|
|
|
|
op = row.operator("gp.layer_name_build", text=namespace.tag)
|
|
|
|
op.suffix = namespace.tag
|
|
|
|
op.tooltip = namespace.name
|
2023-02-07 12:03:00 +01:00
|
|
|
|
2022-10-09 00:28:14 +02:00
|
|
|
if ct > 0:
|
|
|
|
row.operator("gp.layer_name_build", text='', icon='X').suffix = 'suffixkillcode'
|
2021-06-25 18:17:51 +02:00
|
|
|
|
|
|
|
## --- UI dopesheet ---
|
|
|
|
|
|
|
|
def gpencil_dopesheet_header(self, context):
|
|
|
|
'''to append in DOPESHEET_HT_header'''
|
|
|
|
layout = self.layout
|
|
|
|
st = context.space_data
|
2024-11-11 15:35:39 +01:00
|
|
|
if st.mode != 'GREASEPENCIL':
|
2021-06-25 18:17:51 +02:00
|
|
|
return
|
|
|
|
|
|
|
|
row = layout.row(align=True)
|
|
|
|
# row.operator('gp.active_channel_color_to_selected', text='', icon='RESTRICT_COLOR_ON')
|
|
|
|
row.operator('gp.select_same_prefix', text='', icon='SYNTAX_OFF') # SORTALPHA, SMALL_CAPS
|
|
|
|
row.operator('gp.select_same_color', text='', icon='RESTRICT_COLOR_ON')
|
|
|
|
|
|
|
|
|
2021-07-13 16:23:39 +02:00
|
|
|
## --- UI context menu ---
|
|
|
|
|
|
|
|
def gpencil_layer_dropdown_menu(self, context):
|
|
|
|
'''to append in GPENCIL_MT_layer_context_menu'''
|
|
|
|
self.layout.operator('gp.rename_gp_layers', icon='BORDERMOVE')
|
|
|
|
|
2021-07-20 18:53:39 +02:00
|
|
|
## handler and msgbus
|
|
|
|
|
|
|
|
def obj_layer_name_callback():
|
|
|
|
'''assign layer name properties so user an tweak it'''
|
|
|
|
ob = bpy.context.object
|
2024-11-11 15:35:39 +01:00
|
|
|
if not ob or ob.type != 'GREASEPENCIL':
|
2021-07-20 18:53:39 +02:00
|
|
|
return
|
|
|
|
if not ob.data.layers.active:
|
|
|
|
return
|
2021-07-27 18:48:38 +02:00
|
|
|
|
2021-07-29 11:20:43 +02:00
|
|
|
## Set selection to active object ot avoid un-sync selection on Layers stack
|
|
|
|
## (happen when an objet is selected but not active with 'lock object mode')
|
2021-07-27 18:48:38 +02:00
|
|
|
for l in ob.data.layers:
|
|
|
|
l.select = l == ob.data.layers.active
|
|
|
|
|
2021-07-29 11:20:43 +02:00
|
|
|
res = re.search(PATTERN, ob.data.layers.active.info.strip())
|
2021-07-20 18:53:39 +02:00
|
|
|
if not res:
|
|
|
|
return
|
2021-07-27 18:48:38 +02:00
|
|
|
if not res.group('name'):
|
2021-07-20 18:53:39 +02:00
|
|
|
return
|
2021-07-30 18:24:54 +02:00
|
|
|
# print('grp:', res.group('grp'))
|
|
|
|
# print('tag:', res.group('tag'))
|
|
|
|
# print('name:', res.group('name'))
|
|
|
|
# print('sfix:', res.group('sfix'))
|
|
|
|
# print('inc:', res.group('inc'))
|
2021-07-27 18:48:38 +02:00
|
|
|
bpy.context.scene.gptoolprops['layer_name'] = res.group('name')
|
2021-07-20 18:53:39 +02:00
|
|
|
|
2023-01-05 12:28:35 +01:00
|
|
|
|
|
|
|
def subscribe_layer_change():
|
2021-07-20 18:53:39 +02:00
|
|
|
subscribe_to = (bpy.types.GreasePencilLayers, "active_index")
|
|
|
|
bpy.msgbus.subscribe_rna(
|
|
|
|
key=subscribe_to,
|
|
|
|
# owner of msgbus subcribe (for clearing later)
|
|
|
|
# owner=handle,
|
2021-07-30 18:24:54 +02:00
|
|
|
owner=bpy.types.GreasePencil, # <-- can attach to an ID during all it's lifetime...
|
2021-07-20 18:53:39 +02:00
|
|
|
# Args passed to callback function (tuple)
|
|
|
|
args=(),
|
|
|
|
# Callback function for property update
|
|
|
|
notify=obj_layer_name_callback,
|
|
|
|
options={'PERSISTENT'},
|
|
|
|
)
|
|
|
|
|
2023-01-05 12:28:35 +01:00
|
|
|
@persistent
|
|
|
|
def subscribe_layer_change_handler(dummy):
|
|
|
|
subscribe_layer_change()
|
2021-07-20 18:53:39 +02:00
|
|
|
|
2021-12-22 14:11:31 +01:00
|
|
|
##--- Add layers
|
|
|
|
|
|
|
|
class GPTB_PT_layer_name_ui(bpy.types.Panel):
|
|
|
|
bl_space_type = 'TOPBAR' # dummy
|
|
|
|
bl_region_type = 'HEADER'
|
|
|
|
bl_options = {'INSTANCED'}
|
|
|
|
bl_label = 'Layer Rename'
|
|
|
|
bl_ui_units_x = 14
|
|
|
|
|
|
|
|
def invoke(self, context, event):
|
|
|
|
# all_addons_l = get_modifier_list()
|
|
|
|
wm = context.window_manager
|
|
|
|
wm.invoke_props_dialog(self) # , width=600
|
|
|
|
return {'FINISHED'}
|
2023-02-07 12:03:00 +01:00
|
|
|
|
2021-12-22 14:11:31 +01:00
|
|
|
def draw(self, context):
|
|
|
|
layout = self.layout
|
|
|
|
|
|
|
|
# def row_with_icon(layout, icon):
|
|
|
|
# # Edit first editable button in popup
|
|
|
|
# row = layout.row()
|
|
|
|
# row.activate_init = True
|
|
|
|
# row.label(icon=icon)
|
|
|
|
# return row
|
|
|
|
# row = row_with_icon(layout, 'OUTLINER_DATA_GP_LAYER')
|
2023-02-07 12:03:00 +01:00
|
|
|
|
2021-12-22 14:11:31 +01:00
|
|
|
row = layout.row()
|
|
|
|
row.activate_init = True
|
|
|
|
row.label(icon='OUTLINER_DATA_GP_LAYER')
|
|
|
|
row.prop(context.object.data.layers.active, 'info', text='')
|
|
|
|
|
|
|
|
def add_layer(context):
|
|
|
|
bpy.ops.gpencil.layer_add()
|
|
|
|
context.object.data.layers.active.use_lights = False
|
|
|
|
|
|
|
|
class GPTB_OT_add_gp_layer_with_rename(Operator):
|
|
|
|
bl_idname = "gp.add_layer_rename"
|
|
|
|
bl_label = "Add Rename GPencil Layer"
|
|
|
|
bl_description = "Create a new gp layer with use light toggled off and popup a rename box"
|
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
@classmethod
|
|
|
|
def poll(cls, context):
|
2024-11-11 15:35:39 +01:00
|
|
|
return context.object and context.object.type == 'GREASEPENCIL'
|
2021-12-22 14:11:31 +01:00
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
add_layer(context)
|
|
|
|
bpy.ops.wm.call_panel(name="GPTB_PT_layer_name_ui", keep_open = False)
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
class GPTB_OT_add_gp_layer(Operator):
|
|
|
|
bl_idname = "gp.add_layer"
|
|
|
|
bl_label = "Add GPencil Layer"
|
|
|
|
bl_description = "Create a new gp layer with use light toggled off"
|
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
@classmethod
|
|
|
|
def poll(cls, context):
|
2024-11-11 15:35:39 +01:00
|
|
|
return context.object and context.object.type == 'GREASEPENCIL'
|
2021-12-22 14:11:31 +01:00
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
add_layer(context)
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
|
|
addon_keymaps = []
|
|
|
|
def register_keymaps():
|
2023-01-16 17:56:51 +01:00
|
|
|
if bpy.app.background:
|
|
|
|
return
|
2021-12-22 14:11:31 +01:00
|
|
|
addon = bpy.context.window_manager.keyconfigs.addon
|
|
|
|
|
|
|
|
##---# Insert Layers
|
|
|
|
## Insert new gp layer (with no use_light)
|
|
|
|
km = addon.keymaps.new(name = "Grease Pencil", space_type = "EMPTY") # global (only paint ?)
|
|
|
|
kmi = km.keymap_items.new('gp.add_layer', type='INSERT', value='PRESS')
|
|
|
|
addon_keymaps.append((km, kmi))
|
|
|
|
|
|
|
|
## Insert new gp layer (with no use_light and immediately pop up a box to rename)
|
|
|
|
# km = addon.keymaps.new(name = "Grease Pencil", space_type = "EMPTY") # global (only paint ?)
|
|
|
|
kmi = km.keymap_items.new('gp.add_layer_rename', type='INSERT', value='PRESS', shift=True)
|
|
|
|
addon_keymaps.append((km, kmi))
|
|
|
|
|
2023-02-07 12:03:00 +01:00
|
|
|
##---# F2 rename calls
|
2021-12-22 14:11:31 +01:00
|
|
|
## Direct rename active layer in Paint mode
|
|
|
|
km = addon.keymaps.new(name = "Grease Pencil Stroke Paint Mode", space_type = "EMPTY")
|
|
|
|
kmi = km.keymap_items.new('wm.call_panel', type='F2', value='PRESS')
|
|
|
|
kmi.properties.name = 'GPTB_PT_layer_name_ui'
|
|
|
|
kmi.properties.keep_open = False
|
|
|
|
addon_keymaps.append((km, kmi))
|
|
|
|
|
|
|
|
## Same in edit mode
|
|
|
|
km = addon.keymaps.new(name = "Grease Pencil Stroke Edit Mode", space_type = "EMPTY")
|
|
|
|
kmi = km.keymap_items.new('wm.call_panel', type='F2', value='PRESS')
|
|
|
|
kmi.properties.name = 'GPTB_PT_layer_name_ui'
|
|
|
|
kmi.properties.keep_open = False
|
|
|
|
addon_keymaps.append((km, kmi))
|
|
|
|
|
|
|
|
|
|
|
|
def unregister_keymaps():
|
2023-01-16 17:56:51 +01:00
|
|
|
if bpy.app.background:
|
|
|
|
return
|
2021-12-22 14:11:31 +01:00
|
|
|
for km, kmi in addon_keymaps:
|
|
|
|
km.keymap_items.remove(kmi)
|
|
|
|
addon_keymaps.clear()
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-06-25 18:17:51 +02:00
|
|
|
classes=(
|
|
|
|
GPTB_OT_rename_gp_layer,
|
|
|
|
GPTB_OT_layer_name_build,
|
2021-07-27 18:48:38 +02:00
|
|
|
GPTB_OT_layer_group_toggle,
|
|
|
|
GPTB_OT_layer_new_group,
|
2021-06-25 18:17:51 +02:00
|
|
|
GPTB_OT_select_set_same_prefix,
|
|
|
|
GPTB_OT_select_set_same_color,
|
2021-12-22 14:11:31 +01:00
|
|
|
|
|
|
|
## Layer add and pop-up rename
|
|
|
|
GPTB_PT_layer_name_ui, # pop-up
|
|
|
|
GPTB_OT_add_gp_layer_with_rename, # shift+Ins
|
|
|
|
GPTB_OT_add_gp_layer, # Ins
|
2021-06-25 18:17:51 +02:00
|
|
|
)
|
|
|
|
|
2021-12-22 14:11:31 +01:00
|
|
|
def register():
|
2021-06-25 18:17:51 +02:00
|
|
|
for cls in classes:
|
|
|
|
bpy.utils.register_class(cls)
|
2023-02-07 12:03:00 +01:00
|
|
|
|
2021-07-27 18:48:38 +02:00
|
|
|
bpy.types.DATA_PT_gpencil_layers.prepend(layer_name_builder_ui)
|
2021-06-25 18:17:51 +02:00
|
|
|
bpy.types.DOPESHEET_HT_header.append(gpencil_dopesheet_header)
|
2021-07-13 16:23:39 +02:00
|
|
|
bpy.types.GPENCIL_MT_layer_context_menu.append(gpencil_layer_dropdown_menu)
|
2023-01-05 12:28:35 +01:00
|
|
|
bpy.app.handlers.load_post.append(subscribe_layer_change_handler)
|
2021-12-22 14:11:31 +01:00
|
|
|
register_keymaps()
|
2021-06-25 18:17:51 +02:00
|
|
|
|
2023-01-05 12:28:35 +01:00
|
|
|
# Directly set msgbus to work at first addon activation
|
|
|
|
bpy.app.timers.register(subscribe_layer_change, first_interval=1)
|
|
|
|
|
2021-06-25 18:17:51 +02:00
|
|
|
def unregister():
|
2021-12-22 14:11:31 +01:00
|
|
|
unregister_keymaps()
|
2023-01-05 12:28:35 +01:00
|
|
|
bpy.app.handlers.load_post.remove(subscribe_layer_change_handler)
|
2021-07-13 16:23:39 +02:00
|
|
|
bpy.types.GPENCIL_MT_layer_context_menu.remove(gpencil_layer_dropdown_menu)
|
2021-06-25 18:17:51 +02:00
|
|
|
bpy.types.DOPESHEET_HT_header.remove(gpencil_dopesheet_header)
|
2021-07-27 18:48:38 +02:00
|
|
|
bpy.types.DATA_PT_gpencil_layers.remove(layer_name_builder_ui)
|
2023-02-07 12:03:00 +01:00
|
|
|
|
2021-06-25 18:17:51 +02:00
|
|
|
for cls in reversed(classes):
|
2021-07-20 18:53:39 +02:00
|
|
|
bpy.utils.unregister_class(cls)
|
2023-02-07 12:03:00 +01:00
|
|
|
|
2021-07-20 18:53:39 +02:00
|
|
|
# delete layer index trigger
|
|
|
|
bpy.msgbus.clear_by_owner(bpy.types.GreasePencil)
|