#FLM: Automatic Text
# -*- coding: utf-8 -*-

"""Automatic Text

A FontLab Studio port of a long forgotten but very helpful Letraset
FontStudio feature. Automatic Text generates mixed sequences of
glyphs, useful when checking for sanity your font metrics/kerning.

It should work with any copy of FontLab Studio 5, nothing else is
required.

To install, put this file in your FontLab Macros folder (on OS X it is
in ~/Library/Application Support/FontLab 5/Macros), then restart
FontLab. You’ll find Automatic Text in the Macro toolbar, which can be
toggled with the menu item View → Toolbars → Macro.

You can then launch Automatic Text by clicking the Run button next to
the macro name in the Macro toolbar, and it will popup its dialog
box. If you select one glyph in the font window it will be selected as
the glyph to generate. If you select several glyphs Automatic Text
will default to the selection mode (which wasn’t there in FontStudio)
using the selected glyphs as a base to generate the glyph sequence.

Thanks to Claudio Piccinini <http://www.tht.it/> for showing me his
(working!) copy of FontStudio with this neat feature.
"""

import string

from FL import *

__author__ = "Antonio Cavedoni <http://cavedoni.com/>"
__version__ = "0.2"
__svnid__ = "$Id$"
__license__ = "Python"
__contributors__ = [
    "Claudio Piccinini <http://www.tht.it/>",
    "Karsten Luecke <http://www.kltf.de/>"
    ]

UPPERCASE = [x for x in string.uppercase]
LOWERCASE = [x for x in string.lowercase]
DIGITS = "zero one two three four five six seven eight nine".split()

class AutomaticTextDialog:
    def __init__(self):
        self.d = Dialog(self)
        self.d.size = Point(320, 280)
        self.d.Center()
        self.d.title = 'Automatic Text...'
        self.d.AddControl(CHECKBOXCONTROL, Rect(15, 10, 255, 40), 
                          'generateall', STYLE_CHECKBOX, 
                          'Generate all characters')
        self.d.AddControl(CHECKBOXCONTROL, Rect(15, 60, 100, 80), 
                          'generatebeside', STYLE_CHECKBOX, 'Generate')
        self.d.AddControl(EDITCONTROL, Rect(100, 58, 170, 84), 
                          'letter', STYLE_EDIT, '')
        self.d.AddControl(STATICCONTROL, Rect(180, 62, 280, 86), 
                          'label0', STYLE_LABEL, 'beside:')
        self.d.AddControl(CHECKBOXCONTROL, Rect(50, 90, 160, 120), 
                          'lowercase', STYLE_CHECKBOX, 
                          'a through z')
        self.d.AddControl(CHECKBOXCONTROL, Rect(180, 90, 310, 120), 
                          'all_characters', STYLE_CHECKBOX, 'All characters')
        self.d.AddControl(CHECKBOXCONTROL, Rect(50, 120, 160, 150), 
                          'uppercase', STYLE_CHECKBOX, 'A through Z')
        self.d.AddControl(CHECKBOXCONTROL, Rect(180, 120, 310, 150), 
                          'numbers', STYLE_CHECKBOX, '0 through 9')
        self.d.AddControl(CHECKBOXCONTROL, Rect(50, 150, 80, 180), 
                          'other', STYLE_CHECKBOX, '')
        self.d.AddControl(EDITCONTROL, Rect(70, 152, 140, 178), 
                          'other_from', STYLE_EDIT, '')
        self.d.AddControl(STATICCONTROL, Rect(150, 158, 210, 178), 
                          'label1', STYLE_LABEL, 'through')
        self.d.AddControl(EDITCONTROL, Rect(210, 152, 285, 178), 
                          'other_to', STYLE_EDIT, '')
        self.d.AddControl(CHECKBOXCONTROL, Rect(50, 185, 285, 210), 
                          'selection', STYLE_CHECKBOX, 
                          'Selected glyphs in font window')

        self.generateall = 0
        self.generatebeside = 1
        self.letter = ''
        self.lowercase = 1
        self.all_characters = 0
        self.uppercase = 0
        self.numbers = 0
        self.other = 0
        self.other_from = ''
        self.other_to = ''
        self.selection = 0

        if fl.count_selected == 1:
            try:
                self.letter = "/" + fl.glyph.name
            except AttributeError:
                pass
        elif fl.count_selected > 1:
            self.lowercase = 0
            self.other = 0
            self.selection = 1

        self.generates = ['generateall', 'generatebeside']
        self.besides = ['all_characters', 'lowercase', 'uppercase', 
                        'numbers', 'other', 'selection']

    def check_control_in_list(self, control, controls):
        for c in controls:
            if c == control:
                setattr(self, c, 1)
            else:
                setattr(self, c, 0)
            self.d.PutValue(c)
        return control

    def on_generateall(self, code):
        self.d.GetValue('generateall')
        self.check_control_in_list('generateall', self.generates + self.besides)

    def on_generatebeside(self, code):
        self.d.GetValue('generatebeside')
        self.check_control_in_list('generatebeside', self.generates)

    def on_letter(self, code):
        self.d.GetValue('letter')

    def on_other_from(self, code):
        self.d.GetValue('other_from')

    def on_other_to(self, code):
        self.d.GetValue('other_to')

    def on_selection(self, code):
        self.d.GetValue('selection')
        self.check_control_in_list('generatebeside', self.generates)
        self.check_control_in_list('selection', self.besides)

    def on_all_characters(self, code):
        self.d.GetValue('all_characters')
        self.check_control_in_list('generatebeside', self.generates)
        self.check_control_in_list('all_characters', self.besides)

    def on_numbers(self, code):
        self.d.GetValue('numbers')
        self.check_control_in_list('generatebeside', self.generates)
        self.check_control_in_list('numbers', self.besides)

    def on_uppercase(self, code):
        self.d.GetValue('uppercase')
        self.check_control_in_list('generatebeside', self.generates)
        self.check_control_in_list('uppercase', self.besides)

    def on_lowercase(self, code):
        self.d.GetValue('lowercase')
        self.check_control_in_list('generatebeside', self.generates)
        self.check_control_in_list('lowercase', self.besides)

    def on_other(self, code):
        self.d.GetValue('other')
        self.check_control_in_list('generatebeside', self.generates)
        self.check_control_in_list('other', self.besides)

    def on_ok(self, code):
        return 1

    def Run(self):
        return self.d.Run()

def get_all_glyphs():
    # generate a temp font so we can sort its glyphs
    # by Unicode name without damaging the original
    tempfont = Font(fl.font)
    fl.Add(tempfont)
    tempfontid = fl.ifont
    fl.CallCommand(fl_cmd.FontSortByUnicode)

    # build a string with all the characters
    glyphs = []
    for i in range(len(tempfont)):
        n = fl.font[i].name
        if n not in ['.notdef', '.null', 'CR']:
            glyphs.append(get_glyph_name(fl.font[i].name))

    # close the temp font
    tempfont.modified = 0
    fl.Close(tempfontid)

    return glyphs

def map_digit_to_name(digit):
    if digit in [str(x) for x in string.digits]:
        return DIGITS[int(digit)]
    else:
        return digit
    
def get_glyph_name(glyph):
    glyph = map_digit_to_name(glyph)
    if not glyph.startswith('/'):
        return '/%s' % glyph
    else:
        return glyph

def get_selection():
    selected = []
    for i in range(len(fl.font)):
        if fl.Selected(fl.font[i].index):
            try:
                selected.append(get_glyph_name(fl.font[i].name))
            except AttributeError:
                pass
    return selected

def generate(glyph_set, glyph_name=None):
    # open metrics window
    fl.CallCommand(fl_cmd.WindowNewMetrics)

    # normalise the glyph names
    glyph_set = [get_glyph_name(x) for x in glyph_set]

    # write to the metrics preview text
    if not glyph_name:
        fl.metricspreview = "".join(glyph_set)
    else:
        fl.metricspreview = \
            glyph_name + glyph_name.join(glyph_set) + glyph_name

def generate_beside_two(glyph_name, glyph_from, glyph_to):
    glyphset = []
    in_sequence = False
    for i in range(len(fl.font)):
        if not in_sequence:
            if fl.font[i].name == map_digit_to_name(glyph_from):
                glyphset.append(get_glyph_name(fl.font[i].name))
                in_sequence = True
        else:
            glyphset.append(get_glyph_name(fl.font[i].name))
            if fl.font[i].name == map_digit_to_name(glyph_to):
                break
    generate(glyphset, get_glyph_name(glyph_name))

d = AutomaticTextDialog()

if d.Run() == 1:
    if d.generateall:
        generate(get_all_glyphs())

    elif d.generatebeside:
        char = get_glyph_name(d.letter)

        if d.all_characters:
            generate(get_all_glyphs(), char)
        elif d.uppercase:
            generate(UPPERCASE, char)
        elif d.lowercase:
            generate(LOWERCASE, char)
        elif d.numbers:
            generate(DIGITS, char)
        elif d.other:
            generate_beside_two(d.letter, d.other_from, d.other_to)
        elif d.selection:
            generate(get_selection(), char)
