Bullets and numbered lists

From Scribus Wiki
Revision as of 23:51, 14 January 2011 by Meira (talk | contribs) (→‎Caveats)
Jump to navigation Jump to 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.<br>' \
        +'and apply <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
#  {,5} = 0 to 5 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)