Difference between revisions of "Bullets and numbered lists"

From Scribus Wiki
Jump to: navigation, search
m (Caveats)
(Caveats)
Line 21: Line 21:
  
 
"""
 
"""
Insert marks or numbers in front selected paragraphs.
+
Insert marks, numbers or letters in front selected paragraphs.
 
Author: prof. MS. José Antonio Meira da Rocha
 
Author: prof. MS. José Antonio Meira da Rocha
 
mailto:joseantoniomeira@gmail.com
 
mailto:joseantoniomeira@gmail.com
 
http://meiradarocha.jor.br
 
http://meiradarocha.jor.br
 
License GPL 2.0
 
License GPL 2.0
2011-01-14a
+
2011-01-15a
  
======
 
 
Usage:
 
Usage:
Select paragraphs at any points. Run script.  
+
======
Response 'yes' will clear all numbers. Any other response
+
Select paragraphs at any points. Dialog will ask mark type.
will create numbered list.
+
Input:
 +
* "*" will create bulleted list.
 +
* A number (up 3 digit) will create a numbered list begining with such number. Ex.: "1)"
 +
* One or two letters uppper or lower case will create a lettered list begining with such letters. Ex.: "a)"
 +
* "/" will clear all marks.
 +
* An optional character after numbers or letters will be used as separator. Default: dot.
  
 
Caveats:
 
Caveats:
 
* Cleans all text format of text selection (font, bold, subscript, etc).
 
* Cleans all text format of text selection (font, bold, subscript, etc).
* Creates paragraph style "Numbered List" or "Bullet", if it doesn't exist, with proper indentation. Tabulation need to be defined by user to identical identation value.
+
* Creates paragraph style "Numbered List", "Lettered list" or "Bullet", if it doesn't exist, with proper indentation. Tabulation need to be defined by user to identical identation value.
* If there is no selection, will apply bullets to all text in current frame.
+
* If there is no selection, script will apply marks to all text in current frame.
 
"""
 
"""
  
Line 55: Line 59:
 
# to match your needs
 
# to match your needs
 
#######################
 
#######################
listStyle = 'Numbered list'
+
numberedListStyle = 'Numbered list'
 +
letteredListStyle = 'Lettered list'
 
bulletStyle = 'Bullet'
 
bulletStyle = 'Bullet'
 
defaultStyle = 'Default Paragraph Style'
 
defaultStyle = 'Default Paragraph Style'
 
separator = '.'
 
separator = '.'
indent = 18 # points
+
indent = 22 # points
 
# Put your favorite unicode char and tab: '\u2022\t'  
 
# Put your favorite unicode char and tab: '\u2022\t'  
 
# Get hex code from "Insert > Special character..." palette
 
# Get hex code from "Insert > Special character..." palette
Line 82: Line 87:
  
 
markOperation = ''
 
markOperation = ''
bulletOperation = 'B'
+
bulletOperation = '*'
 
numberOperation = '1'
 
numberOperation = '1'
cleanOperation = 'X'
+
cleanOperation = '/'
defaultResponse = bulletOperation
+
alphaOperation = 'Aa'
askOperationMsg = "<h2>What mark operation?</h2>" \
+
defaultOperation = bulletOperation
         +'<ul><li>"<b>'+bulletOperation.lower()+'</b>" input will create <b>bullet</b> marks.</li>' \
+
alphabet = 'abcdefghijklmnopqrstuvwxyz'
 +
alphabetLen = len(alphabet)
 +
 
 +
askOperationMsg = '<h2>What mark operation?</h2>' \
 +
         +'<ul><li>"<b>'+bulletOperation.lower()+'</b>" input will create <b>bullet</b> marks.' \
 
         +'and apply <i>'+bulletStyle+'</i>&nbsp;&nbsp;style.' \
 
         +'and apply <i>'+bulletStyle+'</i>&nbsp;&nbsp;style.' \
         +'<li><b>Any number</b> input will create <b>numbered list</b>  <br>starting with such number ' \
+
         +'<li>A <b>number</b> input will create <b>numbered list</b>  starting <br>with such number and apply <i>'+numberedListStyle+'</i>&nbsp;&nbsp;style.' \
         +'and apply <i>'+listStyle+'</i>&nbsp;&nbsp;style.' \
+
        +'<li>One or two <b>letters</b> input, upper or lower case, <br>will create <b>lettered list</b> starting with such letters,<br>' \
         +'<br><font color="#0000ff">Optional last char will be used as separator.<br>Default: dot. Example:  "<b>1)</b>"</font>' \
+
         +'and apply <i>'+letteredListStyle+'</i>&nbsp;&nbsp;style.' \
 +
         +'<li><font color="#0000ff">Optional char after number or letter<br> will be used as separator. Default: dot. Ex.:  "<b>a)</b>"</font>' \
 
         +'<li>"<b>'+cleanOperation.lower()+'</b>" input will <b>delete</b> marks ' \
 
         +'<li>"<b>'+cleanOperation.lower()+'</b>" input will <b>delete</b> marks ' \
 
         +'and apply <br><i>'+defaultStyle+'</i>&nbsp;&nbsp;style.</li>' \
 
         +'and apply <br><i>'+defaultStyle+'</i>&nbsp;&nbsp;style.</li>' \
 
         +'<li>Any <b>other</b> input will <b>cancel</b> all operations.</li></ul>' \
 
         +'<li>Any <b>other</b> input will <b>cancel</b> all operations.</li></ul>' \
  
# No need to change:
+
###### No need to change after this line #############
  
 
# Regular expression to match any mark:
 
# Regular expression to match any mark:
 
#  '\r' = carriage return in Python
 
#  '\r' = carriage return in Python
 
#  '\t' = tabulation in Python
 
#  '\t' = tabulation in Python
#  [^\t] = non-tab chars
+
#  [^\t|^\r] = non-tab or non-CR chars
 
#  {,4} = 0 to 4 ocurrencies from paragraph begin to tab
 
#  {,4} = 0 to 4 ocurrencies from paragraph begin to tab
reAnyMark  = r'\r[^\t]{,4}\t'  
+
reAnyMark  = r'\r[^\t|^\r]{,4}\t'  
  
 
##############
 
##############
Line 147: Line 157:
 
     '''Function to re.sub use.'''
 
     '''Function to re.sub use.'''
 
     global num
 
     global num
 +
    # Define limit to list
 +
    limit = 999
 +
    if num > limit: num = limit
 
     s = u'\r'+str(num)+separator+'\t'
 
     s = u'\r'+str(num)+separator+'\t'
 +
    num += 1
 +
    return s
 +
 +
####################
 +
# Lettering
 +
def lettering(matchObj):
 +
    '''Function to re.sub use.'''
 +
    ######## bugged ################
 +
    global num
 +
    # Define limit to list (zz)
 +
    limit = ((alphabetLen+1) * alphabetLen)
 +
    if num > limit: num = limit
 +
    d1 = ((num-1) % alphabetLen)
 +
    s1 = alphabet[d1]
 +
    d2 = ((num-1) / alphabetLen)-1
 +
    if d2 > -1:
 +
        s2 = alphabet[d2]
 +
        s1 = s2+s1
 +
    s = u'\r'+s1+separator+'\t'
 +
    if markOperation.isupper():
 +
        s = s.upper()
 
     num += 1
 
     num += 1
 
     return s
 
     return s
Line 159: Line 193:
 
         # Insert numbers
 
         # Insert numbers
 
         s = re.sub(u'\r',numbering,s)
 
         s = re.sub(u'\r',numbering,s)
 +
    elif markOperation.isalpha():
 +
        # Insert letters
 +
        s = re.sub(u'\r',lettering,s)
 
     elif markOperation == bulletOperation:
 
     elif markOperation == bulletOperation:
 
         # Insert bullets
 
         # Insert bullets
Line 197: Line 234:
 
     scribus.selectText(begin,end,story)
 
     scribus.selectText(begin,end,story)
 
     if markOperation == bulletOperation:
 
     if markOperation == bulletOperation:
         style = bulletStyle
+
         applyStyle(bulletStyle,story)
 
     elif markOperation == numberOperation:
 
     elif markOperation == numberOperation:
         style = listStyle
+
         applyStyle(numberedListStyle,story)
     else:
+
     elif markOperation == cleanOperation:
         style = defaultStyle
+
         applyStyle(defaultStyle,story)
     applyStyle(style,story)
+
     elif markOperation.isalpha():
 +
        applyStyle(letteredListStyle,story)
 
     scribus.selectObject(story)
 
     scribus.selectObject(story)
  
 +
#####################
 
def decodeInput(res):
 
def decodeInput(res):
 
     '''Parse user input.'''
 
     '''Parse user input.'''
 
     global num,separator
 
     global num,separator
     f = res.upper()[0]
+
     f = res[0]
 +
    # Test numbered list
 
     if f.isdigit():
 
     if f.isdigit():
 
         n = re.search('\d{1,3}',res).group()
 
         n = re.search('\d{1,3}',res).group()
 
         num = int(n)
 
         num = int(n)
 +
        # Get separator
 
         s = re.search('[^\d]{1,1}',res)
 
         s = re.search('[^\d]{1,1}',res)
 
         if s:
 
         if s:
 
             separator = s.group()
 
             separator = s.group()
 
         return '1'
 
         return '1'
     else:
+
     # Test alphabetic list
         return f
+
    elif f.isalpha():
 +
        if len(res) > 1: # if there is second letter or separator
 +
            f2 = re.search('[a-zA-Z]{1,2}',res).group()
 +
            if len(f2) > 1:
 +
                digit1 = f2[1].lower()
 +
                num = alphabet.find(digit1)+1
 +
                digit2 = f2[0].lower()
 +
                num2 = alphabet.find(digit2)+1
 +
                num2 = num2 * len(alphabet)
 +
                num = num + num2
 +
            else:
 +
                digit1 = f2[0].lower()
 +
                num = alphabet.find(digit1)+1
 +
         else:
 +
            digit1 = f.lower()
 +
            num = alphabet.find(digit1)+1
 +
        # Get separator
 +
        s = re.search('[^a-zA-Z]{1,1}',res)
 +
        if s:
 +
            separator = s.group()
 +
    elif f == bulletOperation: pass
 +
    elif f == cleanOperation: pass
 +
    else: f = ''
 +
    return f
 
      
 
      
 
#####################
 
#####################
Line 225: Line 289:
 
     story = scribus.getSelectedObject(0)  
 
     story = scribus.getSelectedObject(0)  
 
     if story:
 
     if story:
         res = scribus.valueDialog(scriptWindowTitle,askOperationMsg,defaultResponse)
+
         res = scribus.valueDialog(scriptWindowTitle,askOperationMsg,defaultOperation)
 
         res = res.strip()
 
         res = res.strip()
 
         if res:
 
         if res:
 
             markOperation = decodeInput(res)
 
             markOperation = decodeInput(res)
            operation = bulletOperation+numberOperation+cleanOperation
+
             if markOperation:
             if operation.count(markOperation):
+
 
                 doAllOperation(story)
 
                 doAllOperation(story)
 
                 scribus.docChanged(True)
 
                 scribus.docChanged(True)
Line 259: Line 322:
 
if __name__ == '__main__':
 
if __name__ == '__main__':
 
     main_wrapper(sys.argv)
 
     main_wrapper(sys.argv)
 +
 +
 
</pre>
 
</pre>

Revision as of 06:31, 16 January 2011

This article is part of the Scripts series.

Improved bullets simple script using regular expressions Python module. Build bulleted lists, numbered lists starting with any number plus separator and delete list marks. Create styles -- if it doesn't exists -- with proper 2 digits indentation, but is necessary set tabulation "by hand", as this can't be done by script.

Usage

Select a frame or some paragraphs and run the script. Asked, input the operation type:

  • "b" input will create bullet marks and apply "Bullet" style.
  • Any number input will create numbered list, starting with such number, and will apply "Numbered List" style. Optional last char will be used as separator. Default: dot. Example: "1)", "16#", "256-".
  • "x" input will delete marks and apply "Default Paragraph Style" style.
  • Any other response will cancel all operations.

Caveats

Maybe will FREEZE Scribus in Ubuntu 10.04 and 10.10, as script use Pyhton module (re, regular expressions). This is a Ubuntu + Scribus Scripter bug.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
Insert marks, numbers or letters in front selected paragraphs.
Author: prof. MS. José Antonio Meira da Rocha
mailto:joseantoniomeira@gmail.com
http://meiradarocha.jor.br
License GPL 2.0
2011-01-15a

Usage:
======
Select paragraphs at any points. Dialog will ask mark type.
Input: 
* "*" will create bulleted list.
* A number (up 3 digit) will create a numbered list begining with such number. Ex.: "1)"
* One or two letters uppper or lower case will create a lettered list begining with such letters. Ex.: "a)"
* "/" will clear all marks.
* An optional character after numbers or letters will be used as separator. Default: dot.

Caveats:
* Cleans all text format of text selection (font, bold, subscript, etc).
* Creates paragraph style "Numbered List", "Lettered list" or "Bullet", if it doesn't exist, with proper indentation. Tabulation need to be defined by user to identical identation value.
* If there is no selection, script will apply marks to all text in current frame.
"""

import sys
import re

try:
    import scribus
except ImportError,err:
    print "This Python script is written for the Scribus scripting interface."
    print "It can only be run from within Scribus."
    sys.exit(1)
from scribus import BUTTON_OK, ICON_WARNING

#######################
# Change this variables
# to match your needs
#######################
numberedListStyle = 'Numbered list'
letteredListStyle = 'Lettered list'
bulletStyle = 'Bullet'
defaultStyle = 'Default Paragraph Style'
separator = '.'
indent = 22 # points
# Put your favorite unicode char and tab: '\u2022\t' 
# Get hex code from "Insert > Special character..." palette
bulletString = u'•\t'

###########
# Locales
###########

# Message to ask frame selection
scriptWindowTitle = "Format list"
askSelectText = "<h2>Select a text</h2>\n" \
        +"Select a <b>story text</b> where you want insert numbers."
# Message to open a document
askOpenDoc = "<h2>Open a document</h2>" \
        +"Open a document befora run this script."
# Message asking select more text
askSelectMoreText = "<h2>Select more text</h2>" \
        +"Not unique text selected. Select more text before run this script."
wrongFrameTypeErrorMsg = "<h2>Wrong object selected</h2>" \
        +"You must select a <b>text frame</b>."

markOperation = ''
bulletOperation = '*'
numberOperation = '1'
cleanOperation = '/'
alphaOperation = 'Aa'
defaultOperation = bulletOperation
alphabet = 'abcdefghijklmnopqrstuvwxyz'
alphabetLen = len(alphabet)

askOperationMsg = '<h2>What mark operation?</h2>' \
        +'<ul><li>"<b>'+bulletOperation.lower()+'</b>" input will create <b>bullet</b> marks.' \
        +'and apply <i>'+bulletStyle+'</i>  style.' \
        +'<li>A <b>number</b> input will create <b>numbered list</b>  starting <br>with such number and apply <i>'+numberedListStyle+'</i>  style.' \
        +'<li>One or two <b>letters</b> input, upper or lower case, <br>will create <b>lettered list</b> starting with such letters,<br>' \
        +'and apply <i>'+letteredListStyle+'</i>  style.' \
        +'<li><font color="#0000ff">Optional char after number or letter<br> will be used as separator. Default: dot. Ex.:  "<b>a)</b>"</font>' \
        +'<li>"<b>'+cleanOperation.lower()+'</b>" input will <b>delete</b> marks ' \
        +'and apply <br><i>'+defaultStyle+'</i>  style.</li>' \
        +'<li>Any <b>other</b> input will <b>cancel</b> all operations.</li></ul>' \

###### No need to change after this line #############

# Regular expression to match any mark:
#  '\r' = carriage return in Python
#  '\t' = tabulation in Python
#  [^\t|^\r] = non-tab or non-CR chars
#  {,4} = 0 to 4 ocurrencies from paragraph begin to tab
reAnyMark  = r'\r[^\t|^\r]{,4}\t' 

##############
# Apply style
def applyStyle(style,frame):
    '''Apply a style in selected text. 
    If style doesn't exist, create it.'''
    try:
        scribus.setStyle(style,frame)
    except:
        scribus.createParagraphStyle(name=style,leftmargin=indent,firstindent=(indent*-1))
        scribus.setStyle(style,frame)
    return frame

##########################
# Get text cursor position
def getTextCursor(story):
    '''Find position of text cursor in text frame.
    Text need to be selected.'''
    selectedFrame = scribus.getSelectedObject()
    try:
        selectedText = scribus.getText()
    except:
        scribus.messageBox(scriptWindowTitle,wrongFrameTypeErrorMsg,ICON_WARNING,BUTTON_OK)
        scribus.selectObject(story)
        sys.exit()
    scribus.deselectAll()
    allText = scribus.getAllText(selectedFrame)
    
    if allText.count(unicode(selectedText)) == 1:
        cur = allText.find(unicode(selectedText))
    else:
        scribus.messageBox(scriptWindowTitle,askSelectMoreText,ICON_WARNING,BUTTON_OK)
        scribus.selectObject(story)
        sys.exit(1)
    
    return cur,allText,selectedText

############
# Numbering
num = 0
def numbering(matchObj):
    '''Function to re.sub use.'''
    global num
    # Define limit to list
    limit = 999
    if num > limit: num = limit
    s = u'\r'+str(num)+separator+'\t'
    num += 1
    return s

####################
# Lettering
def lettering(matchObj):
    '''Function to re.sub use.'''
    ######## bugged ################
    global num
    # Define limit to list (zz)
    limit = ((alphabetLen+1) * alphabetLen)
    if num > limit: num = limit
    d1 = ((num-1) % alphabetLen)
    s1 = alphabet[d1]
    d2 = ((num-1) / alphabetLen)-1
    if d2 > -1:
        s2 = alphabet[d2]
        s1 = s2+s1
    s = u'\r'+s1+separator+'\t'
    if markOperation.isupper():
        s = s.upper()
    num += 1
    return s

####################
def insertMarks(t):
    '''Delete old marks and insert new ones.'''
    # Clear old numbers
    s = re.sub(reAnyMark,u'\r',t)
    if markOperation == numberOperation:
        # Insert numbers
        s = re.sub(u'\r',numbering,s)
    elif markOperation.isalpha():
        # Insert letters
        s = re.sub(u'\r',lettering,s)
    elif markOperation == bulletOperation:
        # Insert bullets
        b = u'\r'+bulletString
        s = re.sub(u'\r',b,s)
    
    return s

#################
# Do all the job
def doAllOperation(story):
    '''Insert marks in front selected paragraphs or clean marks.'''
    # Get selection begin
    cur,allText,selectedText = getTextCursor(story)
    # Get first selected paragraph begin
    # (last line break position after selection)
    begin = allText.rfind(u'\r',0,cur)+1 # Scribus not use '\n'?
    # Get selection end
    end = cur+len(unicode(selectedText))
    # Get last selected paragraph end
    end = allText.find(u'\r',end)
    totalLen = end - begin
    scribus.selectText(begin,totalLen,story)
    selectedText = scribus.getText(story)
    #print '\nSelected:\n',repr(selectedText.encode('utf-8')) # debug
    # Put temporary carriage return to allow numbering at string begin
    selectedText = u'\r'+selectedText
    # Clean old numbers and insert new ones
    cleanedText = insertMarks(unicode(selectedText))
    # Strip temp initial CR
    cleanedText = unicode(cleanedText)[1:]
    scribus.deleteText(story)
    scribus.insertText(cleanedText,begin,story)
    # Apply style
    scribus.deselectAll()
    # Get selection new end
    end = len(unicode(cleanedText))
    scribus.selectText(begin,end,story)
    if markOperation == bulletOperation:
        applyStyle(bulletStyle,story)
    elif markOperation == numberOperation:
        applyStyle(numberedListStyle,story)
    elif markOperation == cleanOperation:
        applyStyle(defaultStyle,story)
    elif markOperation.isalpha():
        applyStyle(letteredListStyle,story)
    scribus.selectObject(story)

#####################
def decodeInput(res):
    '''Parse user input.'''
    global num,separator
    f = res[0]
    # Test numbered list
    if f.isdigit():
        n = re.search('\d{1,3}',res).group()
        num = int(n)
        # Get separator
        s = re.search('[^\d]{1,1}',res)
        if s:
            separator = s.group()
        return '1'
    # Test alphabetic list
    elif f.isalpha():
        if len(res) > 1: # if there is second letter or separator
            f2 = re.search('[a-zA-Z]{1,2}',res).group()
            if len(f2) > 1:
                digit1 = f2[1].lower()
                num = alphabet.find(digit1)+1
                digit2 = f2[0].lower()
                num2 = alphabet.find(digit2)+1
                num2 = num2 * len(alphabet)
                num = num + num2
            else:
                digit1 = f2[0].lower()
                num = alphabet.find(digit1)+1
        else:
            digit1 = f.lower()
            num = alphabet.find(digit1)+1
        # Get separator
        s = re.search('[^a-zA-Z]{1,1}',res)
        if s:
            separator = s.group()
    elif f == bulletOperation: pass
    elif f == cleanOperation: pass
    else: f = ''
    return f
    
#####################
def handleSelected():
    """Handle frame selection."""
    global markOperation,separator
    story = scribus.getSelectedObject(0) 
    if story:
        res = scribus.valueDialog(scriptWindowTitle,askOperationMsg,defaultOperation)
        res = res.strip()
        if res:
            markOperation = decodeInput(res)
            if markOperation:
                doAllOperation(story)
                scribus.docChanged(True)
    else:
        scribus.messageBox(scriptWindowTitle,askSelectText,ICON_WARNING,BUTTON_OK)

############### 
def main(argv):
    """Main entry point."""
    if scribus.haveDoc():
        scribus.setRedraw(False)
        handleSelected()
    else:  
        scribus.messageBox(scriptWindowTitle,askOpenDoc,ICON_WARNING,BUTTON_OK)

#######################
def main_wrapper(argv):
    try:
        scribus.statusMessage("Running script...")
        scribus.progressReset()
        main(argv)
    finally:
        if scribus.haveDoc():
            scribus.setRedraw(True)
        scribus.statusMessage("")
        scribus.progressReset()

if __name__ == '__main__':
    main_wrapper(sys.argv)