2021-07-20 18:53:39 +02:00
|
|
|
from os import error
|
2021-06-25 18:17:51 +02:00
|
|
|
import bpy
|
|
|
|
import re
|
|
|
|
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-06-25 18:17:51 +02:00
|
|
|
from .utils import get_addon_prefs
|
|
|
|
|
|
|
|
|
|
|
|
# --- 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=''):
|
|
|
|
'''GET a layer and argumen to build and assign name'''
|
|
|
|
|
|
|
|
|
|
|
|
prefs = get_addon_prefs()
|
|
|
|
sep = prefs.separator
|
|
|
|
name = layer.info
|
|
|
|
|
|
|
|
pattern = PATTERN.replace('_', sep) # set separator
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
"""
|
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
|
2021-07-27 18:48:38 +02:00
|
|
|
|
2021-06-25 18:17:51 +02:00
|
|
|
pattern = pattern.replace('_', sep) # set separator
|
|
|
|
|
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
|
|
|
|
|
2021-06-25 18:17:51 +02:00
|
|
|
if desc:
|
|
|
|
p3 = desc
|
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
|
|
|
|
|
|
|
|
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'})
|
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-06-25 18:17:51 +02:00
|
|
|
for l in gpl:
|
2021-07-20 18:53:39 +02:00
|
|
|
if l.select or l == act:
|
2021-07-27 18:48:38 +02:00
|
|
|
layer_name_build(l, prefix=self.prefix, desc=self.desc, suffix=self.suffix)
|
|
|
|
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(' -')
|
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)
|
2021-07-29 11:20:43 +02: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
|
|
|
|
|
|
|
|
# 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"}
|
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"}
|
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()
|
|
|
|
|
|
|
|
|
|
|
|
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):
|
|
|
|
return context.object and context.object.type == 'GPENCIL'
|
|
|
|
|
|
|
|
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 # '_'
|
|
|
|
gpl = context.object.data.layers
|
|
|
|
act = gpl.active
|
|
|
|
|
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:
|
|
|
|
# self.report({'ERROR'}, f'Error scanning {act.info}')
|
|
|
|
return {"CANCELLED"}
|
|
|
|
namespace = res.group(1)
|
|
|
|
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]
|
|
|
|
|
|
|
|
## with reg
|
|
|
|
namespace_bool_list = [l.info.split(sep,1)[0] + sep == namespace for l in gpl]
|
|
|
|
gpl.foreach_set('select', namespace_bool_list)
|
|
|
|
|
|
|
|
elif self.mode == 'SET':
|
|
|
|
for l in gpl:
|
|
|
|
if not l.select or l == act:
|
|
|
|
continue
|
|
|
|
layer_name_build(l, prefix=namespace.strip(sep))
|
|
|
|
|
|
|
|
refresh_areas()
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
|
|
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):
|
|
|
|
return context.object and context.object.type == 'GPENCIL'
|
|
|
|
|
|
|
|
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):
|
|
|
|
gpl = context.object.data.layers
|
|
|
|
act = gpl.active
|
|
|
|
|
|
|
|
if self.mode == 'SELECT':
|
|
|
|
same_color_bool = [l.channel_color == act.channel_color for l in gpl]
|
|
|
|
gpl.foreach_set('select', same_color_bool)
|
|
|
|
|
|
|
|
elif self.mode == 'SET':
|
|
|
|
activate_channel_group_color(context)
|
|
|
|
for l in gpl:
|
|
|
|
if not l.select or l == act:
|
|
|
|
continue
|
|
|
|
l.channel_color = act.channel_color
|
|
|
|
|
|
|
|
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:
|
|
|
|
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):
|
|
|
|
return context.object and context.object.type == 'GPENCIL'
|
|
|
|
|
|
|
|
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")
|
|
|
|
|
|
|
|
row_b= row.row()
|
|
|
|
row_b.prop(self, "prefix")
|
|
|
|
row_c= row.row()
|
|
|
|
|
|
|
|
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'''
|
|
|
|
|
|
|
|
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
|
2021-07-20 18:53:39 +02:00
|
|
|
if not prefs.prefixes and not prefs.suffixes:
|
|
|
|
return
|
|
|
|
|
2021-06-25 18:17:51 +02:00
|
|
|
layout = self.layout
|
|
|
|
# {'EDIT_GPENCIL', 'PAINT_GPENCIL','SCULPT_GPENCIL','WEIGHT_GPENCIL', 'VERTEX_GPENCIL'}
|
|
|
|
# layout.separator()
|
|
|
|
col = layout.column()
|
|
|
|
|
|
|
|
all_prefixes = prefs.prefixes.split(',')
|
2021-07-20 18:53:39 +02:00
|
|
|
all_suffixes = prefs.suffixes.split(',')
|
2021-06-25 18:17:51 +02:00
|
|
|
line_limit = 8
|
|
|
|
|
2021-07-20 18:53:39 +02:00
|
|
|
if prefs.prefixes:
|
|
|
|
## first prefix
|
|
|
|
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'
|
2021-06-25 18:17:51 +02:00
|
|
|
|
2021-07-20 18:53:39 +02:00
|
|
|
## secondary prefix ?
|
2021-06-25 18:17:51 +02:00
|
|
|
|
2021-07-20 18:53:39 +02:00
|
|
|
## name (description)
|
2021-06-25 18:17:51 +02:00
|
|
|
row = col.row(align=True)
|
|
|
|
row.prop(context.scene.gptoolprops, 'layer_name', text='')
|
2021-07-27 18:48:38 +02:00
|
|
|
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
|
|
|
|
# row.operator("gp.layer_name_build", text='', icon='EVENT_RETURN').desc = context.scene.gptoolprops.layer_name
|
|
|
|
|
|
|
|
if prefs.suffixes:
|
|
|
|
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'
|
2021-07-27 18:48:38 +02:00
|
|
|
|
|
|
|
|
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
|
|
|
|
if st.mode != 'GPENCIL':
|
|
|
|
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
|
|
|
|
if not ob or ob.type != 'GPENCIL':
|
|
|
|
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-29 11:20:43 +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
|
|
|
|
|
|
|
@persistent
|
|
|
|
def subscribe_handler(dummy):
|
|
|
|
subscribe_to = (bpy.types.GreasePencilLayers, "active_index")
|
|
|
|
bpy.msgbus.subscribe_rna(
|
|
|
|
key=subscribe_to,
|
|
|
|
# owner of msgbus subcribe (for clearing later)
|
|
|
|
# owner=handle,
|
|
|
|
owner=bpy.types.GreasePencil, # <-- can atach to an ID during all it's lifetime...
|
|
|
|
# Args passed to callback function (tuple)
|
|
|
|
args=(),
|
|
|
|
# Callback function for property update
|
|
|
|
notify=obj_layer_name_callback,
|
|
|
|
options={'PERSISTENT'},
|
|
|
|
)
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
)
|
|
|
|
|
|
|
|
def register():
|
|
|
|
for cls in classes:
|
|
|
|
bpy.utils.register_class(cls)
|
|
|
|
|
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)
|
2021-07-20 18:53:39 +02:00
|
|
|
bpy.app.handlers.load_post.append(subscribe_handler) # need to restart after first activation
|
2021-06-25 18:17:51 +02:00
|
|
|
|
|
|
|
def unregister():
|
2021-07-20 18:53:39 +02:00
|
|
|
bpy.app.handlers.load_post.remove(subscribe_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)
|
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)
|
|
|
|
|
|
|
|
# delete layer index trigger
|
|
|
|
bpy.msgbus.clear_by_owner(bpy.types.GreasePencil)
|