Skip to content

Instantly share code, notes, and snippets.

@tin2tin
Last active November 9, 2019 01:19
Show Gist options
  • Save tin2tin/98e83093cdffbb1da71c455f2b564846 to your computer and use it in GitHub Desktop.
Save tin2tin/98e83093cdffbb1da71c455f2b564846 to your computer and use it in GitHub Desktop.
Fountain_in_Text_Editor - work in progress
import bpy
import textwrap
import os
import sys
import fountain
from bpy.props import BoolProperty, PointerProperty, StringProperty
from pathlib import Path
class FOUNTAIN_PT_panel(bpy.types.Panel):
"""Preview fountain script as formatted screenplay"""
bl_label = "Fountain"
bl_space_type = 'TEXT_EDITOR'
bl_region_type = 'UI'
bl_category = "Text"
def draw(self, context):
layout = self.layout
layout.operator("scene.preview_fountain")
layout.operator("areatype.trimview")
repl = context.scene.text_replace
layout.prop(repl, "enabled")
#print(bpy.context.space_data.type)
class FOUNTAIN_OT_preview_fountain(bpy.types.Operator):
bl_idname = "scene.preview_fountain"
bl_label = "Preview Screenplay"
# @classmethod
# def poll(self, context):
# if bpy.context.space_data.type == 'TEXT_EDITOR':
# suffix = Path(bpy.context.space_data.text.filepath).suffix
# else:
# suffix = ""
# return suffix == ".fountain"
def execute(self, context):
if bpy.context.space_data.type == 'TEXT_EDITOR':
suffix = Path(bpy.context.space_data.text.filepath).suffix
else:
suffix = ""
if suffix != ".fountain":
return {"CANCELLED"}
# dir = os.path.dirname(bpy.data.filepath)
# if not dir in sys.path:
# sys.path.append(dir)
fountain_script = bpy.context.area.spaces.active.text.as_string()#context.scene.fountain.get_body()
F = fountain.Fountain(fountain_script)
# if 'title' in F.metadata:
# file_name = F.metadata['title'][0]
# else:
file_name = "Fountain"
#filename = "Preview_"+file_name[:-4]+".txt"
filename = "Preview"+".txt"
#print(filename)
if filename not in bpy.data.texts:
bpy.data.texts.new(filename) # New document in Text Editor
else:
bpy.data.texts[filename].clear() # Clear existing text
action_wrapper = textwrap.TextWrapper(width=60)
dialogue_wrapper = textwrap.TextWrapper(width=37)
for fc, f in enumerate(F.elements):
if f.element_type == 'Scene Heading':
bpy.data.texts[filename].write(f.element_text+chr(10))
if f.element_type == 'Action':
action = f.element_text
action_list = action_wrapper.wrap(text=action)
for element in action_list:
bpy.data.texts[filename].write(element+chr(10))
#bpy.data.texts[filename].write(chr(10))
if f.element_type == 'Character':
bpy.data.texts[filename].write(chr(10)+(f.element_text).center(60)+chr(10))
if f.element_type == 'Parenthetical':
bpy.data.texts[filename].write((f.element_text).center(60)+chr(10))
if f.element_type == 'Dialogue':
dialogue = f.element_text
line_list = dialogue_wrapper.wrap(text=dialogue)
for element in line_list:
bpy.data.texts[filename].write((" "+element)+chr(10)) # 13 characters
elif f.element_type == 'Synopsis':
continue
elif f.element_type == 'Page Break':
continue
elif f.element_type == 'Boneyard':
continue
elif f.element_type == 'Comment':
continue
elif f.element_type == 'Section Heading':
continue
elif f.element_type == 'Transition':
bpy.data.texts[filename].write(f.element_text.rjust(60)+chr(10))
elif f.element_type == 'Empty Line':
bpy.data.texts[filename].write(chr(10)+chr(10))
return {"FINISHED"}
class AREATYPE_OT_trim(bpy.types.Operator):
bl_idname = "areatype.trimview"
bl_label = "Dual View"
original_area = None
def execute(self,context):
self.original_area = context.area
original = context.copy()
thisarea = context.area
otherarea = None
tgxvalue = thisarea.x + thisarea.width + 1
thistype = context.area.type
arealist = list(context.screen.areas)
for area in context.screen.areas:
if area == thisarea:
continue
elif area.x == tgxvalue and area.y == thisarea.y:
otherarea = area
break
if otherarea: #leave trim-mode
# print("this x: "+str(thisarea.x)) #min_x = left x of left window
# print("this y: "+str(thisarea.y)) #min_y = mouse position, starting from window bottom y
# print("other x: "+str(otherarea.x)) #max_x = x between left and right window
# print("other y: "+str(otherarea.y)) #max_y = mouse position, starting from window bottom y
#join
#bpy.ops.screen.area_join(min_x=thisarea.x, min_y=thisarea.y, max_x=otherarea.x, max_y=otherarea.y) #broken
# normal settings
#bpy.ops.screen.screen_full_area()
#bpy.ops.screen.screen_full_area()
override = context.copy()
area = self.original_area
override['area'] = area
override['space_data'] = area.spaces.active
# for region in area.regions:
# if region.type == 'PREVIEW':
# break
# override['region'] = region
return {"FINISHED"}
else: # enter dual-mode
areax = None
#split
bpy.ops.screen.area_split(direction="VERTICAL")
#settings for preview 2.
# fit 1. preview to window
bpy.ops.screen.screen_full_area()
bpy.ops.screen.screen_full_area()
override = original
area = self.original_area
override['area'] = area
override['space_data'] = area.spaces.active
for area in context.screen.areas:
if area not in arealist:
areax = area
break
# for area in reversed(context.screen.areas):
# if area.type == "TEXT_EDITOR":
# suffix = Path(bpy.context.space_data.text.filepath).suffix
# if suffix == ".fountain":
# area.space_data.text = bpy.data.texts['Preview.txt']
# break
## else:
## raise RuntimeError("Nothing found")
# bpy.context.space_data.text = bpy.data.texts['Preview.txt']
#print(bpy.context.space_data.text.filepath)
if areax:
areax.type = thistype
return {"FINISHED"}
#bpy.context.space_data.text = bpy.data.texts['Preview.txt']
return {"CANCELLED"}
handler = None
last = None
last_index = None
def get_space(context):
for area in context.screen.areas:
if area.type == "TEXT_EDITOR":
return area.spaces.active
def text_handler(spc):
global last, last_index
text = spc.text
if not text:
return
line = text.current_line.body
if last is None or last_index != text.current_line_index:
last = line
last_index = text.current_line_index
return
if line != last and len(line)>len(last):
bpy.ops.scene.preview_fountain()
last = line
def activate_handler(self, context):
global handler
spc = get_space(context)
if not spc:
return
enabled = context.scene.text_replace.enabled
if enabled:
handler = spc.draw_handler_add(text_handler, (spc,), "WINDOW", "POST_PIXEL")
print("handler activated", handler)
else:
if handler is not None:
spc.draw_handler_remove(handler, "WINDOW")
handler = None
print("handler deactivated")
class TextReplaceProperties(bpy.types.PropertyGroup):
enabled: BoolProperty(name="Live Preview", description="Enables live screenplay preview", update=activate_handler)
bpy.utils.register_class(TextReplaceProperties)
bpy.types.Scene.text_replace = PointerProperty(type=TextReplaceProperties)
def register():
bpy.utils.register_class(FOUNTAIN_PT_panel)
bpy.utils.register_class(FOUNTAIN_OT_preview_fountain)
bpy.utils.register_class(AREATYPE_OT_trim)
def unregister():
bpy.utils.unregister_class(FOUNTAIN_PT_panel)
bpy.utils.unregister_class(FOUNTAIN_OT_preview_fountain)
bpy.utils.unregister_class(AREATYPE_OT_trim)
if __name__ == "__main__":
register()
#unregister()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment