Text Editor (GSOC 2008)

=======================
Merge of branch soc-2008-quorn to trunk:
Merged 14970:16308 to trunk@16307, updated to HEAD.
Merged 16318

Main features from this branch:
 - Python text plugins
 - Suggestions and documentation elements
 - Improved syntax highlighting
 - Word wrap
 - Additional editing tools
 - Various undo and clipboard fixes
 - File header info and modification checks
This commit is contained in:
Ian Thompson 2008-09-01 14:04:22 +00:00
commit aa4e4da8c3
38 changed files with 7572 additions and 3050 deletions

@ -917,8 +917,12 @@ GHOST_TUns8* GHOST_SystemWin32::getClipboard(int flag) const
char *buffer;
char *temp_buff;
if ( OpenClipboard(NULL) ) {
if ( IsClipboardFormatAvailable(CF_TEXT) && OpenClipboard(NULL) ) {
HANDLE hData = GetClipboardData( CF_TEXT );
if (hData == NULL) {
CloseClipboard();
return NULL;
}
buffer = (char*)GlobalLock( hData );
temp_buff = (char*) malloc(strlen(buffer)+1);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 66 KiB

@ -0,0 +1,814 @@
"""The BPyTextPlugin Module
Use get_cached_descriptor(txt) to retrieve information about the script held in
the txt Text object.
Use print_cache_for(txt) to print the information to the console.
Use line, cursor = current_line(txt) to get the logical line and cursor position
Use get_targets(line, cursor) to find out what precedes the cursor:
aaa.bbb.cc|c.ddd -> ['aaa', 'bbb', 'cc']
Use resolve_targets(txt, targets) to turn a target list into a usable object if
one is found to match.
"""
import bpy, sys, os
import __builtin__, tokenize
from Blender.sys import time
from tokenize import generate_tokens, TokenError, \
COMMENT, DEDENT, INDENT, NAME, NEWLINE, NL, STRING, NUMBER
class Definition():
"""Describes a definition or defined object through its name, line number
and docstring. This is the base class for definition based descriptors.
"""
def __init__(self, name, lineno, doc=''):
self.name = name
self.lineno = lineno
self.doc = doc
class ScriptDesc():
"""Describes a script through lists of further descriptor objects (classes,
defs, vars) and dictionaries to built-in types (imports). If a script has
not been fully parsed, its incomplete flag will be set. The time of the last
parse is held by the time field and the name of the text object from which
it was parsed, the name field.
"""
def __init__(self, name, imports, classes, defs, vars, incomplete=False):
self.name = name
self.imports = imports
self.classes = classes
self.defs = defs
self.vars = vars
self.incomplete = incomplete
self.parse_due = 0
def set_delay(self, delay):
self.parse_due = time() + delay
class ClassDesc(Definition):
"""Describes a class through lists of further descriptor objects (defs and
vars). The name of the class is held by the name field and the line on
which it is defined is held in lineno.
"""
def __init__(self, name, parents, defs, vars, lineno, doc=''):
Definition.__init__(self, name, lineno, doc)
self.parents = parents
self.defs = defs
self.vars = vars
class FunctionDesc(Definition):
"""Describes a function through its name and list of parameters (name,
params) and the line on which it is defined (lineno).
"""
def __init__(self, name, params, lineno, doc=''):
Definition.__init__(self, name, lineno, doc)
self.params = params
class VarDesc(Definition):
"""Describes a variable through its name and type (if ascertainable) and the
line on which it is defined (lineno). If no type can be determined, type
will equal None.
"""
def __init__(self, name, type, lineno):
Definition.__init__(self, name, lineno)
self.type = type # None for unknown (supports: dict/list/str)
# Context types
CTX_UNSET = -1
CTX_NORMAL = 0
CTX_SINGLE_QUOTE = 1
CTX_DOUBLE_QUOTE = 2
CTX_COMMENT = 3
# Python keywords
KEYWORDS = ['and', 'del', 'from', 'not', 'while', 'as', 'elif', 'global',
'or', 'with', 'assert', 'else', 'if', 'pass', 'yield',
'break', 'except', 'import', 'print', 'class', 'exec', 'in',
'raise', 'continue', 'finally', 'is', 'return', 'def', 'for',
'lambda', 'try' ]
# Module file extensions
MODULE_EXTS = ['.py', '.pyc', '.pyo', '.pyw', '.pyd']
ModuleType = type(__builtin__)
NoneScriptDesc = ScriptDesc('', dict(), dict(), dict(), dict(), True)
_modules = {}
_modules_updated = 0
_parse_cache = dict()
def _load_module_names():
"""Searches the sys.path for module files and lists them, along with
sys.builtin_module_names, in the global dict _modules.
"""
global _modules
for n in sys.builtin_module_names:
_modules[n] = None
for p in sys.path:
if p == '': p = os.curdir
if not os.path.isdir(p): continue
for f in os.listdir(p):
for ext in MODULE_EXTS:
if f.endswith(ext):
_modules[f[:-len(ext)]] = None
break
_load_module_names()
def _trim_doc(doc):
"""Trims the quotes from a quoted STRING token (eg. "'''text'''" -> "text")
"""
l = len(doc)
i = 0
while i < l/2 and (doc[i] == "'" or doc[i] == '"'):
i += 1
return doc[i:-i]
def resolve_targets(txt, targets):
"""Attempts to return a useful object for the locally or externally defined
entity described by targets. If the object is local (defined in txt), a
Definition instance is returned. If the object is external (imported or
built in), the object itself is returned. If no object can be found, None is
returned.
"""
count = len(targets)
if count==0: return None
obj = None
local = None
i = 1
desc = get_cached_descriptor(txt)
b = targets[0].find('(')
if b==-1: b = None # Trick to let us use [:b] and get the whole string
if desc.classes.has_key(targets[0][:b]):
local = desc.classes[targets[0][:b]]
elif desc.defs.has_key(targets[0]):
local = desc.defs[targets[0]]
elif desc.vars.has_key(targets[0]):
obj = desc.vars[targets[0]].type
if local:
while i < count:
b = targets[i].find('(')
if b==-1: b = None
if hasattr(local, 'classes') and local.classes.has_key(targets[i][:b]):
local = local.classes[targets[i][:b]]
elif hasattr(local, 'defs') and local.defs.has_key(targets[i]):
local = local.defs[targets[i]]
elif hasattr(local, 'vars') and local.vars.has_key(targets[i]):
obj = local.vars[targets[i]].type
local = None
i += 1
break
else:
local = None
break
i += 1
if local: return local
if not obj:
if desc.imports.has_key(targets[0]):
obj = desc.imports[targets[0]]
else:
builtins = get_builtins()
if builtins.has_key(targets[0]):
obj = builtins[targets[0]]
while obj and i < count:
if hasattr(obj, targets[i]):
obj = getattr(obj, targets[i])
else:
obj = None
break
i += 1
return obj
def get_cached_descriptor(txt, force_parse=0):
"""Returns the cached ScriptDesc for the specified Text object 'txt'. If the
script has not been parsed in the last 'period' seconds it will be reparsed
to obtain this descriptor.
Specifying TP_AUTO for the period (default) will choose a period based on the
size of the Text object. Larger texts are parsed less often.
"""
global _parse_cache
parse = True
key = hash(txt)
if not force_parse and _parse_cache.has_key(key):
desc = _parse_cache[key]
if desc.parse_due > time():
parse = desc.incomplete
if parse:
desc = parse_text(txt)
return desc
def parse_text(txt):
"""Parses an entire script's text and returns a ScriptDesc instance
containing information about the script.
If the text is not a valid Python script (for example if brackets are left
open), parsing may fail to complete. However, if this occurs, no exception
is thrown. Instead the returned ScriptDesc instance will have its incomplete
flag set and information processed up to this point will still be accessible.
"""
start_time = time()
txt.reset()
tokens = generate_tokens(txt.readline) # Throws TokenError
curl, cursor = txt.getCursorPos()
linen = curl + 1 # Token line numbers are one-based
imports = dict()
imp_step = 0
classes = dict()
cls_step = 0
defs = dict()
def_step = 0
vars = dict()
var1_step = 0
var2_step = 0
var3_step = 0
var_accum = dict()
var_forflag = False
indent = 0
prev_type = -1
prev_text = ''
incomplete = False
while True:
try:
type, text, start, end, line = tokens.next()
except StopIteration:
break
except (TokenError, IndentationError):
incomplete = True
break
# Skip all comments and line joining characters
if type == COMMENT or type == NL:
continue
#################
## Indentation ##
#################
if type == INDENT:
indent += 1
elif type == DEDENT:
indent -= 1
#########################
## Module importing... ##
#########################
imp_store = False
# Default, look for 'from' or 'import' to start
if imp_step == 0:
if text == 'from':
imp_tmp = []
imp_step = 1
elif text == 'import':
imp_from = None
imp_tmp = []
imp_step = 2
# Found a 'from', create imp_from in form '???.???...'
elif imp_step == 1:
if text == 'import':
imp_from = '.'.join(imp_tmp)
imp_tmp = []
imp_step = 2
elif type == NAME:
imp_tmp.append(text)
elif text != '.':
imp_step = 0 # Invalid syntax
# Found 'import', imp_from is populated or None, create imp_name
elif imp_step == 2:
if text == 'as':
imp_name = '.'.join(imp_tmp)
imp_step = 3
elif type == NAME or text == '*':
imp_tmp.append(text)
elif text != '.':
imp_name = '.'.join(imp_tmp)
imp_symb = imp_name
imp_store = True
# Found 'as', change imp_symb to this value and go back to step 2
elif imp_step == 3:
if type == NAME:
imp_symb = text
else:
imp_store = True
# Both imp_name and imp_symb have now been populated so we can import
if imp_store:
# Handle special case of 'import *'
if imp_name == '*':
parent = get_module(imp_from)
imports.update(parent.__dict__)
else:
# Try importing the name as a module
try:
if imp_from:
module = get_module(imp_from +'.'+ imp_name)
else:
module = get_module(imp_name)
except (ImportError, ValueError, AttributeError, TypeError):
# Try importing name as an attribute of the parent
try:
module = __import__(imp_from, globals(), locals(), [imp_name])
imports[imp_symb] = getattr(module, imp_name)
except (ImportError, ValueError, AttributeError, TypeError):
pass
else:
imports[imp_symb] = module
# More to import from the same module?
if text == ',':
imp_tmp = []
imp_step = 2
else:
imp_step = 0
###################
## Class parsing ##
###################
# If we are inside a class then def and variable parsing should be done
# for the class. Otherwise the definitions are considered global
# Look for 'class'
if cls_step == 0:
if text == 'class':
cls_name = None
cls_lineno = start[0]
cls_indent = indent
cls_step = 1
# Found 'class', look for cls_name followed by '(' parents ')'
elif cls_step == 1:
if not cls_name:
if type == NAME:
cls_name = text
cls_sline = False
cls_parents = dict()
cls_defs = dict()
cls_vars = dict()
elif type == NAME:
if classes.has_key(text):
parent = classes[text]
cls_parents[text] = parent
cls_defs.update(parent.defs)
cls_vars.update(parent.vars)
elif text == ':':
cls_step = 2
# Found 'class' name ... ':', now check if it's a single line statement
elif cls_step == 2:
if type == NEWLINE:
cls_sline = False
else:
cls_sline = True
cls_doc = ''
cls_step = 3
elif cls_step == 3:
if not cls_doc and type == STRING:
cls_doc = _trim_doc(text)
if cls_sline:
if type == NEWLINE:
classes[cls_name] = ClassDesc(cls_name, cls_parents, cls_defs, cls_vars, cls_lineno, cls_doc)
cls_step = 0
else:
if type == DEDENT and indent <= cls_indent:
classes[cls_name] = ClassDesc(cls_name, cls_parents, cls_defs, cls_vars, cls_lineno, cls_doc)
cls_step = 0
#################
## Def parsing ##
#################
# Look for 'def'
if def_step == 0:
if text == 'def':
def_name = None
def_lineno = start[0]
def_step = 1
# Found 'def', look for def_name followed by '('
elif def_step == 1:
if type == NAME:
def_name = text
def_params = []
elif def_name and text == '(':
def_step = 2
# Found 'def' name '(', now identify the parameters upto ')'
# TODO: Handle ellipsis '...'
elif def_step == 2:
if type == NAME:
def_params.append(text)
elif text == ':':
def_step = 3
# Found 'def' ... ':', now check if it's a single line statement
elif def_step == 3:
if type == NEWLINE:
def_sline = False
else:
def_sline = True
def_doc = ''
def_step = 4
elif def_step == 4:
if type == STRING:
def_doc = _trim_doc(text)
newdef = None
if def_sline:
if type == NEWLINE:
newdef = FunctionDesc(def_name, def_params, def_lineno, def_doc)
else:
if type == NAME:
newdef = FunctionDesc(def_name, def_params, def_lineno, def_doc)
if newdef:
if cls_step > 0: # Parsing a class
cls_defs[def_name] = newdef
else:
defs[def_name] = newdef
def_step = 0
##########################
## Variable assignation ##
##########################
if cls_step > 0: # Parsing a class
# Look for 'self.???'
if var1_step == 0:
if text == 'self':
var1_step = 1
elif var1_step == 1:
if text == '.':
var_name = None
var1_step = 2
else:
var1_step = 0
elif var1_step == 2:
if type == NAME:
var_name = text
if cls_vars.has_key(var_name):
var_step = 0
else:
var1_step = 3
elif var1_step == 3:
if text == '=':
var1_step = 4
elif text != ',':
var1_step = 0
elif var1_step == 4:
var_type = None
if type == NUMBER:
close = end[1]
if text.find('.') != -1: var_type = float
else: var_type = int
elif type == STRING:
close = end[1]
var_type = str
elif text == '[':
close = line.find(']', end[1])
var_type = list
elif text == '(':
close = line.find(')', end[1])
var_type = tuple
elif text == '{':
close = line.find('}', end[1])
var_type = dict
elif text == 'dict':
close = line.find(')', end[1])
var_type = dict
if var_type and close+1 < len(line):
if line[close+1] != ' ' and line[close+1] != '\t':
var_type = None
cls_vars[var_name] = VarDesc(var_name, var_type, start[0])
var1_step = 0
elif def_step > 0: # Parsing a def
# Look for 'global ???[,???]'
if var2_step == 0:
if text == 'global':
var2_step = 1
elif var2_step == 1:
if type == NAME:
if not vars.has_key(text):
vars[text] = VarDesc(text, None, start[0])
elif text != ',' and type != NL:
var2_step == 0
else: # In global scope
if var3_step == 0:
# Look for names
if text == 'for':
var_accum = dict()
var_forflag = True
elif text == '=' or (var_forflag and text == 'in'):
var_forflag = False
var3_step = 1
elif type == NAME:
if prev_text != '.' and not vars.has_key(text):
var_accum[text] = VarDesc(text, None, start[0])
elif not text in [',', '(', ')', '[', ']']:
var_accum = dict()
var_forflag = False
elif var3_step == 1:
if len(var_accum) != 1:
var_type = None
vars.update(var_accum)
else:
var_name = var_accum.keys()[0]
var_type = None
if type == NUMBER:
if text.find('.') != -1: var_type = float
else: var_type = int
elif type == STRING: var_type = str
elif text == '[': var_type = list
elif text == '(': var_type = tuple
elif text == '{': var_type = dict
vars[var_name] = VarDesc(var_name, var_type, start[0])
var3_step = 0
#######################
## General utilities ##
#######################
prev_type = type
prev_text = text
desc = ScriptDesc(txt.name, imports, classes, defs, vars, incomplete)
desc.set_delay(10 * (time()-start_time) + 0.05)
global _parse_cache
_parse_cache[hash(txt)] = desc
return desc
def get_modules(since=1):
"""Returns the set of built-in modules and any modules that have been
imported into the system upto 'since' seconds ago.
"""
global _modules, _modules_updated
t = time()
if _modules_updated < t - since:
_modules.update(sys.modules)
_modules_updated = t
return _modules.keys()
def suggest_cmp(x, y):
"""Use this method when sorting a list of suggestions.
"""
return cmp(x[0].upper(), y[0].upper())
def get_module(name):
"""Returns the module specified by its name. The module itself is imported
by this method and, as such, any initialization code will be executed.
"""
mod = __import__(name)
components = name.split('.')
for comp in components[1:]:
mod = getattr(mod, comp)
return mod
def type_char(v):
"""Returns the character used to signify the type of a variable. Use this
method to identify the type character for an item in a suggestion list.
The following values are returned:
'm' if the parameter is a module
'f' if the parameter is callable
'v' if the parameter is variable or otherwise indeterminable
"""
if isinstance(v, ModuleType):
return 'm'
elif callable(v):
return 'f'
else:
return 'v'
def get_context(txt):
"""Establishes the context of the cursor in the given Blender Text object
Returns one of:
CTX_NORMAL - Cursor is in a normal context
CTX_SINGLE_QUOTE - Cursor is inside a single quoted string
CTX_DOUBLE_QUOTE - Cursor is inside a double quoted string
CTX_COMMENT - Cursor is inside a comment
"""
l, cursor = txt.getCursorPos()
lines = txt.asLines(0, l+1)
# FIXME: This method is too slow in large files for it to be called as often
# as it is. So for lines below the 1000th line we do this... (quorn)
if l > 1000: return CTX_NORMAL
# Detect context (in string or comment)
in_str = CTX_NORMAL
for line in lines:
if l == 0:
end = cursor
else:
end = len(line)
l -= 1
# Comments end at new lines
if in_str == CTX_COMMENT:
in_str = CTX_NORMAL
for i in range(end):
if in_str == 0:
if line[i] == "'": in_str = CTX_SINGLE_QUOTE
elif line[i] == '"': in_str = CTX_DOUBLE_QUOTE
elif line[i] == '#': in_str = CTX_COMMENT
else:
if in_str == CTX_SINGLE_QUOTE:
if line[i] == "'":
in_str = CTX_NORMAL
# In again if ' escaped, out again if \ escaped, and so on
for a in range(i-1, -1, -1):
if line[a] == '\\': in_str = 1-in_str
else: break
elif in_str == CTX_DOUBLE_QUOTE:
if line[i] == '"':
in_str = CTX_NORMAL
# In again if " escaped, out again if \ escaped, and so on
for a in range(i-1, -1, -1):
if line[i-a] == '\\': in_str = 2-in_str
else: break
return in_str
def current_line(txt):
"""Extracts the Python script line at the cursor in the Blender Text object
provided and cursor position within this line as the tuple pair (line,
cursor).
"""
lineindex, cursor = txt.getCursorPos()
lines = txt.asLines()
line = lines[lineindex]
# Join previous lines to this line if spanning
i = lineindex - 1
while i > 0:
earlier = lines[i].rstrip()
if earlier.endswith('\\'):
line = earlier[:-1] + ' ' + line
cursor += len(earlier)
i -= 1
# Join later lines while there is an explicit joining character
i = lineindex
while i < len(lines)-1 and lines[i].rstrip().endswith('\\'):
later = lines[i+1].strip()
line = line + ' ' + later[:-1]
i += 1
return line, cursor
def get_targets(line, cursor):
"""Parses a period separated string of valid names preceding the cursor and
returns them as a list in the same order.
"""
brk = 0
targets = []
j = cursor
i = j-1
while i >= 0:
if line[i] == ')': brk += 1
elif brk:
if line[i] == '(': brk -= 1
else:
if line[i] == '.':
targets.insert(0, line[i+1:j]); j=i
elif not (line[i].isalnum() or line[i] == '_' or line[i] == '.'):
break
i -= 1
targets.insert(0, line[i+1:j])
return targets
def get_defs(txt):
"""Returns a dictionary which maps definition names in the source code to
a list of their parameter names.
The line 'def doit(one, two, three): print one' for example, results in the
mapping 'doit' : [ 'one', 'two', 'three' ]
"""
return get_cached_descriptor(txt).defs
def get_vars(txt):
"""Returns a dictionary of variable names found in the specified Text
object. This method locates all names followed directly by an equal sign:
'a = ???' or indirectly as part of a tuple/list assignment or inside a
'for ??? in ???:' block.
"""
return get_cached_descriptor(txt).vars
def get_imports(txt):
"""Returns a dictionary which maps symbol names in the source code to their
respective modules.
The line 'from Blender import Text as BText' for example, results in the
mapping 'BText' : <module 'Blender.Text' (built-in)>
Note that this method imports the modules to provide this mapping as as such
will execute any initilization code found within.
"""
return get_cached_descriptor(txt).imports
def get_builtins():
"""Returns a dictionary of built-in modules, functions and variables."""
return __builtin__.__dict__
#################################
## Debugging utility functions ##
#################################
def print_cache_for(txt, period=sys.maxint):
"""Prints out the data cached for a given Text object. If no period is
given the text will not be reparsed and the cached version will be returned.
Otherwise if the period has expired the text will be reparsed.
"""
desc = get_cached_descriptor(txt, period)
print '================================================'
print 'Name:', desc.name, '('+str(hash(txt))+')'
print '------------------------------------------------'
print 'Defs:'
for name, ddesc in desc.defs.items():
print ' ', name, ddesc.params, ddesc.lineno
print ' ', ddesc.doc
print '------------------------------------------------'
print 'Vars:'
for name, vdesc in desc.vars.items():
print ' ', name, vdesc.type, vdesc.lineno
print '------------------------------------------------'
print 'Imports:'
for name, item in desc.imports.items():
print ' ', name.ljust(15), item
print '------------------------------------------------'
print 'Classes:'
for clsnme, clsdsc in desc.classes.items():
print ' *********************************'
print ' Name:', clsnme
print ' ', clsdsc.doc
print ' ---------------------------------'
print ' Defs:'
for name, ddesc in clsdsc.defs.items():
print ' ', name, ddesc.params, ddesc.lineno
print ' ', ddesc.doc
print ' ---------------------------------'
print ' Vars:'
for name, vdesc in clsdsc.vars.items():
print ' ', name, vdesc.type, vdesc.lineno
print ' *********************************'
print '================================================'

@ -0,0 +1,69 @@
#!BPY
"""
Name: 'Text Plugin'
Blender: 246
Group: 'ScriptTemplate'
Tooltip: 'Add a new text for writing a text plugin'
"""
from Blender import Window
import bpy
script_data = \
'''#!BPY
"""
Name: 'My Plugin Script'
Blender: 246
Group: 'TextPlugin'
Shortcut: 'Ctrl+Alt+U'
Tooltip: 'Put some useful info here'
"""
# Add a licence here if you wish to re-distribute, we recommend the GPL
from Blender import Window, sys
import BPyTextPlugin, bpy
def my_script_util(txt):
# This function prints out statistical information about a script
desc = BPyTextPlugin.get_cached_descriptor(txt)
print '---------------------------------------'
print 'Script Name:', desc.name
print 'Classes:', len(desc.classes)
print ' ', desc.classes.keys()
print 'Functions:', len(desc.defs)
print ' ', desc.defs.keys()
print 'Variables:', len(desc.vars)
print ' ', desc.vars.keys()
def main():
# Gets the active text object, there can be many in one blend file.
txt = bpy.data.texts.active
# Silently return if the script has been run with no active text
if not txt:
return
# Text plug-ins should run quickly so we time it here
Window.WaitCursor(1)
t = sys.time()
# Run our utility function
my_script_util(txt)
# Timing the script is a good way to be aware on any speed hits when scripting
print 'Plugin script finished in %.2f seconds' % (sys.time()-t)
Window.WaitCursor(0)
# This lets you import the script without running it
if __name__ == '__main__':
main()
'''
new_text = bpy.data.texts.new('textplugin_template.py')
new_text.write(script_data)
bpy.data.texts.active = new_text
Window.RedrawAll()

@ -0,0 +1,64 @@
#!BPY
"""
Name: 'Function Documentation | Ctrl I'
Blender: 246
Group: 'TextPlugin'
Shortcut: 'Ctrl+I'
Tooltip: 'Attempts to display documentation about the function preceding the cursor.'
"""
# Only run if we have the required modules
try:
import bpy
from BPyTextPlugin import *
except ImportError:
OK = False
else:
OK = True
def main():
txt = bpy.data.texts.active
if not txt:
return
(line, c) = current_line(txt)
# Check we are in a normal context
if get_context(txt) != CTX_NORMAL:
return
# Identify the name under the cursor
llen = len(line)
while c<llen and (line[c].isalnum() or line[c]=='_'):
c += 1
targets = get_targets(line, c)
# If no name under cursor, look backward to see if we're in function parens
if len(targets) == 0 or targets[0] == '':
# Look backwards for first '(' without ')'
b = 0
found = False
for i in range(c-1, -1, -1):
if line[i] == ')': b += 1
elif line[i] == '(':
b -= 1
if b < 0:
found = True
c = i
break
if found: targets = get_targets(line, c)
if len(targets) == 0 or targets[0] == '':
return
obj = resolve_targets(txt, targets)
if not obj: return
if isinstance(obj, Definition): # Local definition
txt.showDocs(obj.doc)
elif hasattr(obj, '__doc__') and obj.__doc__:
txt.showDocs(obj.__doc__)
# Check we are running as a script and not imported as a module
if __name__ == "__main__" and OK:
main()

@ -0,0 +1,91 @@
#!BPY
"""
Name: 'Import Complete|Space'
Blender: 246
Group: 'TextPlugin'
Shortcut: 'Space'
Tooltip: 'Lists modules when import or from is typed'
"""
# Only run if we have the required modules
try:
import bpy, sys
from BPyTextPlugin import *
except ImportError:
OK = False
else:
OK = True
def main():
txt = bpy.data.texts.active
if not txt:
return
line, c = current_line(txt)
# Check we are in a normal context
if get_context(txt) != CTX_NORMAL:
return
pos = line.rfind('from ', 0, c)
# No 'from' found
if pos == -1:
# Check instead for straight 'import xxxx'
pos2 = line.rfind('import ', 0, c)
if pos2 != -1:
pos2 += 7
for i in range(pos2, c):
if line[i]==',' or (line[i]==' ' and line[i-1]==','):
pos2 = i+1
elif not line[i].isalnum() and line[i] != '_':
return
items = [(m, 'm') for m in get_modules()]
items.sort(cmp = suggest_cmp)
txt.suggest(items, line[pos2:c].strip())
return
# Found 'from xxxxx' before cursor
immediate = True
pos += 5
for i in range(pos, c):
if not line[i].isalnum() and line[i] != '_' and line[i] != '.':
immediate = False
break
# Immediate 'from' followed by at most a module name
if immediate:
items = [(m, 'm') for m in get_modules()]
items.sort(cmp = suggest_cmp)
txt.suggest(items, line[pos:c])
return
# Found 'from' earlier, suggest import if not already there
pos2 = line.rfind('import ', pos, c)
# No 'import' found after 'from' so suggest it
if pos2 == -1:
txt.suggest([('import', 'k')], '')
return
# Immediate 'import' before cursor and after 'from...'
for i in range(pos2+7, c):
if line[i]==',' or (line[i]==' ' and line[i-1]==','):
pass
elif not line[i].isalnum() and line[i] != '_':
return
between = line[pos:pos2-1].strip()
try:
mod = get_module(between)
except ImportError:
return
items = [('*', 'k')]
for (k,v) in mod.__dict__.items():
items.append((k, type_char(v)))
items.sort(cmp = suggest_cmp)
txt.suggest(items, '')
# Check we are running as a script and not imported as a module
if __name__ == "__main__" and OK:
main()

@ -0,0 +1,90 @@
#!BPY
"""
Name: 'Member Suggest | .'
Blender: 246
Group: 'TextPlugin'
Shortcut: 'Period'
Tooltip: 'Lists members of the object preceding the cursor in the current text space'
"""
# Only run if we have the required modules
try:
import bpy
from BPyTextPlugin import *
except ImportError:
OK = False
else:
OK = True
def main():
txt = bpy.data.texts.active
if not txt:
return
(line, c) = current_line(txt)
# Check we are in a normal context
if get_context(txt) != CTX_NORMAL:
return
targets = get_targets(line, c)
if targets[0] == '': # Check if we are looking at a constant [] {} '' etc.
i = c - len('.'.join(targets)) - 1
if i >= 0:
if line[i] == '"' or line[i] == "'":
targets[0] = 'str'
elif line[i] == '}':
targets[0] = 'dict'
elif line[i] == ']': # Could be array elem x[y] or list [y]
i = line.rfind('[', 0, i) - 1
while i >= 0:
if line[i].isalnum() or line[i] == '_':
break
elif line[i] != ' ' and line[i] != '\t':
i = -1
break
i -= 1
if i < 0:
targets[0] = 'list'
obj = resolve_targets(txt, targets[:-1])
if not obj:
return
items = []
if isinstance(obj, VarDesc):
obj = obj.type
if isinstance(obj, Definition): # Locally defined
if hasattr(obj, 'classes'):
items.extend([(s, 'f') for s in obj.classes.keys()])
if hasattr(obj, 'defs'):
items.extend([(s, 'f') for s in obj.defs.keys()])
if hasattr(obj, 'vars'):
items.extend([(s, 'v') for s in obj.vars.keys()])
else: # Otherwise we have an imported or builtin object
try:
attr = obj.__dict__.keys()
except AttributeError:
attr = dir(obj)
else:
if not attr: attr = dir(obj)
for k in attr:
try:
v = getattr(obj, k)
except (AttributeError, TypeError): # Some attributes are not readable
pass
else:
items.append((k, type_char(v)))
if items != []:
items.sort(cmp = suggest_cmp)
txt.suggest(items, targets[-1])
# Check we are running as a script and not imported as a module
if __name__ == "__main__" and OK:
main()

@ -0,0 +1,142 @@
#!BPY
"""
Name: 'Code Outline | Ctrl T'
Blender: 246
Group: 'TextPlugin'
Shortcut: 'Ctrl+T'
Tooltip: 'Provides a menu for jumping to class and functions definitions.'
"""
# Only run if we have the required modules
try:
import bpy
from BPyTextPlugin import *
from Blender import Draw
except ImportError:
OK = False
else:
OK = True
def make_menu(items, eventoffs):
n = len(items)
if n < 20:
return [(items[i], i+1+eventoffs) for i in range(len(items))]
letters = []
check = 'abcdefghijklmnopqrstuvwxyz_' # Names cannot start 0-9
for c in check:
for item in items:
if item[0].lower() == c:
letters.append(c)
break
entries = {}
i = 0
for item in items:
i += 1
c = item[0].lower()
entries.setdefault(c, []).append((item, i+eventoffs))
subs = []
for c in letters:
subs.append((c, entries[c]))
return subs
def find_word(txt, word):
i = 0
txt.reset()
while True:
try:
line = txt.readline()
except StopIteration:
break
c = line.find(word)
if c != -1:
txt.setCursorPos(i, c)
break
i += 1
def main():
txt = bpy.data.texts.active
if not txt:
return
# Identify word under cursor
if get_context(txt) == CTX_NORMAL:
line, c = current_line(txt)
start = c-1
end = c
while start >= 0:
if not line[start].lower() in 'abcdefghijklmnopqrstuvwxyz0123456789_':
break
start -= 1
while end < len(line):
if not line[end].lower() in 'abcdefghijklmnopqrstuvwxyz0123456789_':
break
end += 1
word = line[start+1:end]
if word in KEYWORDS:
word = None
else:
word = None
script = get_cached_descriptor(txt)
items = []
desc = None
tmp = script.classes.keys()
tmp.sort(cmp = suggest_cmp)
class_menu = make_menu(tmp, len(items))
class_menu_length = len(tmp)
items.extend(tmp)
tmp = script.defs.keys()
tmp.sort(cmp = suggest_cmp)
defs_menu = make_menu(tmp, len(items))
defs_menu_length = len(tmp)
items.extend(tmp)
tmp = script.vars.keys()
tmp.sort(cmp = suggest_cmp)
vars_menu = make_menu(tmp, len(items))
vars_menu_length = len(tmp)
items.extend(tmp)
menu = [('Script %t', 0),
('Classes', class_menu),
('Functions', defs_menu),
('Variables', vars_menu)]
if word:
menu.extend([None, ('Locate', [(word, -10)])])
i = Draw.PupTreeMenu(menu)
if i == -1:
return
# Chosen to search for word under cursor
if i == -10:
if script.classes.has_key(word):
desc = script.classes[word]
elif script.defs.has_key(word):
desc = script.defs[word]
elif script.vars.has_key(word):
desc = script.vars[word]
else:
find_word(txt, word)
return
else:
i -= 1
if i < class_menu_length:
desc = script.classes[items[i]]
elif i < class_menu_length + defs_menu_length:
desc = script.defs[items[i]]
elif i < class_menu_length + defs_menu_length + vars_menu_length:
desc = script.vars[items[i]]
if desc:
txt.setCursorPos(desc.lineno-1, 0)
# Check we are running as a script and not imported as a module
if __name__ == "__main__" and OK:
main()

@ -0,0 +1,94 @@
#!BPY
"""
Name: 'Suggest All | Ctrl Space'
Blender: 246
Group: 'TextPlugin'
Shortcut: 'Ctrl+Space'
Tooltip: 'Performs suggestions based on the context of the cursor'
"""
# Only run if we have the required modules
try:
import bpy
from BPyTextPlugin import *
except ImportError:
OK = False
else:
OK = True
def check_membersuggest(line, c):
pos = line.rfind('.', 0, c)
if pos == -1:
return False
for s in line[pos+1:c]:
if not s.isalnum() and s != '_':
return False
return True
def check_imports(line, c):
pos = line.rfind('import ', 0, c)
if pos > -1:
for s in line[pos+7:c]:
if not s.isalnum() and s != '_':
return False
return True
pos = line.rfind('from ', 0, c)
if pos > -1:
for s in line[pos+5:c]:
if not s.isalnum() and s != '_':
return False
return True
return False
def main():
txt = bpy.data.texts.active
if not txt:
return
line, c = current_line(txt)
# Check we are in a normal context
if get_context(txt) != CTX_NORMAL:
return
# Check the character preceding the cursor and execute the corresponding script
if check_membersuggest(line, c):
import textplugin_membersuggest
textplugin_membersuggest.main()
return
elif check_imports(line, c):
import textplugin_imports
textplugin_imports.main()
return
# Otherwise we suggest globals, keywords, etc.
list = []
targets = get_targets(line, c)
desc = get_cached_descriptor(txt)
for k in KEYWORDS:
list.append((k, 'k'))
for k, v in get_builtins().items():
list.append((k, type_char(v)))
for k, v in desc.imports.items():
list.append((k, type_char(v)))
for k, v in desc.classes.items():
list.append((k, 'f'))
for k, v in desc.defs.items():
list.append((k, 'f'))
for k, v in desc.vars.items():
list.append((k, 'v'))
list.sort(cmp = suggest_cmp)
txt.suggest(list, targets[-1])
# Check we are running as a script and not imported as a module
if __name__ == "__main__" and OK:
main()

@ -0,0 +1,123 @@
#!BPY
"""
Name: 'Template Completion | Tab'
Blender: 246
Group: 'TextPlugin'
Shortcut: 'Tab'
Tooltip: 'Completes templates based on the text preceding the cursor'
"""
# Only run if we have the required modules
try:
import bpy
from BPyTextPlugin import *
from Blender import Text
except ImportError:
OK = False
else:
OK = True
templates = {
'ie':
'if ${1:cond}:\n'
'\t${2}\n'
'else:\n'
'\t${3}\n',
'iei':
'if ${1:cond}:\n'
'\t${2}\n'
'elif:\n'
'\t${3}\n'
'else:\n'
'\t${4}\n',
'def':
'def ${1:name}(${2:params}):\n'
'\t"""(${2}) - ${3:comment}"""\n'
'\t${4}',
'cls':
'class ${1:name}(${2:parent}):\n'
'\t"""${3:docs}"""\n'
'\t\n'
'\tdef __init__(self, ${4:params}):\n'
'\t\t"""Creates a new ${1}"""\n'
'\t\t${5}',
'class':
'class ${1:name}(${2:parent}):\n'
'\t"""${3:docs}"""\n'
'\t\n'
'\tdef __init__(self, ${4:params}):\n'
'\t\t"""Creates a new ${1}"""\n'
'\t\t${5}'
}
def main():
txt = bpy.data.texts.active
if not txt:
return
row, c = txt.getCursorPos()
line = txt.asLines(row, row+1)[0]
indent=0
while indent<c and (line[indent]==' ' or line[indent]=='\t'):
indent += 1
# Check we are in a normal context
if get_context(txt) != CTX_NORMAL:
return
targets = get_targets(line, c-1);
if len(targets) != 1: return
color = (0, 192, 32)
for trigger, template in templates.items():
if trigger != targets[0]: continue
inserts = {}
txt.delete(-len(trigger)-1)
y, x = txt.getCursorPos()
first = None
# Insert template text and parse for insertion points
count = len(template); i = 0
while i < count:
if i<count-1 and template[i]=='$' and template[i+1]=='{':
i += 2
e = template.find('}', i)
item = template[i:e].split(':')
if len(item)<2: item.append('')
if not inserts.has_key(item[0]):
inserts[item[0]] = (item[1], [(x, y)])
else:
inserts[item[0]][1].append((x, y))
item[1] = inserts[item[0]][0]
if not first: first = (item[1], x, y)
txt.insert(item[1])
x += len(item[1])
i = e
else:
txt.insert(template[i])
if template[i] == '\n':
txt.insert(line[:indent])
y += 1
x = indent
else:
x += 1
i += 1
# Insert markers at insertion points
for id, (text, points) in inserts.items():
for x, y in points:
txt.setCursorPos(y, x)
txt.setSelectPos(y, x+len(text))
txt.markSelection((hash(text)+int(id)) & 0xFFFF, color,
Text.TMARK_TEMP | Text.TMARK_EDITALL)
if first:
text, x, y = first
txt.setCursorPos(y, x)
txt.setSelectPos(y, x+len(text))
break
# Check we are running as a script and not imported as a module
if __name__ == "__main__" and OK:
main()

@ -0,0 +1,93 @@
/**
* $Id: $
*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* The Original Code is Copyright (C) 2008, Blender Foundation
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): none yet.
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef BKE_SUGGESTIONS_H
#define BKE_SUGGESTIONS_H
#ifdef __cplusplus
extern "C" {
#endif
/* ****************************************************************************
Suggestions should be added in sorted order although a linear sorting method is
implemented. The list is then divided up based on the prefix provided by
update_suggestions:
Example:
Prefix: ab
aaa <-- first
aab
aba <-- firstmatch
abb <-- lastmatch
baa
bab <-- last
**************************************************************************** */
struct Text;
typedef struct SuggItem {
struct SuggItem *prev, *next;
char *name;
char type;
} SuggItem;
typedef struct SuggList {
SuggItem *first, *last;
SuggItem *firstmatch, *lastmatch;
SuggItem *selected;
int top;
} SuggList;
/* Free all text tool memory */
void free_texttools();
/* Used to identify which Text object the current tools should appear against */
void texttool_text_set_active(Text *text);
void texttool_text_clear();
short texttool_text_is_active(Text *text);
/* Suggestions */
void texttool_suggest_add(const char *name, char type);
void texttool_suggest_prefix(const char *prefix);
void texttool_suggest_clear();
SuggItem *texttool_suggest_first();
SuggItem *texttool_suggest_last();
void texttool_suggest_select(SuggItem *sel);
SuggItem *texttool_suggest_selected();
int *texttool_suggest_top();
/* Documentation */
void texttool_docs_show(const char *docs);
char *texttool_docs_get();
void texttool_docs_clear();
#ifdef __cplusplus
}
#endif
#endif

@ -52,20 +52,24 @@ void txt_free_cut_buffer (void);
char* txt_to_buf (struct Text *text);
void txt_clean_text (struct Text *text);
void txt_order_cursors (struct Text *text);
int txt_find_string (struct Text *text, char *findstr);
int txt_find_string (struct Text *text, char *findstr, int wrap);
int txt_has_sel (struct Text *text);
int txt_get_span (struct TextLine *from, struct TextLine *to);
void txt_move_up (struct Text *text, short sel);
void txt_move_down (struct Text *text, short sel);
void txt_move_left (struct Text *text, short sel);
void txt_move_right (struct Text *text, short sel);
void txt_jump_left (struct Text *text, short sel);
void txt_jump_right (struct Text *text, short sel);
void txt_move_bof (struct Text *text, short sel);
void txt_move_eof (struct Text *text, short sel);
void txt_move_bol (struct Text *text, short sel);
void txt_move_eol (struct Text *text, short sel);
void txt_move_toline (struct Text *text, unsigned int line, short sel);
void txt_move_to (struct Text *text, unsigned int line, unsigned int ch, short sel);
void txt_pop_sel (struct Text *text);
void txt_delete_char (struct Text *text);
void txt_delete_word (struct Text *text);
void txt_copy_sel (struct Text *text);
void txt_sel_all (struct Text *text);
void txt_sel_line (struct Text *text);
@ -80,8 +84,10 @@ void txt_do_undo (struct Text *text);
void txt_do_redo (struct Text *text);
void txt_split_curline (struct Text *text);
void txt_backspace_char (struct Text *text);
void txt_backspace_word (struct Text *text);
int txt_add_char (struct Text *text, char add);
void txt_find_panel (struct SpaceText *st, int again);
int txt_replace_char (struct Text *text, char add);
void find_and_replace (struct SpaceText *st, short mode);
void run_python_script (struct SpaceText *st);
int jumptoline_interactive (struct SpaceText *st);
void txt_export_to_object (struct Text *text);
@ -94,6 +100,17 @@ int setcurr_tab (struct Text *text);
void convert_tabs (struct SpaceText *st, int tab);
void txt_copy_clipboard (struct Text *text);
void txt_paste_clipboard (struct Text *text);
void txt_add_marker (struct Text *text, struct TextLine *line, int start, int end, char color[4], int group, int flags);
short txt_clear_marker_region (struct Text *text, struct TextLine *line, int start, int end, int group, int flags);
short txt_clear_markers (struct Text *text, int group, int flags);
struct TextMarker *txt_find_marker (struct Text *text, struct TextLine *line, int curs, int group, int flags);
struct TextMarker *txt_find_marker_region (struct Text *text, struct TextLine *line, int start, int end, int group, int flags);
struct TextMarker *txt_prev_marker (struct Text *text, struct TextMarker *marker);
struct TextMarker *txt_next_marker (struct Text *text, struct TextMarker *marker);
struct TextMarker *txt_prev_marker_color (struct Text *text, struct TextMarker *marker);
struct TextMarker *txt_next_marker_color (struct Text *text, struct TextMarker *marker);
/* Undo opcodes */
/* Simple main cursor movement */
@ -135,6 +152,14 @@ void txt_paste_clipboard (struct Text *text);
#define UNDO_COMMENT 034
#define UNDO_UNCOMMENT 035
/* Find and replace flags */
#define TXT_FIND_WRAP 0x01
#define TXT_FIND_ALLTEXTS 0x02
/* Marker flags */
#define TMARK_TEMP 0x01 /* Remove on non-editing events, don't save */
#define TMARK_EDITALL 0x02 /* Edit all markers of the same group as one */
#ifdef __cplusplus
}
#endif

@ -0,0 +1,254 @@
/**
* $Id: $
*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* The Original Code is Copyright (C) 2008, Blender Foundation
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Ian Thompson.
*
* ***** END GPL LICENSE BLOCK *****
*/
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
#include "DNA_text_types.h"
#include "BKE_text.h"
#include "BKE_suggestions.h"
/**********************/
/* Static definitions */
/**********************/
static Text *activeToolText = NULL;
static SuggList suggestions = {NULL, NULL, NULL, NULL, NULL};
static char *documentation = NULL;
static int doc_lines = 0;
static int txttl_cmp(const char *first, const char *second, int len) {
int cmp, i;
for (cmp=0, i=0; i<len; i++) {
if (cmp= toupper(first[i])-toupper(second[i])) {
break;
}
}
return cmp;
}
static void txttl_free_suggest() {
SuggItem *item, *prev;
for (item = suggestions.last; item; item=prev) {
prev = item->prev;
MEM_freeN(item);
}
suggestions.first = suggestions.last = NULL;
suggestions.firstmatch = suggestions.lastmatch = NULL;
suggestions.selected = NULL;
suggestions.top = 0;
}
static void txttl_free_docs() {
if (documentation) {
MEM_freeN(documentation);
documentation = NULL;
}
}
/**************************/
/* General tool functions */
/**************************/
void free_texttools() {
txttl_free_suggest();
txttl_free_docs();
}
void texttool_text_set_active(Text *text) {
if (activeToolText == text) return;
texttool_text_clear();
activeToolText = text;
}
void texttool_text_clear() {
free_texttools();
activeToolText = NULL;
}
short texttool_text_is_active(Text *text) {
return activeToolText==text ? 1 : 0;
}
/***************************/
/* Suggestion list methods */
/***************************/
void texttool_suggest_add(const char *name, char type) {
SuggItem *newitem, *item;
int len, cmp;
newitem = MEM_mallocN(sizeof(SuggItem) + strlen(name) + 1, "SuggestionItem");
if (!newitem) {
printf("Failed to allocate memory for suggestion.\n");
return;
}
newitem->name = (char *) (newitem + 1);
len = strlen(name);
strncpy(newitem->name, name, len);
newitem->name[len] = '\0';
newitem->type = type;
newitem->prev = newitem->next = NULL;
/* Perform simple linear search for ordered storage */
if (!suggestions.first || !suggestions.last) {
suggestions.first = suggestions.last = newitem;
} else {
cmp = -1;
for (item=suggestions.last; item; item=item->prev) {
cmp = txttl_cmp(name, item->name, len);
/* Newitem comes after this item, insert here */
if (cmp >= 0) {
newitem->prev = item;
if (item->next)
item->next->prev = newitem;
newitem->next = item->next;
item->next = newitem;
/* At last item, set last pointer here */
if (item == suggestions.last)
suggestions.last = newitem;
break;
}
}
/* Reached beginning of list, insert before first */
if (cmp < 0) {
newitem->next = suggestions.first;
suggestions.first->prev = newitem;
suggestions.first = newitem;
}
}
suggestions.firstmatch = suggestions.lastmatch = suggestions.selected = NULL;
suggestions.top= 0;
}
void texttool_suggest_prefix(const char *prefix) {
SuggItem *match, *first, *last;
int cmp, len = strlen(prefix), top = 0;
if (!suggestions.first) return;
if (len==0) {
suggestions.selected = suggestions.firstmatch = suggestions.first;
suggestions.lastmatch = suggestions.last;
return;
}
first = last = NULL;
for (match=suggestions.first; match; match=match->next) {
cmp = txttl_cmp(prefix, match->name, len);
if (cmp==0) {
if (!first) {
first = match;
suggestions.top = top;
}
} else if (cmp<0) {
if (!last) {
last = match->prev;
break;
}
}
top++;
}
if (first) {
if (!last) last = suggestions.last;
suggestions.firstmatch = first;
suggestions.lastmatch = last;
suggestions.selected = first;
} else {
suggestions.firstmatch = NULL;
suggestions.lastmatch = NULL;
suggestions.selected = NULL;
suggestions.top = 0;
}
}
void texttool_suggest_clear() {
txttl_free_suggest();
}
SuggItem *texttool_suggest_first() {
return suggestions.firstmatch;
}
SuggItem *texttool_suggest_last() {
return suggestions.lastmatch;
}
void texttool_suggest_select(SuggItem *sel) {
suggestions.selected = sel;
}
SuggItem *texttool_suggest_selected() {
return suggestions.selected;
}
int *texttool_suggest_top() {
return &suggestions.top;
}
/*************************/
/* Documentation methods */
/*************************/
void texttool_docs_show(const char *docs) {
int len;
if (!docs) return;
len = strlen(docs);
if (documentation) {
MEM_freeN(documentation);
documentation = NULL;
}
/* Ensure documentation ends with a '\n' */
if (docs[len-1] != '\n') {
documentation = MEM_mallocN(len+2, "Documentation");
strncpy(documentation, docs, len);
documentation[len++] = '\n';
} else {
documentation = MEM_mallocN(len+1, "Documentation");
strncpy(documentation, docs, len);
}
documentation[len] = '\0';
}
char *texttool_docs_get() {
return documentation;
}
void texttool_docs_clear() {
txttl_free_docs();
}

@ -30,6 +30,8 @@
*/
#include <string.h> /* strstr */
#include <sys/types.h>
#include <sys/stat.h>
#include "MEM_guardedalloc.h"
@ -81,12 +83,19 @@ The st->top determines at what line the top of the text is displayed.
If the user moves the cursor the st containing that cursor should
be popped ... other st's retain their own top location.
*/ /***************/
Markers
--
The mrk->flags define the behaviour and relationships between markers. The
upper two bytes are used to hold a group ID, the lower two are normal flags. If
TMARK_EDITALL is set the group ID defines which other markers should be edited.
The mrk->clr field is used to visually group markers where the flags may not
match. A template system, for example, may allow editing of repeating tokens
(in one group) but include other marked positions (in another group) all in the
same template with the same colour.
/****************/ /*
Undo
Undo
--
Undo/Redo works by storing
events in a queue, and a pointer
to the current position in the
@ -145,6 +154,7 @@ void free_text(Text *text)
}
BLI_freelistN(&text->lines);
BLI_freelistN(&text->markers);
if(text->name) MEM_freeN(text->name);
MEM_freeN(text->undo_buf);
@ -169,10 +179,11 @@ Text *add_empty_text(char *name)
ta->flags= TXT_ISDIRTY | TXT_ISTMP | TXT_ISMEM;
ta->lines.first= ta->lines.last= NULL;
ta->markers.first= ta->markers.last= NULL;
tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline");
tmp->line= (char*) MEM_mallocN(1, "textline_string");
tmp->format= (char*) MEM_mallocN(2, "Syntax_format");
tmp->format= NULL;
tmp->line[0]=0;
tmp->len= 0;
@ -209,11 +220,12 @@ static void cleanup_textline(TextLine * tl)
int reopen_text(Text *text)
{
FILE *fp;
int i, llen, len;
int i, llen, len, res;
unsigned char *buffer;
TextLine *tmp;
char sfile[FILE_MAXFILE];
char str[FILE_MAXDIR+FILE_MAXFILE];
struct stat st;
if (!text || !text->name) return 0;
@ -242,7 +254,7 @@ int reopen_text(Text *text)
text->undo_len= TXT_INIT_UNDO;
text->undo_buf= MEM_mallocN(text->undo_len, "undo buf");
text->flags= TXT_ISDIRTY | TXT_ISTMP;
text->flags= TXT_ISTMP;
fseek(fp, 0L, SEEK_END);
len= ftell(fp);
@ -256,6 +268,9 @@ int reopen_text(Text *text)
len = fread(buffer, 1, len, fp);
fclose(fp);
res= stat(str, &st);
text->mtime= st.st_mtime;
text->nlines=0;
i=0;
@ -264,7 +279,7 @@ int reopen_text(Text *text)
if (buffer[i]=='\n') {
tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline");
tmp->line= (char*) MEM_mallocN(llen+1, "textline_string");
tmp->format= (char*) MEM_mallocN(llen+2, "Syntax_format");
tmp->format= NULL;
if(llen) memcpy(tmp->line, &buffer[i-llen], llen);
tmp->line[llen]=0;
@ -284,7 +299,7 @@ int reopen_text(Text *text)
if (llen!=0 || text->nlines==0) {
tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline");
tmp->line= (char*) MEM_mallocN(llen+1, "textline_string");
tmp->format= (char*) MEM_mallocN(llen+2, "Syntax_format");
tmp->format= NULL;
if(llen) memcpy(tmp->line, &buffer[i-llen], llen);
@ -307,12 +322,13 @@ int reopen_text(Text *text)
Text *add_text(char *file)
{
FILE *fp;
int i, llen, len;
int i, llen, len, res;
unsigned char *buffer;
TextLine *tmp;
Text *ta;
char sfile[FILE_MAXFILE];
char str[FILE_MAXDIR+FILE_MAXFILE];
struct stat st;
BLI_strncpy(str, file, FILE_MAXDIR+FILE_MAXFILE);
if (G.scene) /* can be NULL (bg mode) */
@ -326,6 +342,7 @@ Text *add_text(char *file)
ta->id.us= 1;
ta->lines.first= ta->lines.last= NULL;
ta->markers.first= ta->markers.last= NULL;
ta->curl= ta->sell= NULL;
/* ta->flags= TXT_ISTMP | TXT_ISEXT; */
@ -348,6 +365,9 @@ Text *add_text(char *file)
len = fread(buffer, 1, len, fp);
fclose(fp);
res= stat(str, &st);
ta->mtime= st.st_mtime;
ta->nlines=0;
i=0;
@ -356,7 +376,7 @@ Text *add_text(char *file)
if (buffer[i]=='\n') {
tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline");
tmp->line= (char*) MEM_mallocN(llen+1, "textline_string");
tmp->format= (char*) MEM_mallocN(llen+2, "Syntax_format");
tmp->format= NULL;
if(llen) memcpy(tmp->line, &buffer[i-llen], llen);
tmp->line[llen]=0;
@ -376,7 +396,7 @@ Text *add_text(char *file)
if (llen!=0 || ta->nlines==0) {
tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline");
tmp->line= (char*) MEM_mallocN(llen+1, "textline_string");
tmp->format= (char*) MEM_mallocN(llen+2, "Syntax_format");
tmp->format= NULL;
if(llen) memcpy(tmp->line, &buffer[i-llen], llen);
@ -410,6 +430,7 @@ Text *copy_text(Text *ta)
tan->flags = ta->flags | TXT_ISDIRTY | TXT_ISTMP;
tan->lines.first= tan->lines.last= NULL;
tan->markers.first= tan->markers.last= NULL;
tan->curl= tan->sell= NULL;
tan->nlines= ta->nlines;
@ -419,7 +440,7 @@ Text *copy_text(Text *ta)
while (line) {
tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline");
tmp->line= MEM_mallocN(line->len+1, "textline_string");
tmp->format= MEM_mallocN(line->len+2, "Syntax_format");
tmp->format= NULL;
strcpy(tmp->line, line->line);
@ -440,14 +461,14 @@ Text *copy_text(Text *ta)
/* Editing utility functions */
/*****************************/
static void make_new_line (TextLine *line, char *newline, char *newformat)
static void make_new_line (TextLine *line, char *newline)
{
if (line->line) MEM_freeN(line->line);
if (line->format) MEM_freeN(line->format);
line->line= newline;
line->len= strlen(newline);
line->format= newformat;
line->format= NULL;
}
static TextLine *txt_new_line(char *str)
@ -458,7 +479,7 @@ static TextLine *txt_new_line(char *str)
tmp= (TextLine *) MEM_mallocN(sizeof(TextLine), "textline");
tmp->line= MEM_mallocN(strlen(str)+1, "textline_string");
tmp->format= MEM_mallocN(strlen(str)+2, "Syntax_format");
tmp->format= NULL;
strcpy(tmp->line, str);
@ -476,7 +497,7 @@ static TextLine *txt_new_linen(char *str, int n)
tmp= (TextLine *) MEM_mallocN(sizeof(TextLine), "textline");
tmp->line= MEM_mallocN(n+1, "textline_string");
tmp->format= MEM_mallocN(n+2, "Syntax_format");
tmp->format= NULL;
BLI_strncpy(tmp->line, str, n+1);
@ -553,6 +574,19 @@ static void txt_make_dirty (Text *text)
if (text->compiled) BPY_free_compiled_text(text);
}
/* 0:whitespace, 1:punct, 2:alphanumeric */
static short txt_char_type (char ch)
{
if (ch <= ' ') return 0;
if (ch <= '/') return 1;
if (ch <= '9') return 2;
if (ch <= '@') return 1;
if (ch <= 'Z') return 2;
if (ch <= '`') return 1;
if (ch <= 'z') return 2;
return 1;
}
/****************************/
/* Cursor utility functions */
/****************************/
@ -606,8 +640,7 @@ void txt_move_up(Text *text, short sel)
if(!undoing) txt_undo_add_op(text, sel?UNDO_SUP:UNDO_CUP);
}
} else {
*charp= 0;
if(!undoing) txt_undo_add_op(text, sel?UNDO_SUP:UNDO_CUP);
txt_move_bol(text, sel);
}
if(!sel) txt_pop_sel(text);
@ -632,8 +665,7 @@ void txt_move_down(Text *text, short sel)
} else
if(!undoing) txt_undo_add_op(text, sel?UNDO_SDOWN:UNDO_CDOWN);
} else {
*charp= (*linep)->len;
if(!undoing) txt_undo_add_op(text, sel?UNDO_SDOWN:UNDO_CDOWN);
txt_move_eol(text, sel);
}
if(!sel) txt_pop_sel(text);
@ -689,6 +721,68 @@ void txt_move_right(Text *text, short sel)
if(!sel) txt_pop_sel(text);
}
void txt_jump_left(Text *text, short sel)
{
TextLine **linep, *oldl;
int *charp, oldc, count, i;
unsigned char oldu;
if (!text) return;
if(sel) txt_curs_sel(text, &linep, &charp);
else { txt_pop_first(text); txt_curs_cur(text, &linep, &charp); }
if (!*linep) return;
oldl= *linep;
oldc= *charp;
oldu= undoing;
undoing= 1; /* Don't push individual moves to undo stack */
count= 0;
for (i=0; i<3; i++) {
if (count < 2) {
while (*charp>0 && txt_char_type((*linep)->line[*charp-1])==i) {
txt_move_left(text, sel);
count++;
}
}
}
if (count==0) txt_move_left(text, sel);
undoing= oldu;
if(!undoing) txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, txt_get_span(text->lines.first, oldl), oldc, txt_get_span(text->lines.first, *linep), (unsigned short)*charp);
}
void txt_jump_right(Text *text, short sel)
{
TextLine **linep, *oldl;
int *charp, oldc, count, i;
unsigned char oldu;
if (!text) return;
if(sel) txt_curs_sel(text, &linep, &charp);
else { txt_pop_last(text); txt_curs_cur(text, &linep, &charp); }
if (!*linep) return;
oldl= *linep;
oldc= *charp;
oldu= undoing;
undoing= 1; /* Don't push individual moves to undo stack */
count= 0;
for (i=0; i<3; i++) {
if (count < 2) {
while (*charp<(*linep)->len && txt_char_type((*linep)->line[*charp])==i) {
txt_move_right(text, sel);
count++;
}
}
}
if (count==0) txt_move_right(text, sel);
undoing= oldu;
if(!undoing) txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, txt_get_span(text->lines.first, oldl), oldc, txt_get_span(text->lines.first, *linep), (unsigned short)*charp);
}
void txt_move_bol (Text *text, short sel)
{
TextLine **linep;
@ -760,6 +854,11 @@ void txt_move_eof (Text *text, short sel)
}
void txt_move_toline (Text *text, unsigned int line, short sel)
{
txt_move_to(text, line, 0, sel);
}
void txt_move_to (Text *text, unsigned int line, unsigned int ch, short sel)
{
TextLine **linep, *oldl;
int *charp, oldc;
@ -777,10 +876,12 @@ void txt_move_toline (Text *text, unsigned int line, short sel)
if ((*linep)->next) *linep= (*linep)->next;
else break;
}
*charp= 0;
if (ch>(*linep)->len)
ch= (*linep)->len;
*charp= ch;
if(!sel) txt_pop_sel(text);
if(!undoing) txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, txt_get_span(text->lines.first, oldl), oldc, txt_get_span(text->lines.first, *linep), (unsigned short)*charp);
if(!undoing) txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, txt_get_span(text->lines.first, oldl), oldc, txt_get_span(text->lines.first, *linep), (unsigned short)*charp);
}
/****************************/
@ -865,7 +966,9 @@ int txt_has_sel(Text *text)
static void txt_delete_sel (Text *text)
{
TextLine *tmpl;
char *buf, *format;
TextMarker *mrk;
char *buf;
int move, lineno;
if (!text) return;
if (!text->curl) return;
@ -882,13 +985,33 @@ static void txt_delete_sel (Text *text)
}
buf= MEM_mallocN(text->curc+(text->sell->len - text->selc)+1, "textline_string");
format= MEM_mallocN(text->curc+(text->sell->len - text->selc)+2, "Syntax_format");
if (text->curl != text->sell) {
txt_clear_marker_region(text, text->curl, text->curc, text->curl->len, 0, 0);
move= txt_get_span(text->curl, text->sell);
} else {
mrk= txt_find_marker_region(text, text->curl, text->curc, text->selc, 0, 0);
if (mrk && (mrk->start > text->curc || mrk->end < text->selc))
txt_clear_marker_region(text, text->curl, text->curc, text->selc, 0, 0);
move= 0;
}
mrk= txt_find_marker_region(text, text->sell, text->selc-1, text->sell->len, 0, 0);
if (mrk) {
lineno= mrk->lineno;
do {
mrk->lineno -= move;
if (mrk->start > text->curc) mrk->start -= text->selc - text->curc;
mrk->end -= text->selc - text->curc;
mrk= mrk->next;
} while (mrk && mrk->lineno==lineno);
}
strncpy(buf, text->curl->line, text->curc);
strcpy(buf+text->curc, text->sell->line + text->selc);
buf[text->curc+(text->sell->len - text->selc)]=0;
make_new_line(text->curl, buf, format);
make_new_line(text->curl, buf);
tmpl= text->sell;
while (tmpl != text->curl) {
@ -995,22 +1118,31 @@ char *txt_to_buf (Text *text)
return buf;
}
int txt_find_string(Text *text, char *findstr)
int txt_find_string(Text *text, char *findstr, int wrap)
{
TextLine *tl, *startl;
char *s= NULL;
int oldcl, oldsl, oldcc, oldsc;
if (!text || !text->curl || !text->sell) return 0;
txt_order_cursors(text);
oldcl= txt_get_span(text->lines.first, text->curl);
oldsl= txt_get_span(text->lines.first, text->sell);
tl= startl= text->sell;
oldcc= text->curc;
oldsc= text->selc;
s= strstr(&tl->line[text->selc], findstr);
while (!s) {
tl= tl->next;
if (!tl)
tl= text->lines.first;
if (!tl) {
if (wrap)
tl= text->lines.first;
else
break;
}
s= strstr(tl->line, findstr);
if (tl==startl)
@ -1018,10 +1150,10 @@ int txt_find_string(Text *text, char *findstr)
}
if (s) {
text->curl= text->sell= tl;
text->curc= (int) (s-tl->line);
text->selc= text->curc + strlen(findstr);
int newl= txt_get_span(text->lines.first, tl);
int newc= (int)(s-tl->line);
txt_move_to(text, newl, newc, 0);
txt_move_to(text, newl, newc + strlen(findstr), 1);
return 1;
} else
return 0;
@ -1621,7 +1753,6 @@ void txt_do_undo(Text *text)
case UNDO_SWAP:
txt_curs_swap(text);
txt_do_undo(text); /* swaps should appear transparent */
break;
case UNDO_DBLOCK:
@ -1736,6 +1867,19 @@ void txt_do_undo(Text *text)
break;
}
/* next undo step may need evaluating */
if (text->undo_pos>=0) {
switch (text->undo_buf[text->undo_pos]) {
case UNDO_STO:
txt_do_undo(text);
txt_do_redo(text); /* selections need restoring */
break;
case UNDO_SWAP:
txt_do_undo(text); /* swaps should appear transparent */
break;
}
}
undoing= 0;
}
@ -1810,7 +1954,7 @@ void txt_do_redo(Text *text)
case UNDO_SWAP:
txt_curs_swap(text);
txt_do_undo(text); /* swaps should appear transparent a*/
txt_do_redo(text); /* swaps should appear transparent a*/
break;
case UNDO_CTO:
@ -1947,22 +2091,37 @@ void txt_do_redo(Text *text)
void txt_split_curline (Text *text)
{
TextLine *ins;
char *left, *right, *fleft, *fright;
TextMarker *mrk;
char *left, *right;
int lineno= -1;
if (!text) return;
if (!text->curl) return;
txt_delete_sel(text);
txt_delete_sel(text);
/* Move markers */
lineno= txt_get_span(text->lines.first, text->curl);
mrk= text->markers.first;
while (mrk) {
if (mrk->lineno==lineno && mrk->start>text->curc) {
mrk->lineno++;
mrk->start -= text->curc;
mrk->end -= text->curc;
} else if (mrk->lineno > lineno) {
mrk->lineno++;
}
mrk= mrk->next;
}
/* Make the two half strings */
left= MEM_mallocN(text->curc+1, "textline_string");
fleft= MEM_mallocN(text->curc+2, "Syntax_format");
if (text->curc) memcpy(left, text->curl->line, text->curc);
left[text->curc]=0;
right= MEM_mallocN(text->curl->len - text->curc+1, "textline_string");
fright= MEM_mallocN(text->curl->len - text->curc+2, "Syntax_format");
if (text->curl->len - text->curc) memcpy(right, text->curl->line+text->curc, text->curl->len-text->curc);
right[text->curl->len - text->curc]=0;
@ -1973,11 +2132,11 @@ void txt_split_curline (Text *text)
ins= MEM_mallocN(sizeof(TextLine), "textline");
ins->line= left;
ins->format= fleft;
ins->format= NULL;
ins->len= text->curc;
text->curl->line= right;
text->curl->format= fright;
text->curl->format= NULL;
text->curl->len= text->curl->len - text->curc;
BLI_insertlinkbefore(&text->lines, text->curl, ins);
@ -1993,9 +2152,23 @@ void txt_split_curline (Text *text)
static void txt_delete_line (Text *text, TextLine *line)
{
TextMarker *mrk=NULL, *nxt;
int lineno= -1;
if (!text) return;
if (!text->curl) return;
lineno= txt_get_span(text->lines.first, line);
mrk= text->markers.first;
while (mrk) {
nxt= mrk->next;
if (mrk->lineno==lineno)
BLI_freelinkN(&text->markers, mrk);
else if (mrk->lineno > lineno)
mrk->lineno--;
mrk= nxt;
}
BLI_remlink (&text->lines, line);
if (line->line) MEM_freeN(line->line);
@ -2009,21 +2182,35 @@ static void txt_delete_line (Text *text, TextLine *line)
static void txt_combine_lines (Text *text, TextLine *linea, TextLine *lineb)
{
char *tmp, *format;
char *tmp;
TextMarker *mrk= NULL;
int lineno=-1;
if (!text) return;
if(!linea || !lineb) return;
mrk= txt_find_marker_region(text, lineb, 0, lineb->len, 0, 0);
if (mrk) {
lineno= mrk->lineno;
do {
mrk->lineno--;
mrk->start += linea->len;
mrk->end += linea->len;
mrk= mrk->next;
} while (mrk && mrk->lineno==lineno);
}
if (lineno==-1) lineno= txt_get_span(text->lines.first, lineb);
if (!mrk) mrk= text->markers.first;
tmp= MEM_mallocN(linea->len+lineb->len+1, "textline_string");
format= MEM_mallocN(linea->len+lineb->len+1, "Syntax_format");
strcpy(tmp, linea->line);
strcat(tmp, lineb->line);
make_new_line(linea, tmp, format);
make_new_line(linea, tmp);
txt_delete_line(text, lineb);
txt_delete_line(text, lineb);
txt_make_dirty(text);
txt_clean_text(text);
@ -2037,8 +2224,9 @@ void txt_delete_char (Text *text)
if (!text->curl) return;
if (txt_has_sel(text)) { /* deleting a selection */
txt_delete_sel(text);
return;
txt_delete_sel(text);
txt_make_dirty(text);
return;
}
else if (text->curc== text->curl->len) { /* Appending two lines */
if (text->curl->next) {
@ -2047,6 +2235,25 @@ void txt_delete_char (Text *text)
}
} else { /* Just deleting a char */
int i= text->curc;
TextMarker *mrk= txt_find_marker_region(text, text->curl, i-1, text->curl->len, 0, 0);
if (mrk) {
int lineno= mrk->lineno;
if (mrk->end==i) {
if ((mrk->flags & TMARK_TEMP) && !(mrk->flags & TMARK_EDITALL)) {
txt_clear_markers(text, mrk->group, TMARK_TEMP);
} else {
TextMarker *nxt= mrk->next;
BLI_freelinkN(&text->markers, mrk);
}
return;
}
do {
if (mrk->start>i) mrk->start--;
mrk->end--;
mrk= mrk->next;
} while (mrk && mrk->lineno==lineno);
}
c= text->curl->line[i];
while(i< text->curl->len) {
@ -2064,6 +2271,12 @@ void txt_delete_char (Text *text)
if(!undoing) txt_undo_add_charop(text, UNDO_DEL, c);
}
void txt_delete_word (Text *text)
{
txt_jump_right(text, 1);
txt_delete_sel(text);
}
void txt_backspace_char (Text *text)
{
char c='\n';
@ -2072,8 +2285,9 @@ void txt_backspace_char (Text *text)
if (!text->curl) return;
if (txt_has_sel(text)) { /* deleting a selection */
txt_delete_sel(text);
return;
txt_delete_sel(text);
txt_make_dirty(text);
return;
}
else if (text->curc==0) { /* Appending two lines */
if (!text->curl->prev) return;
@ -2083,19 +2297,38 @@ void txt_backspace_char (Text *text)
txt_combine_lines(text, text->curl, text->curl->next);
txt_pop_sel(text);
}
}
else { /* Just backspacing a char */
int i= text->curc-1;
int i= text->curc-1;
TextMarker *mrk= txt_find_marker_region(text, text->curl, i, text->curl->len, 0, 0);
if (mrk) {
int lineno= mrk->lineno;
if (mrk->start==i+1) {
if ((mrk->flags & TMARK_TEMP) && !(mrk->flags & TMARK_EDITALL)) {
txt_clear_markers(text, mrk->group, TMARK_TEMP);
} else {
TextMarker *nxt= mrk->next;
BLI_freelinkN(&text->markers, mrk);
}
return;
}
do {
if (mrk->start>i) mrk->start--;
mrk->end--;
mrk= mrk->next;
} while (mrk && mrk->lineno==lineno);
}
c= text->curl->line[i];
while(i< text->curl->len) {
text->curl->line[i]= text->curl->line[i+1];
i++;
}
text->curl->len--;
text->curc--;
txt_pop_sel(text);
c= text->curl->line[i];
while(i< text->curl->len) {
text->curl->line[i]= text->curl->line[i+1];
i++;
}
text->curl->len--;
text->curc--;
txt_pop_sel(text);
}
txt_make_dirty(text);
@ -2104,10 +2337,17 @@ void txt_backspace_char (Text *text)
if(!undoing) txt_undo_add_charop(text, UNDO_BS, c);
}
void txt_backspace_word (Text *text)
{
txt_jump_left(text, 1);
txt_delete_sel(text);
}
int txt_add_char (Text *text, char add)
{
int len;
char *tmp, *format;
int len, lineno;
char *tmp;
TextMarker *mrk;
if (!text) return 0;
if (!text->curl) return 0;
@ -2119,8 +2359,17 @@ int txt_add_char (Text *text, char add)
txt_delete_sel(text);
mrk= txt_find_marker_region(text, text->curl, text->curc-1, text->curl->len, 0, 0);
if (mrk) {
lineno= mrk->lineno;
do {
if (mrk->start>text->curc) mrk->start++;
mrk->end++;
mrk= mrk->next;
} while (mrk && mrk->lineno==lineno);
}
tmp= MEM_mallocN(text->curl->len+2, "textline_string");
format= MEM_mallocN(text->curl->len+4, "Syntax_format");
if(text->curc) memcpy(tmp, text->curl->line, text->curc);
tmp[text->curc]= add;
@ -2128,7 +2377,7 @@ int txt_add_char (Text *text, char add)
len= text->curl->len - text->curc;
if(len>0) memcpy(tmp+text->curc+1, text->curl->line+text->curc, len);
tmp[text->curl->len+1]=0;
make_new_line(text->curl, tmp, format);
make_new_line(text->curl, tmp);
text->curc++;
@ -2141,10 +2390,42 @@ int txt_add_char (Text *text, char add)
return 1;
}
int txt_replace_char (Text *text, char add)
{
char del;
if (!text) return 0;
if (!text->curl) return 0;
/* If text is selected or we're at the end of the line just use txt_add_char */
if (text->curc==text->curl->len || txt_has_sel(text) || add=='\n') {
TextMarker *mrk;
int i= txt_add_char(text, add);
mrk= txt_find_marker(text, text->curl, text->curc, 0, 0);
if (mrk && mrk->end==text->curc) mrk->end--;
return i;
}
del= text->curl->line[text->curc];
text->curl->line[text->curc]= (unsigned char) add;
text->curc++;
txt_pop_sel(text);
txt_make_dirty(text);
txt_clean_text(text);
/* Should probably create a new op for this */
if(!undoing) {
txt_undo_add_charop(text, UNDO_DEL, del);
txt_undo_add_charop(text, UNDO_INSERT, add);
}
return 1;
}
void indent(Text *text)
{
int len, num;
char *tmp, *format;
char *tmp;
char add = '\t';
if (!text) return;
@ -2155,7 +2436,6 @@ void indent(Text *text)
while (TRUE)
{
tmp= MEM_mallocN(text->curl->len+2, "textline_string");
format= MEM_mallocN(text->curl->len+3, "Syntax_format");
text->curc = 0;
if(text->curc) memcpy(tmp, text->curl->line, text->curc);
@ -2165,7 +2445,7 @@ void indent(Text *text)
if(len>0) memcpy(tmp+text->curc+1, text->curl->line+text->curc, len);
tmp[text->curl->len+1]=0;
make_new_line(text->curl, tmp, format);
make_new_line(text->curl, tmp);
text->curc++;
@ -2246,7 +2526,7 @@ void unindent(Text *text)
void comment(Text *text)
{
int len, num;
char *tmp, *format;
char *tmp;
char add = '#';
if (!text) return;
@ -2257,7 +2537,6 @@ void comment(Text *text)
while (TRUE)
{
tmp= MEM_mallocN(text->curl->len+2, "textline_string");
format = MEM_mallocN(text->curl->len+3, "Syntax_format");
text->curc = 0;
if(text->curc) memcpy(tmp, text->curl->line, text->curc);
@ -2267,7 +2546,7 @@ void comment(Text *text)
if(len>0) memcpy(tmp+text->curc+1, text->curl->line+text->curc, len);
tmp[text->curl->len+1]=0;
make_new_line(text->curl, tmp, format);
make_new_line(text->curl, tmp);
text->curc++;
@ -2398,3 +2677,148 @@ int setcurr_tab (Text *text)
return i;
}
/*********************************/
/* Text marker utility functions */
/*********************************/
static int color_match(TextMarker *a, TextMarker *b) {
return (a->color[0]==b->color[0] &&
a->color[1]==b->color[1] &&
a->color[2]==b->color[2] &&
a->color[3]==b->color[3]);
}
/* Creates and adds a marker to the list maintaining sorted order */
void txt_add_marker(Text *text, TextLine *line, int start, int end, char color[4], int group, int flags) {
TextMarker *tmp, *marker;
marker= MEM_mallocN(sizeof(TextMarker), "text_marker");
marker->lineno= txt_get_span(text->lines.first, line);
marker->start= MIN2(start, end);
marker->end= MAX2(start, end);
marker->group= group;
marker->flags= flags;
marker->color[0]= color[0];
marker->color[1]= color[1];
marker->color[2]= color[2];
marker->color[3]= color[3];
for (tmp=text->markers.last; tmp; tmp=tmp->prev)
if (tmp->lineno < marker->lineno || (tmp->lineno==marker->lineno && tmp->start < marker->start))
break;
if (tmp) BLI_insertlinkafter(&text->markers, tmp, marker);
else BLI_addhead(&text->markers, marker);
}
/* Returns the first matching marker on the specified line between two points.
If the group or flags fields are non-zero the returned flag must be in the
specified group and have at least the specified flags set. */
TextMarker *txt_find_marker_region(Text *text, TextLine *line, int start, int end, int group, int flags) {
TextMarker *marker, *next;
int lineno= txt_get_span(text->lines.first, line);
for (marker=text->markers.first; marker; marker=next) {
next= marker->next;
if (group && marker->group != group) continue;
else if ((marker->flags & flags) != flags) continue;
else if (marker->lineno < lineno) continue;
else if (marker->lineno > lineno) break;
if ((marker->start==marker->end && start<=marker->start && marker->start<=end) ||
(marker->start<end && marker->end>start))
return marker;
}
return NULL;
}
/* Clears all markers on the specified line between two points. If the group or
flags fields are non-zero the returned flag must be in the specified group
and have at least the specified flags set. */
short txt_clear_marker_region(Text *text, TextLine *line, int start, int end, int group, int flags) {
TextMarker *marker, *next;
int lineno= txt_get_span(text->lines.first, line);
short cleared= 0;
for (marker=text->markers.first; marker; marker=next) {
next= marker->next;
if (group && marker->group != group) continue;
else if ((marker->flags & flags) != flags) continue;
else if (marker->lineno < lineno) continue;
else if (marker->lineno > lineno) break;
if ((marker->start==marker->end && start<=marker->start && marker->start<=end) ||
(marker->start<end && marker->end>start)) {
BLI_freelinkN(&text->markers, marker);
cleared= 1;
}
}
return cleared;
}
/* Clears all markers in the specified group (if given) with at least the
specified flags set. Useful for clearing temporary markers (group=0,
flags=TMARK_TEMP) */
short txt_clear_markers(Text *text, int group, int flags) {
TextMarker *marker, *next;
short cleared= 0;
for (marker=text->markers.first; marker; marker=next) {
next= marker->next;
if ((!group || marker->group==group) &&
(marker->flags & flags) == flags) {
BLI_freelinkN(&text->markers, marker);
cleared= 1;
}
}
return cleared;
}
/* Finds the marker at the specified line and cursor position with at least the
specified flags set in the given group (if non-zero). */
TextMarker *txt_find_marker(Text *text, TextLine *line, int curs, int group, int flags) {
TextMarker *marker;
int lineno= txt_get_span(text->lines.first, line);
for (marker=text->markers.first; marker; marker=marker->next) {
if (group && marker->group != group) continue;
else if ((marker->flags & flags) != flags) continue;
else if (marker->lineno < lineno) continue;
else if (marker->lineno > lineno) break;
if (marker->start <= curs && curs <= marker->end)
return marker;
}
return NULL;
}
/* Finds the previous marker in the same group. If no other is found, the same
marker will be returned */
TextMarker *txt_prev_marker(Text *text, TextMarker *marker) {
TextMarker *tmp= marker;
while (tmp) {
if (tmp->prev) tmp= tmp->prev;
else tmp= text->markers.last;
if (tmp->group == marker->group)
return tmp;
}
return NULL; /* Only if marker==NULL */
}
/* Finds the next marker in the same group. If no other is found, the same
marker will be returned */
TextMarker *txt_next_marker(Text *text, TextMarker *marker) {
TextMarker *tmp= marker;
while (tmp) {
if (tmp->next) tmp= tmp->next;
else tmp= text->markers.first;
if (tmp->group == marker->group)
return tmp;
}
return NULL; /* Only if marker==NULL */
}

@ -2273,6 +2273,7 @@ static void direct_link_text(FileData *fd, Text *text)
*/
link_list(fd, &text->lines);
link_list(fd, &text->markers);
text->curl= newdataadr(fd, text->curl);
text->sell= newdataadr(fd, text->sell);

@ -1849,6 +1849,7 @@ static void write_texts(WriteData *wd, ListBase *idbase)
{
Text *text;
TextLine *tmp;
TextMarker *mrk;
text= idbase->first;
while(text) {
@ -1872,7 +1873,16 @@ static void write_texts(WriteData *wd, ListBase *idbase)
writedata(wd, DATA, tmp->len+1, tmp->line);
tmp= tmp->next;
}
/* write markers */
mrk= text->markers.first;
while (mrk) {
writestruct(wd, DATA, "TextMarker", 1, mrk);
mrk= mrk->next;
}
}
text= text->id.next;
}

@ -38,14 +38,15 @@ void unlink_text(struct Text *text);
void free_textspace(struct SpaceText *st);
int txt_file_modified(struct Text *text);
void txt_write_file(struct Text *text);
void add_text_fs(char *file);
void free_txt_data(void);
void pop_space_text(struct SpaceText *st);
void get_format_string(struct SpaceText *st);
void do_brackets(void);
void txt_format_text(struct SpaceText *st);
void txt_format_line(struct SpaceText *st, struct TextLine *line, int do_next);
#endif

@ -31,6 +31,9 @@
#define BIF_KEYVAL_H
char *key_event_to_string(unsigned short event);
int decode_key_string(char *str, unsigned short *key, unsigned short *qual);
#endif

@ -57,8 +57,8 @@ typedef enum {
ICON_TRIA_UP,
ICON_FONTPREVIEW,
ICON_BLANK4,
ICON_BLANK5,
ICON_BLANK6,
ICON_WORDWRAP,
ICON_WORDWRAP_OFF,
ICON_ORTHO,
ICON_PERSP,
@ -108,7 +108,7 @@ typedef enum {
ICON_FF,
ICON_REW,
ICON_PYTHON,
ICON_BLANK11,
ICON_PYTHON_ON,
ICON_BLANK12,
ICON_BLANK13,
ICON_BLANK14,

@ -83,6 +83,9 @@ struct SpaceOops;
/* nodes handler codes */
#define NODES_HANDLER_GREASEPENCIL 80
/* text handler codes */
#define TEXT_HANDLER_FIND 90
/* theme codes */
#define B_ADD_THEME 3301
#define B_DEL_THEME 3302

@ -377,6 +377,13 @@
#define B_TEXTLINENUM 507
#define B_TAB_NUMBERS 508
#define B_SYNTAX 509
#define B_WORDWRAP 510
#define B_TEXTPLUGINS 511
#define B_PASTEFIND 512
#define B_PASTEREPLACE 513
#define B_TEXTREPLACE 514
#define B_TEXTFIND 515
#define B_TEXTMARKALL 516
/* SCRIPT: 525 */
#define B_SCRIPTBROWSE 526

@ -276,22 +276,23 @@ typedef struct SpaceText {
struct Text *text;
int top, viewlines;
short flags, menunr;
int font_id;
short flags, menunr;
int font_id;
int lheight;
int left;
int showlinenrs;
int tabnumber;
int currtab_set;
int showsyntax;
int unused_padd;
int overwrite;
float pix_per_line;
struct rcti txtscroll, txtbar;
int wordwrap, doplugins;
} SpaceText;
typedef struct Script {

@ -38,25 +38,36 @@ typedef struct TextLine {
struct TextLine *next, *prev;
char *line;
char *format;
int len, blen;
char *format; /* may be NULL if syntax is off or not yet formatted */
int len, blen; /* blen unused */
} TextLine;
typedef struct TextMarker {
struct TextMarker *next, *prev;
int lineno, start, end, pad1; /* line number and start/end character indices */
int group, flags; /* see BKE_text.h for flag defines */
char color[4], pad[4]; /* draw color of the marker */
} TextMarker;
typedef struct Text {
ID id;
char *name;
int flags, nlines;
ListBase lines;
TextLine *curl, *sell;
int curc, selc;
ListBase markers;
char *undo_buf;
int undo_pos, undo_len;
void *compiled;
double mtime;
} Text;
@ -74,4 +85,12 @@ typedef struct Text {
#define TXT_READONLY 0x0100
#define TXT_FOLLOW 0x0200 /* always follow cursor (console) */
/* format continuation flags */
#define TXT_NOCONT 0x00 /* no continuation */
#define TXT_SNGQUOTSTR 0x01 /* single quotes */
#define TXT_DBLQUOTSTR 0x02 /* double quotes */
#define TXT_TRISTR 0x04 /* triplets of quotes: """ or ''' */
#define TXT_SNGTRISTR 0x05 /*(TXT_TRISTR | TXT_SNGQUOTSTR)*/
#define TXT_DBLTRISTR 0x06 /*(TXT_TRISTR | TXT_DBLQUOTSTR)*/
#endif

@ -48,6 +48,7 @@ struct bPythonConstraint; /* DNA_constraint_types.h */
struct bConstraintOb; /* DNA_constraint_types.h */
struct bConstraintTarget; /* DNA_constraint_types.h*/
struct Script; /* DNA_screen_types.h */
struct BPyMenu;
#ifdef __cplusplus
extern "C" {
#endif
@ -91,6 +92,8 @@ extern "C" {
int BPY_txt_do_python_Text( struct Text *text );
int BPY_menu_do_python( short menutype, int event );
int BPY_menu_do_shortcut( short menutype, unsigned short key, unsigned short modifiers );
int BPY_menu_invoke( struct BPyMenu *pym, short menutype );
void BPY_run_python_script( char *filename );
int BPY_run_script(struct Script *script);
void BPY_free_compiled_text( struct Text *text );

@ -556,6 +556,7 @@ void BPY_Err_Handle( char *script_name )
if( exception
&& PyErr_GivenExceptionMatches( exception, PyExc_SyntaxError ) ) {
/* no traceback available when SyntaxError */
PyErr_NormalizeException( &exception, &err, &tb );
PyErr_Restore( exception, err, tb ); /* takes away reference! */
PyErr_Print( );
v = PyObject_GetAttrString( err, "lineno" );
@ -965,8 +966,38 @@ int BPY_run_script(Script *script)
*****************************************************************************/
int BPY_menu_do_python( short menutype, int event )
{
char *argstr = NULL;
BPyMenu *pym;
pym = BPyMenu_GetEntry( menutype, ( short ) event );
return BPY_menu_invoke( pym, menutype );
}
/****************************************************************************
* Description: This function executes the script by its shortcut.
* Notes: It is called by the ui code in src/???.c when a user presses an
* unassigned key combination. Scripts are searched in the BPyMenuTable,
* using the given menutype and event values to know which one to invoke.
*****************************************************************************/
int BPY_menu_do_shortcut( short menutype, unsigned short key, unsigned short qual )
{
BPyMenu *pym;
pym = BPyMenu_GetEntry( menutype, 0 );
while ( pym ) {
if ( pym->key && pym->key == key && pym->qual == qual ) {
return BPY_menu_invoke( pym, menutype );
}
pym = pym->next;
}
return 0;
}
/****************************************************************************
* Description: This function executes the script described by a menu item.
*****************************************************************************/
int BPY_menu_invoke( BPyMenu *pym, short menutype )
{
char *argstr = NULL;
BPySubMenu *pysm;
char scriptname[21];
Script *script = NULL;
@ -974,8 +1005,6 @@ int BPY_menu_do_python( short menutype, int event )
PyGILState_STATE gilstate;
char filestr[FILE_MAX];
pym = BPyMenu_GetEntry( menutype, ( short ) event );
if( !pym )
return 0;
@ -1059,6 +1088,7 @@ int BPY_menu_do_python( short menutype, int event )
case PYMENU_RENDER:
case PYMENU_WIZARDS:
case PYMENU_SCRIPTTEMPLATE:
case PYMENU_TEXTPLUGIN:
case PYMENU_MESHFACEKEY:
break;

@ -42,6 +42,7 @@
#endif
#include "BKE_global.h"
#include "BKE_utildefines.h"
#include "BIF_keyval.h"
#include "BLI_blenlib.h"
#include "MEM_guardedalloc.h"
#include "DNA_userdef_types.h" /* for U.pythondir */
@ -106,6 +107,8 @@ static int bpymenu_group_atoi( char *str )
return PYMENU_ARMATURE;
else if( !strcmp( str, "ScriptTemplate" ) )
return PYMENU_SCRIPTTEMPLATE;
else if( !strcmp( str, "TextPlugin" ) )
return PYMENU_TEXTPLUGIN;
else if( !strcmp( str, "MeshFaceKey" ) )
return PYMENU_MESHFACEKEY;
else if( !strcmp( str, "AddMesh" ) )
@ -184,6 +187,9 @@ char *BPyMenu_group_itoa( short menugroup )
case PYMENU_SCRIPTTEMPLATE:
return "ScriptTemplate";
break;
case PYMENU_TEXTPLUGIN:
return "TextPlugin";
break;
case PYMENU_MESHFACEKEY:
return "MeshFaceKey";
break;
@ -328,6 +334,23 @@ static void bpymenu_set_tooltip( BPyMenu * pymenu, char *tip )
return;
}
static void bpymenu_set_shortcut( BPyMenu * pymenu, char *combi )
{
unsigned short key, qual;
if( !pymenu )
return;
if (!decode_key_string(combi, &key, &qual)) {
return; /* TODO: Print some error */
}
pymenu->key = key;
pymenu->qual = qual;
return;
}
/* bpymenu_AddEntry:
* try to find an existing pymenu entry with the given type and name;
* if found, update it with new info, otherwise create a new one and fill it.
@ -688,6 +711,7 @@ void BPyMenu_PrintAllEntries( void )
* # Blender: <code>short int</code> (minimal Blender version)
* # Group: 'group name' (defines menu)
* # Submenu: 'submenu name' related_1word_arg
* # Shortcut: Modifier+Key (optional shortcut combination for supported groups)
* # Tooltip: 'tooltip for the menu'
* # \"\"\"
*
@ -796,13 +820,19 @@ static int bpymenu_ParseFile(FILE *file, char *fname, int is_userdir)
if ((matches == 3) && (strstr(head, "Submenu:") != NULL)) {
bpymenu_AddSubEntry(scriptMenu, middle, tail);
} else {
/* Tooltip: 'tooltip for the menu */
/* Shortcut: 'key+combination' */
matches = sscanf(line, "%[^']'%[^']'%c", head, middle, tail);
if ((matches == 3) && ((strstr(head, "Tooltip:") != NULL) ||
(strstr(head, "Tip:") != NULL))) {
bpymenu_set_tooltip(scriptMenu, middle);
if ((matches == 3) && (strstr(head, "Shortcut:") != NULL)) {
bpymenu_set_shortcut(scriptMenu, middle);
} else {
/* Tooltip: 'tooltip for the menu */
matches = sscanf(line, "%[^']'%[^']'%c", head, middle, tail);
if ((matches == 3) && ((strstr(head, "Tooltip:") != NULL) ||
(strstr(head, "Tip:") != NULL))) {
bpymenu_set_tooltip(scriptMenu, middle);
}
parser_state = 0;
}
parser_state = 0;
}
break;

@ -59,6 +59,7 @@ typedef struct BPyMenu {
char *name;
char *filename;
char *tooltip;
unsigned short key, qual; /* Registered shortcut key */
short version; /* Blender version */
int dir; /* 0: default, 1: U.pythondir */
struct BPySubMenu *submenus;
@ -99,6 +100,7 @@ typedef enum {
PYMENU_UVCALCULATION,
PYMENU_ARMATURE,
PYMENU_SCRIPTTEMPLATE,
PYMENU_TEXTPLUGIN,
PYMENU_HELP,/*Main Help menu items - prob best to leave for 'official' ones*/
PYMENU_HELPSYSTEM,/* Resources, troubleshooting, system tools */
PYMENU_HELPWEBSITES,/* Help -> Websites submenu */

@ -34,9 +34,13 @@
#include "BKE_global.h"
#include "BKE_main.h"
#include "BIF_drawtext.h"
#include "BIF_screen.h"
#include "BKE_text.h"
#include "BKE_suggestions.h"
#include "BLI_blenlib.h"
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
#include "MEM_guardedalloc.h"
#include "gen_utils.h"
#include "gen_library.h"
#include "../BPY_extern.h"
@ -90,9 +94,20 @@ struct PyMethodDef M_Text_methods[] = {
static PyObject *Text_getFilename( BPy_Text * self );
static PyObject *Text_getNLines( BPy_Text * self );
static PyObject *Text_clear( BPy_Text * self );
static PyObject *Text_reset( BPy_Text * self );
static PyObject *Text_readline( BPy_Text * self );
static PyObject *Text_write( BPy_Text * self, PyObject * value );
static PyObject *Text_insert( BPy_Text * self, PyObject * value );
static PyObject *Text_delete( BPy_Text * self, PyObject * value );
static PyObject *Text_set( BPy_Text * self, PyObject * args );
static PyObject *Text_asLines( BPy_Text * self );
static PyObject *Text_asLines( BPy_Text * self, PyObject * args );
static PyObject *Text_getCursorPos( BPy_Text * self );
static PyObject *Text_setCursorPos( BPy_Text * self, PyObject * args );
static PyObject *Text_getSelectPos( BPy_Text * self );
static PyObject *Text_setSelectPos( BPy_Text * self, PyObject * args );
static PyObject *Text_markSelection( BPy_Text * self, PyObject * args );
static PyObject *Text_suggest( BPy_Text * self, PyObject * args );
static PyObject *Text_showDocs( BPy_Text * self, PyObject * args );
/*****************************************************************************/
/* Python BPy_Text methods table: */
@ -109,12 +124,34 @@ static PyMethodDef BPy_Text_methods[] = {
"(str) - Change Text Object name"},
{"clear", ( PyCFunction ) Text_clear, METH_NOARGS,
"() - Clear Text buffer"},
{"reset", ( PyCFunction ) Text_reset, METH_NOARGS,
"() - Moves the IO pointer back to the start of the Text buffer for reading"},
{"readline", ( PyCFunction ) Text_readline, METH_NOARGS,
"() - Reads a line of text from the buffer and returns it incrementing the internal IO pointer."},
{"write", ( PyCFunction ) Text_write, METH_O,
"(line) - Append string 'str' to Text buffer"},
{"insert", ( PyCFunction ) Text_insert, METH_O,
"(line) - Insert string 'str' to Text buffer at cursor location"},
{"delete", ( PyCFunction ) Text_delete, METH_O,
"(chars) - Deletes a number of characters to the left (chars<0) or right (chars>0)"},
{"set", ( PyCFunction ) Text_set, METH_VARARGS,
"(name, val) - Set attribute 'name' to value 'val'"},
{"asLines", ( PyCFunction ) Text_asLines, METH_NOARGS,
"() - Return text buffer as a list of lines"},
{"asLines", ( PyCFunction ) Text_asLines, METH_VARARGS,
"(start=0, end=nlines) - Return text buffer as a list of lines between start and end"},
{"getCursorPos", ( PyCFunction ) Text_getCursorPos, METH_NOARGS,
"() - Return cursor position as (row, col) tuple"},
{"setCursorPos", ( PyCFunction ) Text_setCursorPos, METH_VARARGS,
"(row, col) - Set the cursor position to (row, col)"},
{"getSelectPos", ( PyCFunction ) Text_getSelectPos, METH_NOARGS,
"() - Return the selection cursor position as (row, col) tuple"},
{"setSelectPos", ( PyCFunction ) Text_setSelectPos, METH_VARARGS,
"(row, col) - Set the selection cursor position to (row, col)"},
{"markSelection", ( PyCFunction ) Text_markSelection, METH_VARARGS,
"(group, (r, g, b), flags) - Places a marker over the current selection. Group: number > 0, flags: TMARK_TEMP, TMARK_EDITALL, etc."},
{"suggest", ( PyCFunction ) Text_suggest, METH_VARARGS,
"(list, prefix='') - Presents a list of suggestions. List is of strings, or tuples. Tuples must be of the form (name, type) where type is one of 'm', 'v', 'f', 'k' for module, variable, function and keyword respectively or '?' for other types"},
{"showDocs", ( PyCFunction ) Text_showDocs, METH_VARARGS,
"(docs) - Documentation string"},
{NULL, NULL, 0, NULL}
};
@ -302,7 +339,7 @@ static PyObject *M_Text_unlink( PyObject * self, PyObject * args )
/*****************************************************************************/
PyObject *Text_Init( void )
{
PyObject *submodule;
PyObject *submodule, *dict;
if( PyType_Ready( &Text_Type ) < 0 )
return NULL;
@ -310,6 +347,19 @@ PyObject *Text_Init( void )
submodule =
Py_InitModule3( "Blender.Text", M_Text_methods, M_Text_doc );
dict = PyModule_GetDict( submodule );
#define EXPP_ADDCONST(x) \
EXPP_dict_set_item_str(dict, #x, PyInt_FromLong(x))
/* So, for example:
* EXPP_ADDCONST(LEFTMOUSE) becomes
* EXPP_dict_set_item_str(dict, "LEFTMOUSE", PyInt_FromLong(LEFTMOUSE))
*/
EXPP_ADDCONST( TMARK_TEMP );
EXPP_ADDCONST( TMARK_EDITALL );
return ( submodule );
}
@ -327,6 +377,8 @@ PyObject *Text_CreatePyObject( Text * txt )
"couldn't create BPy_Text PyObject" );
pytxt->text = txt;
pytxt->iol = NULL;
pytxt->ioc = -1;
return ( PyObject * ) pytxt;
}
@ -376,25 +428,49 @@ static PyObject *Text_clear( BPy_Text * self)
Py_RETURN_NONE;
}
static PyObject *Text_set( BPy_Text * self, PyObject * args )
static PyObject *Text_reset( BPy_Text * self )
{
int ival;
char *attr;
if( !PyArg_ParseTuple( args, "si", &attr, &ival ) )
return EXPP_ReturnPyObjError( PyExc_TypeError,
"expected a string and an int as arguments" );
if( strcmp( "follow_cursor", attr ) == 0 ) {
if( ival )
self->text->flags |= EXPP_TEXT_MODE_FOLLOW;
else
self->text->flags &= EXPP_TEXT_MODE_FOLLOW;
}
self->iol = NULL;
self->ioc = -1;
Py_RETURN_NONE;
}
static PyObject *Text_readline( BPy_Text * self )
{
PyObject *tmpstr;
if( !self->text )
return EXPP_ReturnPyObjError( PyExc_RuntimeError,
"This object isn't linked to a Blender Text Object" );
/* Reset */
if (!self->iol && self->ioc == -1) {
self->iol = self->text->lines.first;
self->ioc = 0;
}
if (!self->iol) {
PyErr_SetString( PyExc_StopIteration, "End of buffer reached" );
return PyString_FromString( "" );
}
if (self->ioc > self->iol->len) {
self->iol = NULL;
return EXPP_ReturnPyObjError( PyExc_RuntimeError,
"Line length exceeded, text may have changed while reading" );
}
tmpstr = PyString_FromString( self->iol->line + self->ioc );
if (self->iol->next)
PyString_ConcatAndDel( &tmpstr, PyString_FromString("\n") );
self->iol = self->iol->next;
self->ioc = 0;
return tmpstr;
}
static PyObject *Text_write( BPy_Text * self, PyObject * value )
{
char *str = PyString_AsString(value);
@ -413,35 +489,324 @@ static PyObject *Text_write( BPy_Text * self, PyObject * value )
txt_move_eof( self->text, 0 );
txt_set_undostate( oldstate );
Text_reset( self );
Py_RETURN_NONE;
}
static PyObject *Text_asLines( BPy_Text * self )
static PyObject *Text_insert( BPy_Text * self, PyObject * value )
{
TextLine *line;
PyObject *list, *tmpstr;
char *str = PyString_AsString(value);
int oldstate;
if( !self->text )
return EXPP_ReturnPyObjError( PyExc_RuntimeError,
"This object isn't linked to a Blender Text Object" );
if( !str )
return EXPP_ReturnPyObjError( PyExc_TypeError,
"expected string argument" );
oldstate = txt_get_undostate( );
txt_insert_buf( self->text, str );
txt_set_undostate( oldstate );
Text_reset( self );
Py_RETURN_NONE;
}
static PyObject *Text_delete( BPy_Text * self, PyObject * value )
{
int num = PyInt_AsLong(value);
int oldstate;
if( !self->text )
return EXPP_ReturnPyObjError( PyExc_RuntimeError,
"This object isn't linked to a Blender Text Object" );
if( !num )
return EXPP_ReturnPyObjError( PyExc_TypeError,
"expected non-zero int argument" );
oldstate = txt_get_undostate( );
while (num<0) {
txt_backspace_char(self->text);
num++;
}
while (num>0) {
txt_delete_char(self->text);
num--;
}
txt_set_undostate( oldstate );
Text_reset( self );
Py_RETURN_NONE;
}
static PyObject *Text_set( BPy_Text * self, PyObject * args )
{
int ival;
char *attr;
if( !PyArg_ParseTuple( args, "si", &attr, &ival ) )
return EXPP_ReturnPyObjError( PyExc_TypeError,
"expected a string and an int as arguments" );
if( strcmp( "follow_cursor", attr ) == 0 ) {
if( ival )
self->text->flags |= EXPP_TEXT_MODE_FOLLOW;
else
self->text->flags &= EXPP_TEXT_MODE_FOLLOW;
}
Py_RETURN_NONE;
}
static PyObject *Text_asLines( BPy_Text * self, PyObject * args )
{
TextLine *line;
PyObject *list, *tmpstr;
int start=0, end=-1, i;
if( !self->text )
return EXPP_ReturnPyObjError( PyExc_RuntimeError,
"This object isn't linked to a Blender Text Object" );
if( !PyArg_ParseTuple( args, "|ii", &start, &end ) )
return EXPP_ReturnPyObjError( PyExc_TypeError,
"expected upto two optional ints as arguments" );
if (start<0)
start=0;
line = self->text->lines.first;
for (i = 0; i < start && line->next; i++)
line= line->next;
list = PyList_New( 0 );
if( !list )
return EXPP_ReturnPyObjError( PyExc_MemoryError,
"couldn't create PyList" );
while( line ) {
while( line && (i < end || end == -1) ) {
tmpstr = PyString_FromString( line->line );
PyList_Append( list, tmpstr );
Py_DECREF(tmpstr);
line = line->next;
i++;
}
return list;
}
static PyObject *Text_getCursorPos( BPy_Text * self )
{
Text *text;
TextLine *linep;
int row, col;
text = self->text;
if( !text )
return EXPP_ReturnPyObjError( PyExc_RuntimeError,
"This object isn't linked to a Blender Text Object" );
for (row=0,linep=text->lines.first; linep!=text->curl; linep=linep->next)
row++;
col= text->curc;
return Py_BuildValue( "ii", row, col );
}
static PyObject *Text_setCursorPos( BPy_Text * self, PyObject * args )
{
int row, col;
SpaceText *st;
if (!self->text)
return EXPP_ReturnPyObjError(PyExc_RuntimeError,
"This object isn't linked to a Blender Text Object");
if (!PyArg_ParseTuple(args, "ii", &row, &col))
return EXPP_ReturnPyObjError(PyExc_TypeError,
"expected two ints as arguments.");
if (row<0) row=0;
if (col<0) col=0;
txt_move_to(self->text, row, col, 0);
if (curarea->spacetype == SPACE_TEXT && (st=curarea->spacedata.first))
pop_space_text(st);
Py_RETURN_NONE;
}
static PyObject *Text_getSelectPos( BPy_Text * self )
{
Text *text;
TextLine *linep;
int row, col;
text = self->text;
if( !text )
return EXPP_ReturnPyObjError( PyExc_RuntimeError,
"This object isn't linked to a Blender Text Object" );
for (row=0,linep=text->lines.first; linep!=text->sell; linep=linep->next)
row++;
col= text->selc;
return Py_BuildValue( "ii", row, col );
}
static PyObject *Text_setSelectPos( BPy_Text * self, PyObject * args )
{
int row, col;
SpaceText *st;
if (!self->text)
return EXPP_ReturnPyObjError(PyExc_RuntimeError,
"This object isn't linked to a Blender Text Object");
if (!PyArg_ParseTuple(args, "ii", &row, &col))
return EXPP_ReturnPyObjError(PyExc_TypeError,
"expected two ints as arguments.");
if (row<0) row=0;
if (col<0) col=0;
txt_move_to(self->text, row, col, 1);
if (curarea->spacetype == SPACE_TEXT && (st=curarea->spacedata.first))
pop_space_text(st);
Py_RETURN_NONE;
}
static PyObject *Text_markSelection( BPy_Text * self, PyObject * args )
{
int group = 0, flags = 0,r, g, b;
Text *text;
char color[4];
text = self->text;
if (!text)
return EXPP_ReturnPyObjError(PyExc_RuntimeError,
"This object isn't linked to a Blender Text Object");
if (!PyArg_ParseTuple(args, "i(iii)i", &group, &r, &g, &b, &flags))
return EXPP_ReturnPyObjError(PyExc_TypeError,
"expected int, 3-tuple of ints and int as arguments.");
if (text->curl != text->sell)
return EXPP_ReturnPyObjError(PyExc_RuntimeError,
"Cannot mark multi-line selection.");
color[0] = (char) (r&0xFF);
color[1] = (char) (g&0xFF);
color[2] = (char) (b&0xFF);
color[3] = 255;
group &= 0xFFFF;
txt_add_marker(text, text->curl, text->curc, text->selc, color, group, flags);
Py_RETURN_NONE;
}
static PyObject *Text_suggest( BPy_Text * self, PyObject * args )
{
PyObject *item = NULL, *tup1 = NULL, *tup2 = NULL;
PyObject *list = NULL, *resl = NULL;
int list_len, i;
char *prefix = NULL, *name, type;
SpaceText *st;
if (!self->text)
return EXPP_ReturnPyObjError(PyExc_RuntimeError,
"This object isn't linked to a Blender Text Object");
/* Parse args for a list of strings/tuples */
if (!PyArg_ParseTuple(args, "O!|s", &PyList_Type, &list, &prefix))
return EXPP_ReturnPyObjError(PyExc_TypeError,
"expected list of strings or tuples followed by an optional string");
if (curarea->spacetype != SPACE_TEXT)
return EXPP_ReturnPyObjError(PyExc_RuntimeError,
"Active space type is not text");
st = curarea->spacedata.first;
if (!st || !st->text)
return EXPP_ReturnPyObjError(PyExc_RuntimeError,
"Active text area has no Text object");
texttool_suggest_clear();
texttool_text_set_active(st->text);
list_len = PyList_Size(list);
for (i = 0; i < list_len; i++) {
item = PyList_GetItem(list, i);
if (PyString_Check(item)) {
name = PyString_AsString(item);
type = '?';
} else if (PyTuple_Check(item) && PyTuple_GET_SIZE(item) == 2) {
tup1 = PyTuple_GetItem(item, 0);
tup2 = PyTuple_GetItem(item, 1);
if (PyString_Check(tup1) && PyString_Check(tup2)) {
name = PyString_AsString(tup1);
type = PyString_AsString(tup2)[0];
} else
return EXPP_ReturnPyObjError(PyExc_AttributeError,
"list must contain tuples of two strings only: (name, type)" );
} else
return EXPP_ReturnPyObjError(PyExc_AttributeError,
"list must contain only individual strings or tuples of size 2" );
if (!strlen(name) || (type!='m' && type!='v' && type!='f' && type!='k' && type!='?'))
return EXPP_ReturnPyObjError(PyExc_AttributeError,
"names must be non-empty and types in ['m', 'v', 'f', 'k', '?']" );
texttool_suggest_add(name, type);
}
if (!prefix)
prefix = "";
texttool_suggest_prefix(prefix);
scrarea_queue_redraw(curarea);
Py_RETURN_NONE;
}
static PyObject *Text_showDocs( BPy_Text * self, PyObject * args )
{
char *docs;
SpaceText *st;
if (!self->text)
return EXPP_ReturnPyObjError(PyExc_RuntimeError,
"This object isn't linked to a Blender Text Object");
if (!PyArg_ParseTuple(args, "s", &docs))
return EXPP_ReturnPyObjError( PyExc_TypeError,
"expected a string as argument" );
if (curarea->spacetype != SPACE_TEXT)
return EXPP_ReturnPyObjError(PyExc_RuntimeError,
"Active space type is not text");
st = curarea->spacedata.first;
if (!st || !st->text)
return EXPP_ReturnPyObjError(PyExc_RuntimeError,
"Active text area has no Text object");
texttool_text_set_active(st->text);
texttool_docs_show(docs);
scrarea_queue_redraw(curarea);
Py_RETURN_NONE;
}
/*****************************************************************************/
/* Function: Text_compare */
/* Description: This is a callback function for the BPy_Text type. It */

@ -41,6 +41,8 @@ extern PyTypeObject Text_Type;
typedef struct {
PyObject_HEAD
Text * text; /* libdata must be second */
TextLine * iol; /* current line being read or NULL if reset */
int ioc; /* character offset in line being read */
} BPy_Text;
PyObject *Text_Init( void );

@ -357,12 +357,14 @@ def PupTreeMenu( menu ):
"""
Create a popup menu tree.
Each item in the list is a menu item - (str, event), separator - None or submenu - (str, [...]).
Each item in the list is: a menu item - (str, event); a separator - None;
or submenu - (str, [...]).
Submenus list uses the same syntax as the menu list.
Submenus list uses the same syntax as the menu list. To add a title to the
main menu, end the first entry str with '%t' - the event is ignored.
Example::
result = Draw.PupTreeMenu( [ ("Menu Item 1", 10), ("Menu Item 2", 12), ("SubMenu", [("Menu Item 3", 100), ("MenuItem4", 101) ] ) ] )
result = Draw.PupTreeMenu( [ ("Title%t", 0), ("Menu Item 1", 10), ("Menu Item 2", 12), ("SubMenu", [("Menu Item 3", 100), ("MenuItem4", 101) ] ) ] )
@type menu: string
@param menu: A menu list

@ -100,6 +100,19 @@ class Text:
Clear this Text object: its buffer becomes empty.
"""
def reset():
"""
Reset the read IO pointer to the start of the buffer.
"""
def readline():
"""
Reads a line of text from the buffer from the current IO pointer
position to the end of the line. If the text has changed since the last
read, reset() *must* be called.
@rtype: string
"""
def set(attribute, value):
"""
Set this Text's attributes.
@ -118,12 +131,94 @@ class Text:
@param data: The string to append to the text buffer.
"""
def asLines():
def insert(data):
"""
Retrieve the contents of this Text buffer as a list of strings.
Inserts a string into this Text buffer at the cursor.
@type data: string
@param data: The string to insert into the text buffer.
"""
def asLines(start=0, end=-1):
"""
Retrieve the contents of this Text buffer as a list of strings between
the start and end lines specified. If end < 0 all lines from start will
be included.
@type start int
@param start: Optional index of first line of the span to return
@type end int
@param end: Optional index of the line to which the span is taken or
-1 to include all lines from start
@rtype: list of strings
@return: A list of strings, one for each line in the buffer
@return: A list of strings, one for each line in the buffer between
start and end.
"""
def getCursorPos():
"""
Retrieve the position of the cursor in this Text buffer.
@rtype: (int, int)
@return: A pair (row, col) indexing the line and character of the
cursor.
"""
def setCursorPos(row, col):
"""
Set the position of the cursor in this Text buffer. Any selection will
be cleared. Use setSelectPos to extend a selection from the point
specified here.
@type row: int
@param row: The index of the line in which to position the cursor.
@type col: int
@param col: The index of the character within the line to position the
cursor.
"""
def getSelectPos():
"""
Retrieve the position of the selection cursor in this Text buffer.
@rtype: (int, int)
@return: A pair (row, col) indexing the line and character of the
selection cursor.
"""
def setSelectPos(row, col):
"""
Set the position of the selection cursor in this Text buffer. This
method should be called after setCursorPos to extend the selection to
the specified point.
@type row: int
@param row: The index of the line in which to position the cursor.
@type col: int
@param col: The index of the character within the line to position the
cursor.
"""
def suggest(list, prefix=''):
"""
Suggest a list of names. If list is a list of tuples (name, type) the
list will be formatted to syntax-highlight each entry type. Types must
be strings in the list ['m', 'f', 'v', 'k', '?']. It is recommended that
the list be sorted, case-insensitively by name.
@type list: list of tuples or strings
@param list: List of pair-tuples of the form (name, type) where name is
the suggested name and type is one of 'm' (module or class), 'f'
(function or method), 'v' (variable), 'k' (keyword), '?' (other).
Lists of plain strings are also accepted where the type is always
'?'.
@type prefix: string
@param prefix: The optional prefix used to limit what is suggested from
the list. This is usually whatever precedes the cursor so that
backspace will update it.
"""
def showDocs(docs):
"""
Displays a word-wrapped message box containing the specified
documentation when this Text object is visible.
@type docs: string
@param docs: The documentation string to display.
"""
import id_generics
Text.__doc__ += id_generics.attributes
Text.__doc__ += id_generics.attributes

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -42,6 +42,7 @@
#include "BMF_Api.h"
#include "BIF_language.h"
#include "MEM_guardedalloc.h"
#include "BSE_headerbuttons.h"
@ -52,6 +53,7 @@
#include "DNA_constraint_types.h"
#include "DNA_action_types.h"
#include "BIF_gl.h" /* for glRasterPos2i */
#include "BIF_drawtext.h"
#include "BIF_interface.h"
#include "BIF_resources.h"
@ -128,7 +130,7 @@ void do_text_buttons(unsigned short event)
st->top= 0;
pop_space_text(st);
if (st->showsyntax) get_format_string(st);
if (st->showsyntax) txt_format_text(st);
allqueue(REDRAWTEXT, 0);
allqueue(REDRAWHEADERS, 0);
}
@ -194,14 +196,20 @@ void do_text_buttons(unsigned short event)
break;
case B_TAB_NUMBERS:
if (st->showsyntax) get_format_string(st);
if (st->showsyntax) txt_format_text(st);
allqueue(REDRAWTEXT, 0);
allqueue(REDRAWHEADERS, 0);
break;
case B_SYNTAX:
if (st->showsyntax) {
get_format_string(st);
}
if (st->showsyntax) txt_format_text(st);
allqueue(REDRAWTEXT, 0);
allqueue(REDRAWHEADERS, 0);
break;
case B_TEXTPLUGINS:
allqueue(REDRAWHEADERS, 0);
break;
case B_WORDWRAP:
st->left= 0;
allqueue(REDRAWTEXT, 0);
allqueue(REDRAWHEADERS, 0);
break;
@ -239,6 +247,37 @@ static uiBlock *text_template_scriptsmenu (void *args_unused)
return block;
}
static void do_text_plugin_scriptsmenu(void *arg, int event)
{
BPY_menu_do_python(PYMENU_TEXTPLUGIN, event);
allqueue(REDRAWIMAGE, 0);
}
static uiBlock *text_plugin_scriptsmenu (void *args_unused)
{
uiBlock *block;
BPyMenu *pym;
int i= 0;
short yco = 20, menuwidth = 120;
block= uiNewBlock(&curarea->uiblocks, "text_plugin_scriptsmenu", UI_EMBOSSP, UI_HELV, G.curscreen->mainwin);
uiBlockSetButmFunc(block, do_text_plugin_scriptsmenu, NULL);
/* note that we acount for the N previous entries with i+20: */
for (pym = BPyMenuTable[PYMENU_TEXTPLUGIN]; pym; pym = pym->next, i++) {
uiDefIconTextBut(block, BUTM, 1, ICON_PYTHON, pym->name, 0, yco-=20, menuwidth, 19,
NULL, 0.0, 0.0, 1, i,
pym->tooltip?pym->tooltip:pym->filename);
}
uiBlockSetDirection(block, UI_RIGHT);
uiTextBoundsBlock(block, 60);
return block;
}
/* action executed after clicking in File menu */
static void do_text_filemenu(void *arg, int event)
{
@ -268,7 +307,7 @@ static void do_text_filemenu(void *arg, int event)
if (!reopen_text(text)) {
error("Could not reopen file");
}
if (st->showsyntax) get_format_string(st);
if (st->showsyntax) txt_format_text(st);
}
break;
case 5:
@ -277,9 +316,14 @@ static void do_text_filemenu(void *arg, int event)
txt_write_file(text);
break;
case 6:
run_python_script(st);
text->flags |= TXT_ISMEM | TXT_ISDIRTY | TXT_ISTMP;
MEM_freeN(text->name);
text->name= NULL;
break;
case 7:
run_python_script(st);
break;
case 8:
{
Object *ob;
bConstraint *con;
@ -340,11 +384,17 @@ static void do_text_editmenu(void *arg, int event)
switch(event) {
case 1:
txt_do_undo(text);
pop_space_text(st);
break;
case 2:
txt_do_redo(text);
pop_space_text(st);
break;
case 3:
if (text && text->id.lib) {
error_libdata();
break;
}
txt_copy_clipboard(text);
txt_cut_sel(text);
pop_space_text(st);
@ -354,8 +404,12 @@ static void do_text_editmenu(void *arg, int event)
txt_copy_clipboard(text);
break;
case 5:
if (text && text->id.lib) {
error_libdata();
break;
}
txt_paste_clipboard(text);
if (st->showsyntax) get_format_string(st);
if (st->showsyntax) txt_format_text(st);
break;
case 6:
txt_print_cutbuffer();
@ -364,10 +418,11 @@ static void do_text_editmenu(void *arg, int event)
jumptoline_interactive(st);
break;
case 8:
txt_find_panel(st,1);
break;
case 9:
txt_find_panel(st,0);
find_and_replace(st, 0);
break;
case 10:
find_and_replace(st, 1);
break;
default:
break;
@ -443,6 +498,57 @@ static void do_text_editmenu_selectmenu(void *arg, int event)
}
}
/* action executed after clicking in Markers menu */
static void do_text_editmenu_markermenu(void *arg, int event)
{
SpaceText *st= curarea->spacedata.first; /* bad but cant pass as an arg here */
Text *text;
TextMarker *mrk;
ScrArea *sa;
int lineno;
if (st==NULL || st->spacetype != SPACE_TEXT) return;
text = st->text;
switch(event) {
case 1:
txt_clear_markers(text, 0, 0);
break;
case 2:
lineno= txt_get_span(text->lines.first, text->curl);
mrk= text->markers.first;
while (mrk && (mrk->lineno<lineno || (mrk->lineno==lineno && mrk->start <= text->curc)))
mrk= mrk->next;
if (!mrk) mrk= text->markers.first;
if (mrk) {
txt_move_to(text, mrk->lineno, mrk->start, 0);
txt_move_to(text, mrk->lineno, mrk->end, 1);
}
break;
case 3:
lineno= txt_get_span(text->lines.first, text->curl);
mrk= text->markers.last;
while (mrk && (mrk->lineno>lineno || (mrk->lineno==lineno && mrk->end > text->curc)))
mrk= mrk->prev;
if (!mrk) mrk= text->markers.last;
if (mrk) {
txt_move_to(text, mrk->lineno, mrk->start, 0);
txt_move_to(text, mrk->lineno, mrk->end, 1);
}
break;
default:
break;
}
for (sa= G.curscreen->areabase.first; sa; sa= sa->next) {
SpaceText *st= sa->spacedata.first;
if (st && st->spacetype==SPACE_TEXT) {
scrarea_queue_redraw(sa);
}
}
}
/* action executed after clicking in Format menu */
static void do_text_formatmenu(void *arg, int event)
{
@ -456,6 +562,10 @@ static void do_text_formatmenu(void *arg, int event)
switch(event) {
case 3:
if (text && text->id.lib) {
error_libdata();
break;
}
if (txt_has_sel(text)) {
txt_order_cursors(text);
indent(text);
@ -466,6 +576,10 @@ static void do_text_formatmenu(void *arg, int event)
break;
}
case 4:
if (text && text->id.lib) {
error_libdata();
break;
}
if ( txt_has_sel(text)) {
txt_order_cursors(text);
unindent(text);
@ -473,18 +587,26 @@ static void do_text_formatmenu(void *arg, int event)
}
break;
case 5:
if (text && text->id.lib) {
error_libdata();
break;
}
if ( txt_has_sel(text)) {
txt_order_cursors(text);
comment(text);
if (st->showsyntax) get_format_string(st);
if (st->showsyntax) txt_format_text(st);
break;
}
break;
case 6:
if (text && text->id.lib) {
error_libdata();
break;
}
if ( txt_has_sel(text)) {
txt_order_cursors(text);
uncomment(text);
if (st->showsyntax) get_format_string(st);
if (st->showsyntax) txt_format_text(st);
break;
}
break;
@ -536,6 +658,25 @@ static uiBlock *text_editmenu_selectmenu(void *arg_unused)
return block;
}
/* Select menu */
static uiBlock *text_editmenu_markermenu(void *arg_unused)
{
uiBlock *block;
short yco = 20, menuwidth = 120;
block= uiNewBlock(&curarea->uiblocks, "text_editmenu_markermenu", UI_EMBOSSP, UI_HELV, G.curscreen->mainwin);
uiBlockSetButmFunc(block, do_text_editmenu_markermenu, NULL);
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Clear All", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 1, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Next Marker", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 2, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Previous Marker", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 3, "");
uiBlockSetDirection(block, UI_RIGHT);
uiTextBoundsBlock(block, 60);
return block;
}
void do_text_formatmenu_convert(void *arg, int event)
{
SpaceText *st= curarea->spacedata.first; /* bad but cant pass as an arg here */
@ -649,10 +790,12 @@ static uiBlock *text_editmenu(void *arg_unused)
uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
uiDefIconTextBlockBut(block, text_editmenu_viewmenu, NULL, ICON_RIGHTARROW_THIN, "View|Alt Shift V ", 0, yco-=20, 120, 19, "");
uiDefIconTextBlockBut(block, text_editmenu_selectmenu, NULL, ICON_RIGHTARROW_THIN, "Select|Alt Shift S ", 0, yco-=20, 120, 19, "");
uiDefIconTextBlockBut(block, text_editmenu_markermenu, NULL, ICON_RIGHTARROW_THIN, "Markers", 0, yco-=20, 120, 19, "");
uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Jump...|Alt J", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 7, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Find...|Alt Ctrl F", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 8, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Find Again|Alt F", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 9, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Find And Replace...|Alt F", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 8, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Find Next|Alt F", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 9, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Replace|Alt H", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 10, "");
uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
uiDefIconTextBlockBut(block, text_editmenu_to3dmenu, NULL, ICON_RIGHTARROW_THIN, "Text to 3d Object", 0, yco-=20, 120, 19, "");
@ -690,17 +833,21 @@ static uiBlock *text_filemenu(void *arg_unused)
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Save|Alt S", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 4, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Save As...", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 5, "");
if (text->name)
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Make Internal", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 6, "");
uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Run Python Script|Alt P", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 6, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Run Python Script|Alt P", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 7, "");
if (BPY_is_pyconstraint(text))
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Refresh All PyConstraints", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 7, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Refresh All PyConstraints", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 8, "");
uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
}
uiDefIconTextBlockBut(block, text_template_scriptsmenu, NULL, ICON_RIGHTARROW_THIN, "Script Templates", 0, yco-=20, 120, 19, "");
uiDefIconTextBlockBut(block, text_plugin_scriptsmenu, NULL, ICON_RIGHTARROW_THIN, "Text Plugins", 0, yco-=20, 120, 19, "");
if(curarea->headertype==HEADERTOP) {
uiBlockSetDirection(block, UI_DOWN);
@ -715,13 +862,15 @@ static uiBlock *text_filemenu(void *arg_unused)
}
/* header */
#define PATH_MAX 260
void text_buttons(void)
{
uiBlock *block;
SpaceText *st= curarea->spacedata.first;
Text *text;
short xco, xmax;
char naam[256];
char naam[256], fname[PATH_MAX], headtxt[PATH_MAX+17];
int len;
if (st==NULL || st->spacetype != SPACE_TEXT) return;
@ -779,8 +928,9 @@ void text_buttons(void)
else uiDefIconBut(block, BUT,B_FULL, ICON_FULLSCREEN, xco,0,XIC,YIC, 0, 0, 0, 0, 0, "Makes current window full screen (CTRL+Down arrow)");
uiDefIconButI(block, ICONTOG, B_TEXTLINENUM, ICON_LONGDISPLAY, xco+=XIC,0,XIC,YIC, &st->showlinenrs, 0, 0, 0, 0, "Displays line numbers");
uiDefIconButI(block, ICONTOG, B_SYNTAX, ICON_SYNTAX, xco+=XIC,0,XIC,YIC, &st->showsyntax, 0, 0, 0, 0, "Enables Syntax Highlighting");
uiDefIconButI(block, ICONTOG, B_WORDWRAP, ICON_WORDWRAP, xco+=XIC,0,XIC,YIC, &st->wordwrap, 0, 0, 0, 0, "Enables word wrap");
uiDefIconButI(block, ICONTOG, B_SYNTAX, ICON_SYNTAX, xco+=XIC,0,XIC,YIC, &st->showsyntax, 0, 0, 0, 0, "Enables syntax highlighting");
uiDefIconButI(block, ICONTOG, B_TEXTPLUGINS, ICON_PYTHON, xco+=XIC,0,XIC,YIC, &st->doplugins, 0, 0, 0, 0, "Enables Python text plugins");
uiBlockEndAlign(block);
/* STD TEXT BUTTONS */
@ -804,9 +954,30 @@ void text_buttons(void)
uiDefButI(block, MENU, B_TEXTFONT, "Screen 12 %x0|Screen 15%x1", xco,0,100,YIC, &st->font_id, 0, 0, 0, 0, "Displays available fonts");
xco+=110;
uiDefButI(block, NUM, B_TAB_NUMBERS, "Tab:", xco, 0, XIC+50, YIC, &st->tabnumber, 2, 8, 0, 0, "Set spacing of Tab");
uiDefButI(block, NUM, B_TAB_NUMBERS, "Tab:", xco, 0, XIC+50, YIC, &st->tabnumber, 2, 8, 0, 0, "Set spacing of Tab");
xco+= XIC+50;
/* File info */
if (text) {
if (text->name) {
len = strlen(text->name);
if (len > PATH_MAX-1)
len = PATH_MAX-1;
strncpy(fname, text->name, len);
fname[len]='\0';
if (text->flags & TXT_ISDIRTY)
sprintf(headtxt, "File: *%s (unsaved)", fname);
else
sprintf(headtxt, "File: %s", fname);
} else {
sprintf(headtxt, text->id.lib?"Text: External":"Text: Internal");
}
BIF_ThemeColor(TH_MENU_TEXT);
glRasterPos2i(xco+=XIC, 5);
BMF_DrawString(G.font, headtxt);
xco += BMF_GetStringWidth(G.font, headtxt);
}
/* always as last */
curarea->headbutlen= xco+2*XIC;

@ -27,6 +27,11 @@
* ***** END GPL LICENSE BLOCK *****
*/
#include "stdio.h"
#include "ctype.h"
#include "string.h"
#include "BKE_global.h"
#include "BLI_blenlib.h"
#include "BLI_arithb.h"
#include "BIF_keyval.h"
@ -349,3 +354,193 @@ char *key_event_to_string(unsigned short event)
return "";
}
/*
* Decodes key combination strings [qual1+[qual2+[...]]]keyname
* The '+'s may be replaced by '-' or ' ' characters to support different
* formats. No additional whitespace is allowed. The keyname may be an internal
* name, like "RETKEY", or a more common name, like "Return". Decoding is case-
* insensitive.
*
* Example strings: "Ctrl+L", "ALT-ESC", "Shift A"
*
* Returns 1 if successful.
*/
int decode_key_string(char *str, unsigned short *key, unsigned short *qual)
{
int i, prev, len, invalid=0;
len= strlen(str);
*key= *qual= 0;
/* Convert to upper case */
for (i=0; i<len; i++) {
str[i]= toupper(str[i]);
}
/* Handle modifiers */
for (prev=i=0; i<len; i++) {
if (str[i]==' ' || str[i]=='+' || str[i]=='-') {
if (!strncmp(str+prev, "CTRL", i-prev)) *qual |= LR_CTRLKEY;
else if (!strncmp(str+prev, "ALT", i-prev)) *qual |= LR_ALTKEY;
else if (!strncmp(str+prev, "SHIFT", i-prev)) *qual |= LR_SHIFTKEY;
else if (!strncmp(str+prev, "COMMAND", i-prev)) *qual |= LR_COMMANDKEY;
prev=i+1;
}
}
/* Compare last part against key names */
if (len-prev==1 || len-prev==4 && !strncmp(str+prev, "KEY", 3)) {
if (str[prev]>='A' && str[prev]<='Z') {
*key= str[prev]-'A'+AKEY;
} else if (str[prev]>='0' && str[prev]<='9') {
*key= str[prev]-'0'+ZEROKEY;
} else {
invalid= 1;
}
} else if (!strncmp(str+prev, "ZEROKEY", len-prev) || !strncmp(str+prev, "ZERO", len-prev)) {
*key= ZEROKEY;
} else if (!strncmp(str+prev, "ONEKEY", len-prev) || !strncmp(str+prev, "ONE", len-prev)) {
*key= ONEKEY;
} else if (!strncmp(str+prev, "TWOKEY", len-prev) || !strncmp(str+prev, "TWO", len-prev)) {
*key= TWOKEY;
} else if (!strncmp(str+prev, "THREEKEY", len-prev) || !strncmp(str+prev, "THREE", len-prev)) {
*key= THREEKEY;
} else if (!strncmp(str+prev, "FOURKEY", len-prev) || !strncmp(str+prev, "FOUR", len-prev)) {
*key= FOURKEY;
} else if (!strncmp(str+prev, "FIVEKEY", len-prev) || !strncmp(str+prev, "FIVE", len-prev)) {
*key= FIVEKEY;
} else if (!strncmp(str+prev, "SIZEKEY", len-prev) || !strncmp(str+prev, "SIX", len-prev)) {
*key= SIXKEY;
} else if (!strncmp(str+prev, "SEVENKEY", len-prev) || !strncmp(str+prev, "SEVEN", len-prev)) {
*key= SEVENKEY;
} else if (!strncmp(str+prev, "EIGHTKEY", len-prev) || !strncmp(str+prev, "EIGHT", len-prev)) {
*key= EIGHTKEY;
} else if (!strncmp(str+prev, "NINEKEY", len-prev) || !strncmp(str+prev, "NINE", len-prev)) {
*key= NINEKEY;
} else if (!strncmp(str+prev, "ESCKEY", len-prev) || !strncmp(str+prev, "ESC", len-prev)) {
*key= ESCKEY;
} else if (!strncmp(str+prev, "TABKEY", len-prev) || !strncmp(str+prev, "TAB", len-prev)) {
*key= TABKEY;
} else if (!strncmp(str+prev, "RETKEY", len-prev) || !strncmp(str+prev, "RETURN", len-prev) || !strncmp(str+prev, "ENTER", len-prev)) {
*key= RETKEY;
} else if (!strncmp(str+prev, "SPACEKEY", len-prev) || !strncmp(str+prev, "SPACE", len-prev)) {
*key= SPACEKEY;
} else if (!strncmp(str+prev, "LINEFEEDKEY", len-prev) || !strncmp(str+prev, "LINEFEED", len-prev)) {
*key= LINEFEEDKEY;
} else if (!strncmp(str+prev, "BACKSPACEKEY", len-prev) || !strncmp(str+prev, "BACKSPACE", len-prev)) {
*key= BACKSPACEKEY;
} else if (!strncmp(str+prev, "DELKEY", len-prev) || !strncmp(str+prev, "DELETE", len-prev)) {
*key= DELKEY;
} else if (!strncmp(str+prev, "SEMICOLONKEY", len-prev) || !strncmp(str+prev, "SEMICOLON", len-prev)) {
*key= SEMICOLONKEY;
} else if (!strncmp(str+prev, "PERIODKEY", len-prev) || !strncmp(str+prev, "PERIOD", len-prev)) {
*key= PERIODKEY;
} else if (!strncmp(str+prev, "COMMAKEY", len-prev) || !strncmp(str+prev, "COMMA", len-prev)) {
*key= COMMAKEY;
} else if (!strncmp(str+prev, "QUOTEKEY", len-prev) || !strncmp(str+prev, "QUOTE", len-prev)) {
*key= QUOTEKEY;
} else if (!strncmp(str+prev, "ACCENTGRAVEKEY", len-prev) || !strncmp(str+prev, "ACCENTGRAVE", len-prev)) {
*key= ACCENTGRAVEKEY;
} else if (!strncmp(str+prev, "MINUSKEY", len-prev) || !strncmp(str+prev, "MINUS", len-prev)) {
*key= MINUSKEY;
} else if (!strncmp(str+prev, "SLASHKEY", len-prev) || !strncmp(str+prev, "SLASH", len-prev)) {
*key= SLASHKEY;
} else if (!strncmp(str+prev, "BACKSLASHKEY", len-prev) || !strncmp(str+prev, "BACKSLASH", len-prev)) {
*key= BACKSLASHKEY;
} else if (!strncmp(str+prev, "EQUALKEY", len-prev) || !strncmp(str+prev, "EQUAL", len-prev)) {
*key= EQUALKEY;
} else if (!strncmp(str+prev, "LEFTBRACKETKEY", len-prev) || !strncmp(str+prev, "LEFTBRACKET", len-prev)) {
*key= LEFTBRACKETKEY;
} else if (!strncmp(str+prev, "RIGHTBRACKETKEY", len-prev) || !strncmp(str+prev, "RIGHTBRACKET", len-prev)) {
*key= RIGHTBRACKETKEY;
} else if (!strncmp(str+prev, "DELKEY", len-prev) || !strncmp(str+prev, "DELETE", len-prev)) {
*key= DELKEY;
} else if (!strncmp(str+prev, "LEFTARROWKEY", len-prev) || !strncmp(str+prev, "LEFTARROW", len-prev)) {
*key= LEFTARROWKEY;
} else if (!strncmp(str+prev, "DOWNARROWKEY", len-prev) || !strncmp(str+prev, "DOWNARROW", len-prev)) {
*key= DOWNARROWKEY;
} else if (!strncmp(str+prev, "RIGHTARROWKEY", len-prev) || !strncmp(str+prev, "RIGHTARROW", len-prev)) {
*key= RIGHTARROWKEY;
} else if (!strncmp(str+prev, "UPARROWKEY", len-prev) || !strncmp(str+prev, "UPARROW", len-prev)) {
*key= UPARROWKEY;
} else if (!strncmp(str+prev, "PAD", 3)) {
if (len-prev<=4) {
if (str[prev]>='0' && str[prev]<='9') {
*key= str[prev]-'0'+ZEROKEY;
} else {
invalid= 1;
}
} else if (!strncmp(str+prev+3, "PERIODKEY", len-prev-3) || !strncmp(str+prev+3, "PERIOD", len-prev-3)) {
*key= PADPERIOD;
} else if (!strncmp(str+prev+3, "SLASHKEY", len-prev-3) || !strncmp(str+prev+3, "SLASH", len-prev-3)) {
*key= PADSLASHKEY;
} else if (!strncmp(str+prev+3, "ASTERKEY", len-prev-3) || !strncmp(str+prev+3, "ASTERISK", len-prev-3)) {
*key= PADASTERKEY;
} else if (!strncmp(str+prev+3, "MINUSKEY", len-prev-3) || !strncmp(str+prev+3, "MINUS", len-prev-3)) {
*key= PADMINUS;
} else if (!strncmp(str+prev+3, "ENTERKEY", len-prev-3) || !strncmp(str+prev+3, "ENTER", len-prev-3)) {
*key= PADENTER;
} else if (!strncmp(str+prev+3, "PLUSKEY", len-prev-3) || !strncmp(str+prev+3, "PLUS", len-prev-3)) {
*key= PADPLUSKEY;
} else {
invalid= 1;
}
} else if (!strncmp(str+prev, "F1KEY", len-prev) || !strncmp(str+prev, "F1", len-prev)) {
*key= F1KEY;
} else if (!strncmp(str+prev, "F2KEY", len-prev) || !strncmp(str+prev, "F2", len-prev)) {
*key= F2KEY;
} else if (!strncmp(str+prev, "F3KEY", len-prev) || !strncmp(str+prev, "F3", len-prev)) {
*key= F3KEY;
} else if (!strncmp(str+prev, "F4KEY", len-prev) || !strncmp(str+prev, "F4", len-prev)) {
*key= F4KEY;
} else if (!strncmp(str+prev, "F5KEY", len-prev) || !strncmp(str+prev, "F5", len-prev)) {
*key= F5KEY;
} else if (!strncmp(str+prev, "F6KEY", len-prev) || !strncmp(str+prev, "F6", len-prev)) {
*key= F6KEY;
} else if (!strncmp(str+prev, "F7KEY", len-prev) || !strncmp(str+prev, "F7", len-prev)) {
*key= F7KEY;
} else if (!strncmp(str+prev, "F8KEY", len-prev) || !strncmp(str+prev, "F8", len-prev)) {
*key= F8KEY;
} else if (!strncmp(str+prev, "F9KEY", len-prev) || !strncmp(str+prev, "F9", len-prev)) {
*key= F9KEY;
} else if (!strncmp(str+prev, "F10KEY", len-prev) || !strncmp(str+prev, "F10", len-prev)) {
*key= F10KEY;
} else if (!strncmp(str+prev, "F11KEY", len-prev) || !strncmp(str+prev, "F11", len-prev)) {
*key= F11KEY;
} else if (!strncmp(str+prev, "F12KEY", len-prev) || !strncmp(str+prev, "F12", len-prev)) {
*key= F12KEY;
} else if (!strncmp(str+prev, "PAUSEKEY", len-prev) || !strncmp(str+prev, "PAUSE", len-prev)) {
*key= PAUSEKEY;
} else if (!strncmp(str+prev, "INSERTKEY", len-prev) || !strncmp(str+prev, "INSERT", len-prev)) {
*key= INSERTKEY;
} else if (!strncmp(str+prev, "HOMEKEY", len-prev) || !strncmp(str+prev, "HOME", len-prev)) {
*key= HOMEKEY;
} else if (!strncmp(str+prev, "PAGEUPKEY", len-prev) || !strncmp(str+prev, "PAGEUP", len-prev)) {
*key= PAGEUPKEY;
} else if (!strncmp(str+prev, "PAGEDOWNKEY", len-prev) || !strncmp(str+prev, "PAGEDOWN", len-prev)) {
*key= PAGEDOWNKEY;
} else if (!strncmp(str+prev, "ENDKEY", len-prev) || !strncmp(str+prev, "END", len-prev)) {
*key= ENDKEY;
} else {
invalid= 1;
}
if (!invalid && *key) {
return 1;
}
return 0;
}

@ -6060,6 +6060,10 @@ static void init_textspace(ScrArea *sa)
st->lheight= 12;
st->showlinenrs= 0;
st->tabnumber = 4;
st->showsyntax= 0;
st->doplugins= 0;
st->overwrite= 0;
st->wordwrap= 0;
st->currtab_set = 0;
st->top= 0;

@ -2282,7 +2282,7 @@ void toolbox_generic( TBitem *generic_menu )
uiBlock *block;
uiBut *but;
TBitem *menu;
int dx=96;
int dx=96, first=1, len;
short event, mval[2];
intptr_t ypos = -5;
@ -2303,11 +2303,17 @@ void toolbox_generic( TBitem *generic_menu )
/* Add the menu */
for (menu = generic_menu; menu->icon != -1; menu++) {
if(strcmp(menu->name, "SEPR")==0) {
if (first && (len=strlen(menu->name)) > 2 && menu->name[len-2]=='%' && menu->name[len-1]=='t') {
menu->name[len-2] = '\0';
uiSetCurFont(block, UI_HELVB);
uiDefIconTextBut(block, LABEL, 0, ICON_BLANK1, menu->name, mval[0]+tb_mainx,mval[1]+tb_mainy+ypos+5, dx, 19, NULL, 0.0, 0.0, 0, 0, "");
uiSetCurFont(block, UI_HELV);
ypos-=20;
} else if(strcmp(menu->name, "SEPR")==0) {
uiDefBut(block, SEPR, 0, "", mval[0]+tb_mainx,mval[1]+tb_mainy+ypos+5, dx, 6, NULL, 0.0, 0.0, 0, 0, "");
ypos-=6;
} else {
if (menu->poin) {
if (menu->poin) {
but=uiDefIconTextBlockBut(block, tb_makemenu, menu->poin, ICON_RIGHTARROW_THIN, menu->name, mval[0]+tb_mainx,mval[1]+tb_mainy+ypos+5, dx, 19, "");
uiButSetFlag(but, UI_MAKE_RIGHT);
@ -2318,6 +2324,7 @@ void toolbox_generic( TBitem *generic_menu )
}
ypos-=20;
}
first= 0;
}
uiBlockSetButmFunc(block, menu->poin, NULL);

@ -69,6 +69,7 @@
#include "DNA_sound_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "DNA_text_types.h"
#include "BKE_blender.h"
#include "BKE_curve.h"
@ -81,6 +82,7 @@
#include "BKE_mball.h"
#include "BKE_node.h"
#include "BKE_packedFile.h"
#include "BKE_suggestions.h"
#include "BKE_texture.h"
#include "BKE_utildefines.h"
#include "BKE_pointcache.h"
@ -1107,6 +1109,7 @@ void exit_usiblender(void)
free_actcopybuf();
free_vertexpaint();
free_imagepaint();
free_texttools();
/* editnurb can remain to exist outside editmode */
freeNurblist(&editNurb);