trelby_export/src/titlesdlg.py

511 lines
16 KiB
Python

import gutil
import misc
import pdf
import pml
import titles
import util
import copy
import wx
class TitlesDlg(wx.Dialog):
def __init__(self, parent, titles, cfg, cfgGl):
wx.Dialog.__init__(self, parent, -1, "Title pages",
style = wx.DEFAULT_DIALOG_STYLE)
self.titles = titles
self.cfg = cfg
self.cfgGl = cfgGl
# whether some events are blocked
self.block = False
self.setPage(0)
vsizer = wx.BoxSizer(wx.VERTICAL)
self.pageLabel = wx.StaticText(self, -1, "")
vsizer.Add(self.pageLabel, 0, wx.ADJUST_MINSIZE)
hsizer = wx.BoxSizer(wx.HORIZONTAL)
tmp = wx.Button(self, -1, "Add")
hsizer.Add(tmp)
self.Bind(wx.EVT_BUTTON, self.OnAddPage, id=tmp.GetId())
gutil.btnDblClick(tmp, self.OnAddPage)
self.delPageBtn = wx.Button(self, -1, "Delete")
hsizer.Add(self.delPageBtn, 0, wx.LEFT, 10)
self.Bind(wx.EVT_BUTTON, self.OnDeletePage, id=self.delPageBtn.GetId())
gutil.btnDblClick(self.delPageBtn, self.OnDeletePage)
self.moveBtn = wx.Button(self, -1, "Move")
hsizer.Add(self.moveBtn, 0, wx.LEFT, 10)
self.Bind(wx.EVT_BUTTON, self.OnMovePage, id=self.moveBtn.GetId())
gutil.btnDblClick(self.moveBtn, self.OnMovePage)
self.nextBtn = wx.Button(self, -1, "Next")
hsizer.Add(self.nextBtn, 0, wx.LEFT, 10)
self.Bind(wx.EVT_BUTTON, self.OnNextPage, id=self.nextBtn.GetId())
gutil.btnDblClick(self.nextBtn, self.OnNextPage)
vsizer.Add(hsizer, 0, wx.TOP, 5)
vsizer.Add(wx.StaticLine(self, -1), 0, wx.EXPAND | wx.TOP | wx.BOTTOM,
10)
hsizer = wx.BoxSizer(wx.HORIZONTAL)
vsizer2 = wx.BoxSizer(wx.VERTICAL)
tmp = wx.StaticText(self, -1, "Strings:")
vsizer2.Add(tmp)
self.stringsLb = wx.ListBox(self, -1, size = (200, 150))
vsizer2.Add(self.stringsLb)
hsizer2 = wx.BoxSizer(wx.HORIZONTAL)
self.addBtn = gutil.createStockButton(self, "Add")
hsizer2.Add(self.addBtn)
self.Bind(wx.EVT_BUTTON, self.OnAddString, id=self.addBtn.GetId())
gutil.btnDblClick(self.addBtn, self.OnAddString)
self.delBtn = gutil.createStockButton(self, "Delete")
hsizer2.Add(self.delBtn, 0, wx.LEFT, 10)
self.Bind(wx.EVT_BUTTON, self.OnDeleteString, id=self.delBtn.GetId())
gutil.btnDblClick(self.delBtn, self.OnDeleteString)
vsizer2.Add(hsizer2, 0, wx.TOP, 5)
hsizer.Add(vsizer2)
self.previewCtrl = TitlesPreview(self, self, self.cfg)
util.setWH(self.previewCtrl, 150, 150)
hsizer.Add(self.previewCtrl, 1, wx.EXPAND | wx.LEFT, 10)
vsizer.Add(hsizer, 0, wx.EXPAND)
hsizer = wx.BoxSizer(wx.HORIZONTAL)
hsizer.Add(wx.StaticText(self, -1, "Text:"), 0,
wx.ALIGN_CENTER_VERTICAL)
self.textEntry = wx.TextCtrl(
self, -1, style = wx.TE_MULTILINE | wx.TE_DONTWRAP, size = (200, 75))
hsizer.Add(self.textEntry, 1, wx.LEFT, 10)
self.Bind(wx.EVT_TEXT, self.OnMisc, id=self.textEntry.GetId())
vsizer.Add(hsizer, 0, wx.EXPAND | wx.TOP, 20)
# TODO: should use FlexGridSizer, like headersdlg, to get neater
# layout
hsizerTop = wx.BoxSizer(wx.HORIZONTAL)
vsizer2 = wx.BoxSizer(wx.VERTICAL)
hsizer = wx.BoxSizer(wx.HORIZONTAL)
hsizer.Add(wx.StaticText(self, -1, "Alignment:"), 0,
wx.ALIGN_CENTER_VERTICAL)
self.alignCombo = wx.ComboBox(self, -1, style = wx.CB_READONLY)
for it in [ ("Left", util.ALIGN_LEFT), ("Center", util.ALIGN_CENTER),
("Right", util.ALIGN_RIGHT) ]:
self.alignCombo.Append(it[0], it[1])
hsizer.Add(self.alignCombo, 0, wx.LEFT, 10)
self.Bind(wx.EVT_COMBOBOX, self.OnMisc, id=self.alignCombo.GetId())
vsizer2.Add(hsizer, 0, wx.TOP, 5)
hsizer = wx.BoxSizer(wx.HORIZONTAL)
hsizer.Add(wx.StaticText(self, -1, "X / Y Pos (mm):"), 0,
wx.ALIGN_CENTER_VERTICAL)
self.xEntry = wx.TextCtrl(self, -1)
hsizer.Add(self.xEntry, 0, wx.LEFT, 10)
self.Bind(wx.EVT_TEXT, self.OnMisc, id=self.xEntry.GetId())
self.yEntry = wx.TextCtrl(self, -1)
hsizer.Add(self.yEntry, 0, wx.LEFT, 10)
self.Bind(wx.EVT_TEXT, self.OnMisc, id=self.yEntry.GetId())
vsizer2.Add(hsizer, 0, wx.TOP, 5)
hsizer = wx.BoxSizer(wx.HORIZONTAL)
hsizer.Add(wx.StaticText(self, -1, "Font / Size:"), 0,
wx.ALIGN_CENTER_VERTICAL)
self.fontCombo = wx.ComboBox(self, -1, style = wx.CB_READONLY)
for it in [ ("Courier", pml.COURIER), ("Helvetica", pml.HELVETICA),
("Times-Roman", pml.TIMES_ROMAN) ]:
self.fontCombo.Append(it[0], it[1])
hsizer.Add(self.fontCombo, 0, wx.LEFT, 10)
self.Bind(wx.EVT_COMBOBOX, self.OnMisc, id=self.fontCombo.GetId())
self.sizeEntry = wx.SpinCtrl(self, -1, size = (50, -1))
self.sizeEntry.SetRange(4, 288)
self.Bind(wx.EVT_SPINCTRL, self.OnMisc, id=self.sizeEntry.GetId())
self.sizeEntry.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)
hsizer.Add(self.sizeEntry, 0, wx.LEFT, 10)
vsizer2.Add(hsizer, 0, wx.TOP, 10)
hsizerTop.Add(vsizer2)
bsizer = wx.StaticBoxSizer(wx.StaticBox(self, -1, "Style"),
wx.HORIZONTAL)
vsizer2 = wx.BoxSizer(wx.VERTICAL)
# wxGTK adds way more space by default than wxMSW between the
# items, have to adjust for that
pad = 0
if misc.isWindows:
pad = 5
self.addCheckBox("Bold", self, vsizer2, pad)
self.addCheckBox("Italic", self, vsizer2, pad)
self.addCheckBox("Underlined", self, vsizer2, pad)
bsizer.Add(vsizer2)
hsizerTop.Add(bsizer, 0, wx.LEFT, 20)
vsizer.Add(hsizerTop, 0, wx.TOP, 10)
hsizer = wx.BoxSizer(wx.HORIZONTAL)
hsizer.Add((1, 1), 1)
self.previewBtn = gutil.createStockButton(self, "Preview")
hsizer.Add(self.previewBtn)
cancelBtn = gutil.createStockButton(self, "Cancel")
hsizer.Add(cancelBtn, 0, wx.LEFT, 10)
okBtn = gutil.createStockButton(self, "OK")
hsizer.Add(okBtn, 0, wx.LEFT, 10)
vsizer.Add(hsizer, 0, wx.EXPAND | wx.TOP, 20)
util.finishWindow(self, vsizer)
self.Bind(wx.EVT_BUTTON, self.OnPreview, id=self.previewBtn.GetId())
self.Bind(wx.EVT_BUTTON, self.OnCancel, id=cancelBtn.GetId())
self.Bind(wx.EVT_BUTTON, self.OnOK, id=okBtn.GetId())
self.Bind(wx.EVT_LISTBOX, self.OnStringsLb, id=self.stringsLb.GetId())
# list of widgets that are specific to editing the selected string
self.widList = [ self.textEntry, self.xEntry, self.alignCombo,
self.yEntry, self.fontCombo, self.sizeEntry,
self.boldCb, self.italicCb, self.underlinedCb ]
self.updateGui()
self.textEntry.SetFocus()
def addCheckBox(self, name, parent, sizer, pad):
cb = wx.CheckBox(parent, -1, name)
self.Bind(wx.EVT_CHECKBOX, self.OnMisc, id=cb.GetId())
sizer.Add(cb, 0, wx.TOP, pad)
setattr(self, name.lower() + "Cb", cb)
def OnOK(self, event):
self.titles.sort()
self.EndModal(wx.ID_OK)
def OnCancel(self, event):
self.EndModal(wx.ID_CANCEL)
def OnPreview(self, event):
doc = pml.Document(self.cfg.paperWidth, self.cfg.paperHeight)
self.titles.generatePages(doc)
tmp = pdf.generate(doc)
gutil.showTempPDF(tmp, self.cfgGl, self)
# set given page. 'page' can be an invalid value.
def setPage(self, page):
# selected page index or -1
self.pageIndex = -1
if self.titles.pages:
self.pageIndex = 0
if (page >= 0) and (len(self.titles.pages) > page):
self.pageIndex = page
# selected string index or -1
self.tsIndex = -1
if self.pageIndex == -1:
return
if len(self.titles.pages[self.pageIndex]) > 0:
self.tsIndex = 0
def OnKillFocus(self, event):
self.OnMisc()
# if we don't call this, the spin entry on wxGTK gets stuck in
# some weird state
event.Skip()
def OnStringsLb(self, event = None):
self.tsIndex = self.stringsLb.GetSelection()
self.updateStringGui()
def OnAddPage(self, event):
self.titles.pages.append([])
self.setPage(len(self.titles.pages) - 1)
self.updateGui()
def OnDeletePage(self, event):
del self.titles.pages[self.pageIndex]
self.setPage(0)
self.updateGui()
def OnMovePage(self, event):
newIndex = (self.pageIndex + 1) % len(self.titles.pages)
self.titles.pages[self.pageIndex], self.titles.pages[newIndex] = (
self.titles.pages[newIndex], self.titles.pages[self.pageIndex])
self.setPage(newIndex)
self.updateGui()
def OnNextPage(self, event):
self.setPage((self.pageIndex + 1) % len(self.titles.pages))
self.updateGui()
def OnAddString(self, event):
if self.pageIndex == -1:
return
if self.tsIndex != -1:
ts = copy.deepcopy(self.titles.pages[self.pageIndex][self.tsIndex])
ts.y += util.getTextHeight(ts.size)
else:
ts = titles.TitleString(["new string"], 0.0, 100.0)
self.titles.pages[self.pageIndex].append(ts)
self.tsIndex = len(self.titles.pages[self.pageIndex]) - 1
self.updateGui()
def OnDeleteString(self, event):
if (self.pageIndex == -1) or (self.tsIndex == -1):
return
del self.titles.pages[self.pageIndex][self.tsIndex]
self.tsIndex = min(self.tsIndex,
len(self.titles.pages[self.pageIndex]) - 1)
self.updateGui()
# update page/string listboxes and selection
def updateGui(self):
self.stringsLb.Clear()
pgCnt = len(self.titles.pages)
self.delPageBtn.Enable(pgCnt > 0)
self.moveBtn.Enable(pgCnt > 1)
self.nextBtn.Enable(pgCnt > 1)
self.previewBtn.Enable(pgCnt > 0)
if self.pageIndex != -1:
page = self.titles.pages[self.pageIndex]
self.pageLabel.SetLabel("Page: %d / %d" % (self.pageIndex + 1,
pgCnt))
self.addBtn.Enable(True)
self.delBtn.Enable(len(page) > 0)
for s in page:
self.stringsLb.Append("--".join(s.items))
if self.tsIndex != -1:
self.stringsLb.SetSelection(self.tsIndex)
else:
self.pageLabel.SetLabel("No pages.")
self.addBtn.Disable()
self.delBtn.Disable()
self.updateStringGui()
self.previewCtrl.Refresh()
# update selected string stuff
def updateStringGui(self):
if self.tsIndex == -1:
for w in self.widList:
w.Disable()
self.textEntry.SetValue("")
self.xEntry.SetValue("")
self.yEntry.SetValue("")
self.sizeEntry.SetValue(12)
self.boldCb.SetValue(False)
self.italicCb.SetValue(False)
self.underlinedCb.SetValue(False)
return
self.block = True
ts = self.titles.pages[self.pageIndex][self.tsIndex]
for w in self.widList:
w.Enable(True)
if ts.isCentered:
self.xEntry.Disable()
self.textEntry.SetValue("\n".join(ts.items))
self.xEntry.SetValue("%.2f" % ts.x)
self.yEntry.SetValue("%.2f" % ts.y)
util.reverseComboSelect(self.alignCombo, ts.getAlignment())
util.reverseComboSelect(self.fontCombo, ts.font)
self.sizeEntry.SetValue(ts.size)
self.boldCb.SetValue(ts.isBold)
self.italicCb.SetValue(ts.isItalic)
self.underlinedCb.SetValue(ts.isUnderlined)
self.block = False
self.previewCtrl.Refresh()
def OnMisc(self, event = None):
if (self.tsIndex == -1) or self.block:
return
ts = self.titles.pages[self.pageIndex][self.tsIndex]
ts.items = [util.toInputStr(s) for s in
misc.fromGUI(self.textEntry.GetValue()).split("\n")]
self.stringsLb.SetString(self.tsIndex, "--".join(ts.items))
ts.x = util.str2float(self.xEntry.GetValue(), 0.0)
ts.y = util.str2float(self.yEntry.GetValue(), 0.0)
ts.setAlignment(self.alignCombo.GetClientData(self.alignCombo.GetSelection()))
self.xEntry.Enable(not ts.isCentered)
ts.size = util.getSpinValue(self.sizeEntry)
ts.font = self.fontCombo.GetClientData(self.fontCombo.GetSelection())
ts.isBold = self.boldCb.GetValue()
ts.isItalic = self.italicCb.GetValue()
ts.isUnderlined = self.underlinedCb.GetValue()
self.previewCtrl.Refresh()
class TitlesPreview(wx.Window):
def __init__(self, parent, ctrl, cfg):
wx.Window.__init__(self, parent, -1)
self.cfg = cfg
self.ctrl = ctrl
self.Bind(wx.EVT_SIZE, self.OnSize)
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
self.Bind(wx.EVT_PAINT, self.OnPaint)
def OnSize(self, event):
size = self.GetClientSize()
self.screenBuf = wx.Bitmap(size.width, size.height)
def OnEraseBackground(self, event):
pass
def OnPaint(self, event):
dc = wx.BufferedPaintDC(self, self.screenBuf)
# widget size
ww, wh = self.GetClientSize()
dc.SetBrush(wx.Brush(self.GetBackgroundColour()))
dc.SetPen(wx.Pen(self.GetBackgroundColour()))
dc.DrawRectangle(0, 0, ww, wh)
# aspect ratio of paper
aspect = self.cfg.paperWidth / self.cfg.paperHeight
# calculate which way we can best fit the paper on screen
h = wh
w = int(aspect * wh)
if w > ww:
w = ww
h = int(ww / aspect)
# offset of paper
ox = (ww - w) // 2
oy = (wh - h) // 2
dc.SetPen(wx.BLACK_PEN)
dc.SetBrush(wx.WHITE_BRUSH)
dc.DrawRectangle(ox, oy, w, h)
if self.ctrl.pageIndex != -1:
page = self.ctrl.titles.pages[self.ctrl.pageIndex]
for i in range(len(page)):
ts = page[i]
# text height in mm
textHinMM = util.getTextHeight(ts.size)
textH = int((textHinMM / self.cfg.paperHeight) * h)
textH = max(1, textH)
y = ts.y
for line in ts.items:
# people may have empty lines in between non-empty
# lines to achieve double spaced lines; don't draw a
# rectangle for lines consisting of nothing but
# whitespace
if line.strip():
textW = int((util.getTextWidth(line, ts.getStyle(),
ts.size) / self.cfg.paperWidth) * w)
textW = max(1, textW)
if ts.isCentered:
xp = w // 2 - textW // 2
else:
xp = int((ts.x / self.cfg.paperWidth) * w)
if ts.isRightJustified:
xp -= textW
if i == self.ctrl.tsIndex:
dc.SetPen(wx.RED_PEN)
dc.SetBrush(wx.RED_BRUSH)
else:
dc.SetPen(wx.BLACK_PEN)
dc.SetBrush(wx.BLACK_BRUSH)
yp = int((y / self.cfg.paperHeight) * h)
dc.DrawRectangle(ox + xp, oy + yp, textW, textH)
y += textHinMM