Bullets and numbered lists

From Scribus Wiki
Revision as of 02:47, 15 January 2011 by Gpittman (Talk | contribs)

Jump to: navigation, search

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 or numbers 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-14a

======
Usage:
Select paragraphs at any points. Run script. 
Response 'yes' will clear all numbers. Any other response
will create numbered list.

Caveats:
* 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.
* If there is no selection, will apply bullets 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
#######################
listStyle = 'Numbered list'
bulletStyle = 'Bullet'
defaultStyle = 'Default Paragraph Style'
separator = '.'
indent = 18 # 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 = 'B'
numberOperation = '1'
cleanOperation = 'X'
defaultResponse = bulletOperation
askOperationMsg = "<h2>What mark operation?</h2>" \
        +'<ul><li>"<b>'+bulletOperation.lower()+'</b>" input will create <b>bullet</b> marks.</li>' \
        +'and apply <i>'+bulletStyle+'</i>  style.' \
        +'<li><b>Any number</b> input will create <b>numbered list</b>  <br>starting with such number ' \
        +'and apply <i>'+listStyle+'</i>  style.' \
        +'<br><font color="#0000ff">Optional last char will be used as separator.<br>Default: dot. Example:  "<b>1)</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:

justClean = 0

# Regular expression to match any mark:
#  '\r' = carriage return in Python
#  '\t' = tabulation in Python
#  [^\t] = non-tab chars
#  {,4} = 0 to 4 ocurrencies from paragraph begin to tab
reAnyMark  = r'\r[^\t]{,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
    s = u'\r'+str(num)+separator+'\t'
    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 == 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:
        style = bulletStyle
    elif markOperation == numberOperation:
        style = listStyle
    else:
        style = defaultStyle
    applyStyle(style,story)
    scribus.selectObject(story)

def decodeInput(res):
    '''Parse user input.'''
    global num,separator
    f = res.upper()[0]
    if f.isdigit():
        n = re.search('\d{1,3}',res).group()
        num = int(n)
        s = re.search('[^\d]{1,1}',res)
        if s:
            separator = s.group()
        return '1'
    else:
        return f
    
#####################
def handleSelected():
    """Handle frame selection."""
    global markOperation,separator
    story = scribus.getSelectedObject(0) 
    if story:
        res = scribus.valueDialog(scriptWindowTitle,askOperationMsg,defaultResponse)
        res = res.strip()
        if res:
            markOperation = decodeInput(res)
            operation = bulletOperation+numberOperation+cleanOperation
            if operation.count(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)