manuskript/manuskript/ui/editors/t2tFunctions.py
2016-02-06 08:38:33 +01:00

371 lines
10 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from qt import *
import re
def t2tFormatSelection(editor, style):
"""
Formats the current selection of ``editor`` in the format given by ``style``,
style being:
0: bold
1: italic
2: underline
3: strike
4: code
5: tagged
"""
print("Formatting:", style)
formatChar = "*/_-`'"[style]
# FIXME: doesn't work if selection spans over several blocks.
cursor = editor.textCursor()
cursor.beginEditBlock()
if cursor.hasSelection():
pass
else:
# If no selection, selects the word in which the cursor is now
cursor.movePosition(QTextCursor.StartOfWord,
QTextCursor.MoveAnchor)
cursor.movePosition(QTextCursor.EndOfWord,
QTextCursor.KeepAnchor)
if not cursor.hasSelection():
# No selection means we are outside a word,
# so we insert markup and put cursor in the middle
cursor.insertText(formatChar * 4)
cursor.setPosition(cursor.position() - 2)
cursor.endEditBlock()
editor.setTextCursor(cursor)
# And we are done
return
# Get start and end of selection
start = cursor.selectionStart() - cursor.block().position()
end = cursor.selectionEnd() - cursor.block().position()
if start > end:
start, end = end, start
# Whole block
text = cursor.block().text()
# Adjusts selection to exclude the markup
while text[start:start + 1] == formatChar:
start += 1
while text[end - 1:end] ==formatChar:
end -= 1
# Get the text without formatting, and the array of format
fText, fArray = textToFormatArrayNoMarkup(text)
# Get the translated start and end of selection in the unformated text
tStart, tEnd = translateSelectionToUnformattedText(text, start, end)
# We want only the array that contains the propper formatting
propperArray = fArray[style]
if 0 in propperArray[tStart:tEnd]:
# have some unformated text in the selection, so we format the
# whole selection
propperArray = propperArray[:tStart] + [1] * \
(tEnd - tStart) + propperArray[tEnd:]
else:
# The whole selection is already formatted, so we remove the
# formatting
propperArray = propperArray[:tStart] + [0] * \
(tEnd - tStart) + propperArray[tEnd:]
fArray = fArray[0:style] + [propperArray] + fArray[style + 1:]
text = reformatText(fText, fArray)
# Replaces the whole block
cursor.movePosition(QTextCursor.StartOfBlock)
cursor.movePosition(QTextCursor.EndOfBlock,
QTextCursor.KeepAnchor)
cursor.insertText(text)
cursor.setPosition(end + cursor.block().position())
cursor.endEditBlock()
editor.setTextCursor(cursor)
def t2tClearFormat(editor):
"Clears format on ``editor``'s current selection."
cursor = editor.textCursor()
cursor.beginEditBlock()
text = cursor.selectedText()
t, a = textToFormatArrayNoMarkup(text)
cursor.insertText(t)
cursor.endEditBlock()
editor.setTextCursor(cursor)
def textToFormatArray(text):
"""
Take some text and returns an array of array containing informations
about how the text is formatted:
r = [ [0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1],
...
]
Each sub-array is for one of the beautifier:
0: bold
1: italic
2: underline
3: strike
4: code
5: tagged
Each sub-array contains an element for each character of the text, with the
value 1 if it is formatted in the specific format, -1 if it is markup, and
0 otherwise.
removeMarks returns a both the array and a string, in which all of the
formatting marks have been removed.
"""
result = []
for markup in ["\*", "/", "_", "-", "`", "\'"]:
rList = []
r = QRegExp(r'(' + markup * 2 + ')(.+)(' + markup * 2 + ')')
r.setMinimal(True)
pos = r.indexIn(text, 0)
lastPos = 0
while pos >= 0:
#We have a winner
rList += [0] * (pos - lastPos)
rList += [2] * 2
rList += [1] * len(r.cap(2))
rList += [2] * 2
lastPos = pos + len(r.cap(0))
pos = r.indexIn(text, len(rList))
if len(rList) < len(text):
rList += [0] * (len(text) - len(rList))
result.append(rList)
return result
def textToFormatArrayNoMarkup(text):
"""
Same as textToFormatArray, except that it removes all the markup from the
text and returns two elements:
the array
the text without markup
"""
r = textToFormatArray(text)
result = [[], [], [], [], [], []]
rText = ""
for i in range(len(text)):
t = max([k[i] for k in r]) # kind of flattens all the format array
if t != 2:
rText += text[i]
[result[k].append(r[k][i]) for k in range(len(r))]
return rText, result
def translateSelectionToUnformattedText(text, start, end):
"""
Translate the start / end of selection from a formatted text to an
unformatted one.
"""
r = textToFormatArray(text)
rStart, rEnd = start, end
for i in range(len(text)):
t = max([k[i] for k in r]) # kind of flattens all the format array
if t == 2: # t == 2 means this character is markup
if i <= start: rStart -= 1
if i < end: rEnd -= 1
return rStart, rEnd
def translateSelectionToFormattedText(text, start, end):
"""
Translate the start / end of selection from a formatted text to an
unformatted one.
"""
r = textToFormatArray(text)
rStart, rEnd = start, end
for i in range(len(text)):
t = max([k[i] for k in r]) # kind of flattens all the format array
if t == 2: # t == 2 means this character is markup
if i <= start: rStart -= 1
if i < end: rEnd -= 1
return rStart, rEnd
def printArray(array):
print(("".join([str(j) for j in array])))
def printArrays(arrays):
for i in arrays: printArray(i)
def reformatText(text, markupArray):
"""
Takes a text without formatting markup, and an array generated by
textToFormatArray, and adds the propper markup.
"""
rText = ""
markup = ["**", "//", "__", "--", "``", "''"]
for k, m in enumerate(markupArray):
#m = markupArray[k]
_open = False # Are we in an _openned markup
d = 0
alreadySeen = []
for i, t in enumerate(text):
insert = False
if not _open and m[i] == 1:
insert = True
_open = True
if _open and m[i] == 0:
insert = True
_open = False
if _open and m[i] > 1:
z = i
while m[z] == m[i]: z += 1
if m[z] != 1 and not m[i] in alreadySeen:
insert = True
_open = False
alreadySeen.append(m[i])
if insert:
rText += markup[k]
for j, m2 in enumerate(markupArray):
# The other array still have the same length
if j > k:
#Insert 2 for bold, 3 for italic, etc.
m2.insert(i + d, k + 2)
m2.insert(i + d, k + 2)
alreadySeen = []
d += 2
rText += t
if _open:
rText += markup[k]
for j, m2 in enumerate(markupArray):
# The other array still have the same length
if j > k:
#Insert 2 for bold, 3 for italic, etc.
m2.insert(i + d, k + 2)
m2.insert(i + d, k + 2)
text = rText
rText = ""
## Clean up
# Exclude first and last space of the markup
for markup in ["\*", "/", "_", "-", "`", "\'"]:
#r = QRegExp(r'(' + markup * 2 + ')(\s+)(.+)(' + markup * 2 + ')')
#r.setMinimal(True)
#text.replace(r, "\\2\\1\\3\\4")
text = re.sub(r'(' + markup * 2 + ')(\s+?)(.+?)(' + markup * 2 + ')',
"\\2\\1\\3\\4",
text)
#r = QRegExp(r'(' + markup * 2 + ')(.+)(\s+)(' + markup * 2 + ')')
#r.setMinimal(True)
#text.replace(r, "\\1\\2\\4\\3")
text = re.sub(r'(' + markup * 2 + ')(.+?)(\s+?)(' + markup * 2 + ')',
"\\1\\2\\4\\3",
text)
return text
def cleanFormat(text):
"Makes markup clean (removes doubles, etc.)"
t, a = textToFormatArrayNoMarkup(text)
return reformatText(t, a)
class State:
NORMAL = 0
TITLE_1 = 1
TITLE_2 = 2
TITLE_3 = 3
TITLE_4 = 4
TITLE_5 = 5
NUMBERED_TITLE_1 = 6
NUMBERED_TITLE_2 = 7
NUMBERED_TITLE_3 = 8
NUMBERED_TITLE_4 = 9
NUMBERED_TITLE_5 = 10
TITLES = [TITLE_1, TITLE_2, TITLE_3, TITLE_4, TITLE_5, NUMBERED_TITLE_1,
NUMBERED_TITLE_2, NUMBERED_TITLE_3, NUMBERED_TITLE_4,
NUMBERED_TITLE_5]
# AREA
COMMENT_AREA = 11
CODE_AREA = 12
RAW_AREA = 13
TAGGED_AREA = 14
# AREA MARKUP
COMMENT_AREA_BEGINS = 15
COMMENT_AREA_ENDS = 16
CODE_AREA_BEGINS = 17
CODE_AREA_ENDS = 18
RAW_AREA_BEGINS = 19
RAW_AREA_ENDS = 20
TAGGED_AREA_BEGINS = 21
TAGGED_AREA_ENDS = 22
#LINE
COMMENT_LINE = 30
CODE_LINE = 31
RAW_LINE = 32
TAGGED_LINE = 33
SETTINGS_LINE = 34
BLOCKQUOTE_LINE = 35
HORIZONTAL_LINE = 36
HEADER_LINE = 37
# LIST
LIST_BEGINS = 40
LIST_ENDS = 41
LIST_EMPTY = 42
LIST_BULLET = 43
LIST_BULLET_ENDS = 44
LIST = [40, 41, 42] + list(range(100, 201))
# TABLE
TABLE_LINE = 50
TABLE_HEADER = 51
#OTHER
MARKUP = 60
LINKS = 61
MACRO = 62
DEFAULT = 63
@staticmethod
def titleLevel(state):
"""
Returns the level of the title, from the block state.
"""
return {
State.TITLE_1: 1,
State.TITLE_2: 2,
State.TITLE_3: 3,
State.TITLE_4: 4,
State.TITLE_5: 5,
State.NUMBERED_TITLE_1: 1,
State.NUMBERED_TITLE_2: 2,
State.NUMBERED_TITLE_3: 3,
State.NUMBERED_TITLE_4: 4,
State.NUMBERED_TITLE_5: 5,
}.get(state, -1)