Listing the "Blender Foundation" as copyright holder implied the Blender Foundation holds copyright to files which may include work from many developers. While keeping copyright on headers makes sense for isolated libraries, Blender's own code may be refactored or moved between files in a way that makes the per file copyright holders less meaningful. Copyright references to the "Blender Foundation" have been replaced with "Blender Authors", with the exception of `./extern/` since these this contains libraries which are more isolated, any changed to license headers there can be handled on a case-by-case basis. Some directories in `./intern/` have also been excluded: - `./intern/cycles/` it's own `AUTHORS` file is planned. - `./intern/opensubdiv/`. An "AUTHORS" file has been added, using the chromium projects authors file as a template. Design task: #110784 Ref !110783.
484 lines
13 KiB
Python
484 lines
13 KiB
Python
# SPDX-FileCopyrightText: 2009-2023 Blender Authors
|
|
#
|
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
import bpy
|
|
from bpy.types import Header, Menu, Panel
|
|
from bpy.app.translations import (
|
|
contexts as i18n_contexts,
|
|
pgettext_iface as iface_,
|
|
)
|
|
|
|
|
|
class TEXT_HT_header(Header):
|
|
bl_space_type = 'TEXT_EDITOR'
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
st = context.space_data
|
|
text = st.text
|
|
is_syntax_highlight_supported = st.is_syntax_highlight_supported()
|
|
layout.template_header()
|
|
|
|
TEXT_MT_editor_menus.draw_collapsible(context, layout)
|
|
|
|
layout.separator_spacer()
|
|
|
|
if text and text.is_modified:
|
|
row = layout.row(align=True)
|
|
row.alert = True
|
|
row.operator("text.resolve_conflict", text="", icon='QUESTION')
|
|
|
|
row = layout.row(align=True)
|
|
row.template_ID(st, "text", new="text.new",
|
|
unlink="text.unlink", open="text.open")
|
|
|
|
if text:
|
|
is_osl = text.name.endswith((".osl", ".osl"))
|
|
if is_osl:
|
|
row.operator("node.shader_script_update",
|
|
text="", icon='FILE_REFRESH')
|
|
else:
|
|
row = layout.row()
|
|
row.active = is_syntax_highlight_supported
|
|
row.operator("text.run_script", text="", icon='PLAY')
|
|
|
|
layout.separator_spacer()
|
|
|
|
row = layout.row(align=True)
|
|
row.prop(st, "show_line_numbers", text="")
|
|
row.prop(st, "show_word_wrap", text="")
|
|
|
|
syntax = row.row(align=True)
|
|
syntax.active = is_syntax_highlight_supported
|
|
syntax.prop(st, "show_syntax_highlight", text="")
|
|
|
|
|
|
class TEXT_HT_footer(Header):
|
|
bl_space_type = 'TEXT_EDITOR'
|
|
bl_region_type = 'FOOTER'
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
st = context.space_data
|
|
text = st.text
|
|
if text:
|
|
row = layout.row()
|
|
if text.filepath:
|
|
if text.is_dirty:
|
|
row.label(
|
|
text=iface_("File: *%s (unsaved)") % text.filepath,
|
|
translate=False,
|
|
)
|
|
else:
|
|
row.label(
|
|
text=iface_("File: %s") % text.filepath,
|
|
translate=False,
|
|
)
|
|
else:
|
|
row.label(
|
|
text=iface_("Text: External")
|
|
if text.library
|
|
else iface_("Text: Internal"),
|
|
translate=False
|
|
)
|
|
|
|
|
|
class TEXT_MT_editor_menus(Menu):
|
|
bl_idname = "TEXT_MT_editor_menus"
|
|
bl_label = ""
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
st = context.space_data
|
|
text = st.text
|
|
|
|
layout.menu("TEXT_MT_view")
|
|
layout.menu("TEXT_MT_text")
|
|
|
|
if text:
|
|
layout.menu("TEXT_MT_edit")
|
|
layout.menu("TEXT_MT_select")
|
|
layout.menu("TEXT_MT_format")
|
|
|
|
layout.menu("TEXT_MT_templates")
|
|
|
|
|
|
class TEXT_PT_properties(Panel):
|
|
bl_space_type = 'TEXT_EDITOR'
|
|
bl_region_type = 'UI'
|
|
bl_category = "Text"
|
|
bl_label = "Properties"
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
layout.use_property_decorate = False
|
|
st = context.space_data
|
|
|
|
if not st.text:
|
|
layout.active = False
|
|
|
|
st = context.space_data
|
|
|
|
col = layout.column(align=False, heading="Margin")
|
|
row = col.row(align=True)
|
|
sub = row.row(align=True)
|
|
sub.prop(st, "show_margin", text="")
|
|
sub = sub.row(align=True)
|
|
sub.active = st.show_margin
|
|
sub.prop(st, "margin_column", text="")
|
|
|
|
layout.prop(st, "font_size")
|
|
layout.prop(st, "tab_width")
|
|
|
|
text = st.text
|
|
if text:
|
|
layout.prop(text, "indentation")
|
|
|
|
|
|
class TEXT_PT_find(Panel):
|
|
bl_space_type = 'TEXT_EDITOR'
|
|
bl_region_type = 'UI'
|
|
bl_category = "Text"
|
|
bl_label = "Find & Replace"
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
st = context.space_data
|
|
|
|
# find
|
|
col = layout.column()
|
|
row = col.row(align=True)
|
|
row.prop(st, "find_text", icon='VIEWZOOM', text="")
|
|
row.operator("text.find_set_selected", text="", icon='EYEDROPPER')
|
|
col.operator("text.find")
|
|
|
|
layout.separator()
|
|
|
|
# replace
|
|
col = layout.column()
|
|
row = col.row(align=True)
|
|
row.prop(st, "replace_text", icon='DECORATE_OVERRIDE', text="")
|
|
row.operator("text.replace_set_selected", text="", icon='EYEDROPPER')
|
|
|
|
row = col.row(align=True)
|
|
row.operator("text.replace")
|
|
row.operator("text.replace", text="Replace All").all = True
|
|
|
|
layout.separator()
|
|
|
|
# settings
|
|
row = layout.row(align=True)
|
|
if not st.text:
|
|
row.active = False
|
|
row.prop(st, "use_match_case", text="Case",
|
|
text_ctxt=i18n_contexts.id_text, toggle=True)
|
|
row.prop(st, "use_find_wrap", text="Wrap",
|
|
text_ctxt=i18n_contexts.id_text, toggle=True)
|
|
row.prop(st, "use_find_all", text="All", toggle=True)
|
|
|
|
|
|
class TEXT_MT_view_navigation(Menu):
|
|
bl_label = "Navigation"
|
|
|
|
def draw(self, _context):
|
|
layout = self.layout
|
|
|
|
layout.operator("text.move", text="Top").type = 'FILE_TOP'
|
|
layout.operator("text.move", text="Bottom").type = 'FILE_BOTTOM'
|
|
|
|
layout.separator()
|
|
|
|
layout.operator("text.move", text="Line Begin").type = 'LINE_BEGIN'
|
|
layout.operator("text.move", text="Line End").type = 'LINE_END'
|
|
|
|
layout.separator()
|
|
|
|
layout.operator("text.move", text="Previous Line").type = 'PREVIOUS_LINE'
|
|
layout.operator("text.move", text="Next Line").type = 'NEXT_LINE'
|
|
|
|
layout.separator()
|
|
|
|
layout.operator("text.move", text="Previous Word").type = 'PREVIOUS_WORD'
|
|
layout.operator("text.move", text="Next Word").type = 'NEXT_WORD'
|
|
|
|
|
|
class TEXT_MT_view(Menu):
|
|
bl_label = "View"
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
st = context.space_data
|
|
|
|
layout.prop(st, "show_region_ui")
|
|
|
|
layout.separator()
|
|
|
|
layout.prop(st, "show_line_numbers")
|
|
layout.prop(st, "show_word_wrap")
|
|
syntax = layout.column()
|
|
syntax.active = st.is_syntax_highlight_supported()
|
|
syntax.prop(st, "show_syntax_highlight")
|
|
layout.prop(st, "show_line_highlight")
|
|
|
|
layout.separator()
|
|
|
|
props = layout.operator("wm.context_cycle_int", text="Zoom In")
|
|
props.data_path = "space_data.font_size"
|
|
props.reverse = False
|
|
|
|
props = layout.operator("wm.context_cycle_int", text="Zoom Out")
|
|
props.data_path = "space_data.font_size"
|
|
props.reverse = True
|
|
|
|
layout.separator()
|
|
|
|
layout.menu("TEXT_MT_view_navigation")
|
|
|
|
layout.separator()
|
|
|
|
layout.menu("INFO_MT_area")
|
|
|
|
|
|
class TEXT_MT_text(Menu):
|
|
bl_label = "Text"
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
st = context.space_data
|
|
text = st.text
|
|
|
|
layout.operator("text.new", text="New",
|
|
text_ctxt=i18n_contexts.id_text, icon='FILE_NEW')
|
|
layout.operator("text.open", text="Open...", icon='FILE_FOLDER')
|
|
|
|
if text:
|
|
layout.separator()
|
|
row = layout.row()
|
|
row.operator("text.reload")
|
|
row.enabled = not text.is_in_memory
|
|
|
|
row = layout.row()
|
|
row.operator("text.jump_to_file_at_point", text="Edit Externally")
|
|
row.enabled = (not text.is_in_memory and context.preferences.filepaths.text_editor != "")
|
|
|
|
layout.separator()
|
|
layout.operator("text.save", icon='FILE_TICK')
|
|
layout.operator("text.save_as", text="Save As...")
|
|
|
|
if text.filepath:
|
|
layout.separator()
|
|
layout.operator("text.make_internal")
|
|
|
|
layout.separator()
|
|
layout.prop(text, "use_module")
|
|
|
|
layout.prop(st, "use_live_edit")
|
|
|
|
layout.separator()
|
|
layout.operator("text.run_script")
|
|
|
|
|
|
class TEXT_MT_templates_py(Menu):
|
|
bl_label = "Python"
|
|
|
|
def draw(self, context):
|
|
prefs = context.preferences
|
|
use_asset_shelf = prefs.experimental.use_asset_shelf
|
|
|
|
self.path_menu(
|
|
bpy.utils.script_paths(subdir="templates_py"),
|
|
"text.open",
|
|
props_default={"internal": True},
|
|
filter_ext=lambda ext: (ext.lower() == ".py"),
|
|
# Filter out asset shelf template depending on experimental "Asset Shelf" option.
|
|
filter_path=lambda path, use_asset_shelf=use_asset_shelf:
|
|
(use_asset_shelf or not path.endswith("ui_asset_shelf.py")),
|
|
)
|
|
|
|
|
|
class TEXT_MT_templates_osl(Menu):
|
|
bl_label = "Open Shading Language"
|
|
|
|
def draw(self, _context):
|
|
self.path_menu(
|
|
bpy.utils.script_paths(subdir="templates_osl"),
|
|
"text.open",
|
|
props_default={"internal": True},
|
|
filter_ext=lambda ext: (ext.lower() == ".osl"),
|
|
)
|
|
|
|
|
|
class TEXT_MT_templates(Menu):
|
|
bl_label = "Templates"
|
|
|
|
def draw(self, _context):
|
|
layout = self.layout
|
|
layout.menu("TEXT_MT_templates_py")
|
|
layout.menu("TEXT_MT_templates_osl")
|
|
|
|
|
|
class TEXT_MT_select(Menu):
|
|
bl_label = "Select"
|
|
|
|
def draw(self, _context):
|
|
layout = self.layout
|
|
|
|
layout.operator("text.select_all", text="All")
|
|
layout.operator("text.select_line", text="Line")
|
|
layout.operator("text.select_word", text="Word")
|
|
|
|
layout.separator()
|
|
|
|
layout.operator("text.move_select", text="Top").type = 'FILE_TOP'
|
|
layout.operator("text.move_select", text="Bottom").type = 'FILE_BOTTOM'
|
|
|
|
layout.separator()
|
|
|
|
layout.operator("text.move_select", text="Line Begin").type = 'LINE_BEGIN'
|
|
layout.operator("text.move_select", text="Line End").type = 'LINE_END'
|
|
|
|
layout.separator()
|
|
|
|
layout.operator("text.move_select", text="Previous Line").type = 'PREVIOUS_LINE'
|
|
layout.operator("text.move_select", text="Next Line").type = 'NEXT_LINE'
|
|
|
|
layout.separator()
|
|
|
|
layout.operator("text.move_select", text="Previous Word").type = 'PREVIOUS_WORD'
|
|
layout.operator("text.move_select", text="Next Word").type = 'NEXT_WORD'
|
|
|
|
|
|
class TEXT_MT_format(Menu):
|
|
bl_label = "Format"
|
|
|
|
def draw(self, _context):
|
|
layout = self.layout
|
|
|
|
layout.operator("text.indent")
|
|
layout.operator("text.unindent")
|
|
|
|
layout.separator()
|
|
|
|
layout.operator("text.comment_toggle")
|
|
|
|
layout.separator()
|
|
|
|
layout.operator_menu_enum("text.convert_whitespace", "type")
|
|
|
|
|
|
class TEXT_MT_edit_to3d(Menu):
|
|
bl_label = "Text to 3D Object"
|
|
|
|
def draw(self, _context):
|
|
layout = self.layout
|
|
|
|
layout.operator("text.to_3d_object",
|
|
text="One Object",
|
|
).split_lines = False
|
|
layout.operator("text.to_3d_object",
|
|
text="One Object Per Line",
|
|
).split_lines = True
|
|
|
|
|
|
class TEXT_MT_edit(Menu):
|
|
bl_label = "Edit"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return context.space_data.text is not None
|
|
|
|
def draw(self, _context):
|
|
layout = self.layout
|
|
|
|
layout.operator("ed.undo")
|
|
layout.operator("ed.redo")
|
|
|
|
layout.separator()
|
|
|
|
layout.operator("text.cut")
|
|
layout.operator("text.copy", icon='COPYDOWN')
|
|
layout.operator("text.paste", icon='PASTEDOWN')
|
|
layout.operator("text.duplicate_line")
|
|
|
|
layout.separator()
|
|
|
|
layout.operator("text.move_lines", text="Move Line(s) Up").direction = 'UP'
|
|
layout.operator("text.move_lines", text="Move Line(s) Down").direction = 'DOWN'
|
|
|
|
layout.separator()
|
|
|
|
layout.operator("text.start_find", text="Find & Replace...")
|
|
layout.operator("text.find_set_selected")
|
|
layout.operator("text.jump", text="Jump To...")
|
|
|
|
layout.separator()
|
|
|
|
layout.operator("text.autocomplete")
|
|
|
|
layout.separator()
|
|
|
|
layout.menu("TEXT_MT_edit_to3d")
|
|
|
|
|
|
class TEXT_MT_context_menu(Menu):
|
|
bl_label = ""
|
|
|
|
def draw(self, _context):
|
|
layout = self.layout
|
|
|
|
layout.operator_context = 'INVOKE_DEFAULT'
|
|
|
|
layout.operator("text.cut")
|
|
layout.operator("text.copy", icon='COPYDOWN')
|
|
layout.operator("text.paste", icon='PASTEDOWN')
|
|
layout.operator("text.duplicate_line")
|
|
|
|
layout.separator()
|
|
|
|
layout.operator("text.move_lines", text="Move Line(s) Up").direction = 'UP'
|
|
layout.operator("text.move_lines", text="Move Line(s) Down").direction = 'DOWN'
|
|
|
|
layout.separator()
|
|
|
|
layout.operator("text.indent")
|
|
layout.operator("text.unindent")
|
|
|
|
layout.separator()
|
|
|
|
layout.operator("text.comment_toggle")
|
|
|
|
layout.separator()
|
|
|
|
layout.operator("text.autocomplete")
|
|
|
|
|
|
classes = (
|
|
TEXT_HT_header,
|
|
TEXT_HT_footer,
|
|
TEXT_MT_edit,
|
|
TEXT_MT_editor_menus,
|
|
TEXT_PT_find,
|
|
TEXT_PT_properties,
|
|
TEXT_MT_view,
|
|
TEXT_MT_view_navigation,
|
|
TEXT_MT_text,
|
|
TEXT_MT_templates,
|
|
TEXT_MT_templates_py,
|
|
TEXT_MT_templates_osl,
|
|
TEXT_MT_select,
|
|
TEXT_MT_format,
|
|
TEXT_MT_edit_to3d,
|
|
TEXT_MT_context_menu,
|
|
)
|
|
|
|
if __name__ == "__main__": # only for live edit.
|
|
from bpy.utils import register_class
|
|
for cls in classes:
|
|
register_class(cls)
|