~ubuntu-branches/ubuntu/karmic/tovid/karmic

« back to all changes in this revision

Viewing changes to libtovid/gui/panels.py

  • Committer: Bazaar Package Importer
  • Author(s): Matvey Kozhev
  • Date: 2008-01-24 22:04:40 UTC
  • Revision ID: james.westby@ubuntu.com-20080124220440-x7cheljduf1rdgnq
Tags: upstream-0.31
ImportĀ upstreamĀ versionĀ 0.31

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#! /usr/bin/env python
 
2
# panels.py
 
3
 
 
4
import os
 
5
import wx
 
6
 
 
7
import libtovid
 
8
from libtovid.gui.configs import TovidConfig
 
9
from libtovid.gui.constants import *
 
10
from libtovid.gui.controls import BoldToggleButton, FlexTreeCtrl, HeadingText
 
11
from libtovid.gui.dialogs import FontChooserDialog
 
12
from libtovid.gui.icons import MenuIcon, SlideIcon, VideoIcon, DiscIcon, GroupIcon
 
13
from libtovid.gui.options import DiscOptions, MenuOptions, VideoOptions, GroupOptions
 
14
from libtovid.gui import util
 
15
from libtovid.gui.util import _, VER_GetFirstChild, VideoStatSeeker
 
16
from libtovid.author import Group
 
17
 
 
18
__all__ = [\
 
19
    "AuthorFilesTaskPanel",
 
20
    "DiscLayoutPanel",
 
21
    "EncodingPanel",
 
22
    "BurnDiscPanel",
 
23
    "CommandOutputPanel",
 
24
    "DiscPanel",
 
25
    "GroupPanel",
 
26
    "GuidePanel",
 
27
    "HidablePanel",
 
28
    "MenuPanel",
 
29
    "VideoPanel",
 
30
    "PlaylistTabPanel",
 
31
    "MenuTabPanel",
 
32
    "ThumbnailTabPanel",
 
33
    "DebugTabPanel"]
 
34
 
 
35
class AuthorFilesTaskPanel(wx.Panel):
 
36
    """A three-step interface for authoring video files to disc.
 
37
    Uses DiscLayoutPanel, EncodingPanel, and BurnDiscPanel."""
 
38
 
 
39
    def __init__(self, parent, id):
 
40
        wx.Panel.__init__(self, parent, id)
 
41
 
 
42
        # Global configuration
 
43
        self.curConfig = TovidConfig()
 
44
 
 
45
        # Task panels
 
46
        self.panDiscLayout = DiscLayoutPanel(self, wx.ID_ANY)
 
47
        self.panEncoding = EncodingPanel(self, wx.ID_ANY)
 
48
        self.panBurnDisc = BurnDiscPanel(self, wx.ID_ANY)
 
49
        self.panEncoding.Hide()
 
50
        self.panBurnDisc.Hide()
 
51
        # Keep an eye on the current panel
 
52
        self.curPanel = self.panDiscLayout
 
53
 
 
54
        # 3-step buttons (toggle buttons)
 
55
        # Layout / Encode / Burn
 
56
 
 
57
        # ======================
 
58
        # gettext test
 
59
        self.btnLayout = BoldToggleButton(self, wx.ID_ANY, _("1. Layout"))
 
60
        self.btnEncode = BoldToggleButton(self, wx.ID_ANY, _("2. Encode"))
 
61
        self.btnBurn = BoldToggleButton(self, wx.ID_ANY, _("3. Burn"))
 
62
        # ======================
 
63
 
 
64
        self.btnLayout.SetToolTipString("Modify the arrangement of videos "
 
65
            "and menus on the disc")
 
66
        self.btnEncode.SetToolTipString("Set up and encode the videos "
 
67
            "and menus in the current disc layout")
 
68
        self.btnBurn.SetToolTipString("Burn the completed disc")
 
69
 
 
70
        # Toggle button events
 
71
        wx.EVT_TOGGLEBUTTON(self, self.btnLayout.GetId(), self.OnLayout)
 
72
        wx.EVT_TOGGLEBUTTON(self, self.btnEncode.GetId(), self.OnEncode)
 
73
        wx.EVT_TOGGLEBUTTON(self, self.btnBurn.GetId(), self.OnBurn)
 
74
        self.btnLayout.SetValue(True)
 
75
        self.btnEncode.Enable(False)
 
76
        self.btnBurn.Enable(False)
 
77
 
 
78
        # 3-step sizer
 
79
        self.sizSteps = wx.BoxSizer(wx.HORIZONTAL)
 
80
        self.sizSteps.Add(self.btnLayout, 1, wx.EXPAND | wx.ALL, 2)
 
81
        self.sizSteps.Add(self.btnEncode, 1, wx.EXPAND | wx.ALL, 2)
 
82
        self.sizSteps.Add(self.btnBurn, 1, wx.EXPAND | wx.ALL, 2)
 
83
 
 
84
        # Task sizer. Holds panel(s) related to current task
 
85
        # (layout, encoding, burning). Shows only one panel
 
86
        # at a time.
 
87
        self.sizTask = wx.BoxSizer(wx.VERTICAL)
 
88
        self.sizTask.Add(self.panDiscLayout, 1, wx.EXPAND)
 
89
 
 
90
        # Main sizer. Holds task (layout, encoding) panel and 3-step buttons
 
91
        self.sizMain = wx.BoxSizer(wx.VERTICAL)
 
92
        self.sizMain.Add(self.sizSteps, 0, wx.EXPAND | wx.ALL, 6)
 
93
        self.sizMain.Add(wx.StaticLine(self, wx.ID_ANY, style = wx.LI_HORIZONTAL),
 
94
            0, wx.EXPAND)
 
95
        self.sizMain.Add(self.sizTask, 1, wx.EXPAND)
 
96
        self.SetSizer(self.sizMain)
 
97
 
 
98
    def EncodeOK(self, ok):
 
99
        """Indicate whether it's okay to begin encoding."""
 
100
        if ok == True:
 
101
            self.btnEncode.Enable(True)
 
102
        else:
 
103
            self.btnEncode.Enable(False)
 
104
 
 
105
    def BurnOK(self, ok):
 
106
        """Indicate whether it's okay to begin burning."""
 
107
        if ok:
 
108
            self.btnBurn.Enable(True)
 
109
        else:
 
110
            self.btnBurn.Enable(False)
 
111
 
 
112
       
 
113
    def OnLayout(self, evt):
 
114
        """Display controls for doing disc layout."""
 
115
        # Set buttons
 
116
        self.btnLayout.SetValue(True)
 
117
        self.btnEncode.SetValue(False)
 
118
        self.btnBurn.SetValue(False)
 
119
        # If Layout is showing, do nothing else
 
120
        if self.curPanel == self.panDiscLayout:
 
121
            return
 
122
        # Clear Encoding panel command list
 
123
        self.panEncoding.panCmdList.Clear()
 
124
        # Show Layout panel
 
125
        self.curPanel = self.panDiscLayout
 
126
        self.sizTask.Remove(0)
 
127
        self.panEncoding.Hide()
 
128
        self.panBurnDisc.Hide()
 
129
        self.sizTask.Add(self.panDiscLayout, 1, wx.EXPAND)
 
130
        self.panDiscLayout.Show()
 
131
        self.sizTask.Layout()
 
132
        self.curPanel = self.panDiscLayout
 
133
        # Set buttons
 
134
        self.btnLayout.SetValue(True)
 
135
        self.btnEncode.SetValue(False)
 
136
        self.btnBurn.SetValue(False)
 
137
        # Set appropriate guide text
 
138
        self.curConfig.panGuide.SetTask(ID_TASK_GETTING_STARTED)
 
139
 
 
140
    def OnEncode(self, evt):
 
141
 
 
142
        # Clear Encoding panel command list
 
143
        self.panEncoding.panCmdList.Clear()
 
144
 
 
145
        # Abort encoding if detected an error
 
146
        if self.btnLayout.GetValue() == True:
 
147
            CanContinue = self.panDiscLayout.PerformSanityCheckOnFiles(self)
 
148
            if CanContinue == False: 
 
149
                # Set buttons
 
150
                self.btnLayout.SetValue(True)
 
151
                self.btnEncode.SetValue(False)
 
152
                self.btnBurn.SetValue(False)
 
153
                return
 
154
 
 
155
        """Display controls for encoding."""
 
156
        # Set buttons
 
157
        self.btnLayout.SetValue(False)
 
158
        self.btnEncode.SetValue(True)
 
159
        self.btnBurn.SetValue(False)
 
160
        # If Encode is showing, do nothing else
 
161
        if self.curPanel == self.panEncoding:
 
162
            return
 
163
        # Show Encode panel
 
164
        self.curPanel = self.panEncoding
 
165
        self.sizTask.Remove(0)
 
166
        self.panDiscLayout.Hide()
 
167
        self.panBurnDisc.Hide()
 
168
        self.sizTask.Add(self.panEncoding, 1, wx.EXPAND)
 
169
        self.panEncoding.Show()
 
170
        self.sizTask.Layout()
 
171
        self.curPanel = self.panEncoding
 
172
        # Give command list to encoding panel
 
173
        cmdList = self.panDiscLayout.GetAllCommands()
 
174
        self.panEncoding.SetCommands(cmdList)
 
175
        # Set appropriate guide text
 
176
        self.curConfig.panGuide.SetTask(ID_TASK_PREP_ENCODING)
 
177
 
 
178
    def OnBurn(self, evt):
 
179
        """Display controls for burning the disc."""
 
180
        # Set buttons
 
181
        self.btnLayout.SetValue(False)
 
182
        self.btnEncode.SetValue(False)
 
183
        self.btnBurn.SetValue(True)
 
184
        # If Burn is showing, do nothing
 
185
        if self.curPanel == self.panBurnDisc:
 
186
            return
 
187
        # Show Burn panel
 
188
        self.curPanel = self.panBurnDisc
 
189
        self.sizTask.Remove(0)
 
190
        self.panDiscLayout.Hide()
 
191
        self.panEncoding.Hide()
 
192
        self.sizTask.Add(self.panBurnDisc, 1, wx.EXPAND)
 
193
        self.panBurnDisc.Show()
 
194
        self.sizTask.Layout()
 
195
        self.curPanel = self.panBurnDisc
 
196
        # Generate the default commandlist
 
197
        self.panBurnDisc.SetCommands()
 
198
        # Set appropriate guide text
 
199
        self.curConfig.panGuide.SetTask(ID_TASK_BURN_DISC)
 
200
 
 
201
 
 
202
class BurnDiscPanel(wx.Panel):
 
203
    """A panel of controls for burning a disc"""
 
204
 
 
205
    def __init__(self, parent, id):
 
206
        wx.Panel.__init__(self, parent, id)
 
207
 
 
208
        # Keep track of parent
 
209
        self.parent = parent
 
210
        self.device = "/dev/dvdrw"
 
211
        self.doAuthor = True
 
212
        self.doBurn = False
 
213
 
 
214
        self.txtHeading = HeadingText(self, wx.ID_ANY, "Author and burn")
 
215
 
 
216
        self.chkDoAuthor = wx.CheckBox(self, wx.ID_ANY, "Author disc structure")
 
217
        self.chkDoBurn = wx.CheckBox(self, wx.ID_ANY, "Burn disc")
 
218
        self.chkDoAuthor.SetToolTipString("Create the disc filesystem " \
 
219
                "hierarchy using dvdauthor")
 
220
        self.chkDoBurn.SetToolTipString("Burn a disc in the selected " \
 
221
                "device")
 
222
        self.chkDoAuthor.SetValue(self.doAuthor)
 
223
        self.chkDoBurn.SetValue(self.doBurn)
 
224
        wx.EVT_CHECKBOX(self, self.chkDoAuthor.GetId(), self.OnDoAuthor)
 
225
        wx.EVT_CHECKBOX(self, self.chkDoBurn.GetId(), self.OnDoBurn)
 
226
 
 
227
        self.lblDiscDevice = wx.StaticText(self, wx.ID_ANY, "Burn to device:")
 
228
        self.txtDiscDevice = wx.TextCtrl(self, wx.ID_ANY, self.device)
 
229
        wx.EVT_TEXT(self, self.txtDiscDevice.GetId(), self.OnSetDevice)
 
230
 
 
231
        # Sizer to hold burning controls
 
232
        self.sizBurn = wx.FlexGridSizer(4, 2, 8, 8)
 
233
        self.sizBurn.Add((2, 2))
 
234
        self.sizBurn.Add(self.chkDoAuthor, 0, wx.EXPAND)
 
235
        self.sizBurn.Add((2, 2))
 
236
        self.sizBurn.Add(self.chkDoBurn, 0, wx.EXPAND)
 
237
        self.sizBurn.Add(self.lblDiscDevice, 0, wx.EXPAND)
 
238
        self.sizBurn.Add(self.txtDiscDevice, 1, wx.EXPAND)
 
239
 
 
240
        # Command window
 
241
        self.panCmdList = CommandOutputPanel(self, wx.ID_ANY)
 
242
        self.panCmdList.Enable(False)
 
243
 
 
244
        # Start button
 
245
        self.btnStart = wx.Button(self, wx.ID_ANY, "Start")
 
246
        wx.EVT_BUTTON(self, self.btnStart.GetId(), self.OnStart)
 
247
 
 
248
        # Sizer to hold controls
 
249
        self.sizMain = wx.BoxSizer(wx.VERTICAL)
 
250
        self.sizMain.Add(self.txtHeading, 0, wx.EXPAND | wx.ALL, 8)
 
251
        self.sizMain.Add(self.sizBurn, 0, wx.EXPAND | wx.ALL, 8)
 
252
        self.sizMain.Add(self.panCmdList, 1, wx.EXPAND | wx.ALL, 8)
 
253
        self.sizMain.Add(self.btnStart, 0, wx.EXPAND | wx.ALL, 8)
 
254
        self.SetSizer(self.sizMain)
 
255
    
 
256
    def OnDoAuthor(self, evt):
 
257
        self.doAuthor = evt.Checked()
 
258
        self.SetCommands()
 
259
        
 
260
    def OnDoBurn(self, evt):
 
261
        self.doBurn = evt.Checked()
 
262
        self.SetCommands()
 
263
 
 
264
    def OnSetDevice(self, evt):
 
265
        """Use the selected device."""
 
266
        self.device = self.txtDiscDevice.GetValue()
 
267
        self.SetCommands()
 
268
 
 
269
    def OnStart(self, evt):
 
270
        """Begin authoring and burning the disc."""
 
271
 
 
272
        msgWarning = wx.MessageDialog(self,
 
273
            "Disc burning is still experimental in this release,\n" \
 
274
            "and might not work. Please report any bugs you encounter!",
 
275
            "Experimental feature", wx.ICON_INFORMATION | wx.OK)
 
276
        msgWarning.ShowModal()
 
277
 
 
278
        #Disable button to prevent e.g. double clicks
 
279
        self.btnStart.Enable(False)
 
280
        #Clear any previous errors from previous attempts at burning
 
281
        self.panCmdList.errorOccurred = False
 
282
        self.panCmdList.Start()
 
283
 
 
284
    def ProcessingDone(self, errorOccurred):
 
285
        """Signify that disc burning is done."""
 
286
        # Let user know if error(s) occurred
 
287
        if errorOccurred:
 
288
            msgError = wx.MessageDialog(self,
 
289
                "Error(s) occurred during burning. If you want to\n" \
 
290
                "help improve this software, please file a bug report\n" \
 
291
                "containing a copy of the output log. \n" \
 
292
                "Sorry for the inconvenience!",
 
293
                "Error occurred during burning", wx.ICON_ERROR | wx.OK)
 
294
            msgError.ShowModal()
 
295
        # Show success message and enable burning
 
296
        else:
 
297
            if self.doBurn:
 
298
                strSuccess = "Done burning the disc."
 
299
            else:
 
300
                strSuccess = "Done authoring the disc."
 
301
            msgSuccess = wx.MessageDialog(self, strSuccess, "Success!",
 
302
                wx.ICON_INFORMATION | wx.OK)
 
303
            msgSuccess.ShowModal()
 
304
 
 
305
 
 
306
            #Set the command, so can do another burn immediately
 
307
            self.SetCommandsWithoutClearingList()
 
308
 
 
309
        self.btnStart.Enable(True)
 
310
 
 
311
    def SetCommands(self):
 
312
        """Set command-list to be executed based on Author and Burn boxes."""
 
313
        self.panCmdList.Clear()
 
314
        self.SetCommandsWithoutClearingList()
 
315
 
 
316
    def SetCommandsWithoutClearingList(self):
 
317
        makedvdOptions = ""
 
318
 
 
319
        # Get global config (for XML filename and format)
 
320
        curConfig = TovidConfig()
 
321
 
 
322
        # Construct authoring/imaging/burning options
 
323
        if curConfig.curDiscFormat == 'vcd' or \
 
324
           curConfig.curDiscFormat == 'svcd':
 
325
            strAuthorCmd = "makevcd -quiet -overwrite "
 
326
        else:
 
327
            strAuthorCmd = "makedvd -quiet -author "
 
328
        if self.doBurn:
 
329
            strAuthorCmd += "-burn -device %s " % (self.device)
 
330
        strAuthorCmd += "%s.xml" % (curConfig.strOutputXMLFile)
 
331
 
 
332
        self.panCmdList.Enable(True)
 
333
        self.panCmdList.Execute(strAuthorCmd)
 
334
 
 
335
# ===================================================================
 
336
class CommandOutputPanel(wx.Panel):
 
337
    """A panel for executing and logging command-lines.
 
338
 
 
339
    To use CommandOutputPanel, the parent should implement a function
 
340
    ProcessingDone(bool errStatus), which is called by CommandOutputPanel to
 
341
    notify the parent that the panel is done running commands.  Presumably, the
 
342
    commands that are executing must finish before proceeding with another
 
343
    operation, so this function is used to let the parent know that the commands
 
344
    are done executing."""
 
345
    # Current (running) process
 
346
    process = None
 
347
    # Has an error occurred in any of the commands?
 
348
    errorOccurred = False
 
349
    # Command queue (starts out empty)
 
350
    strCmdQueue = []
 
351
    strCurCmd = ""
 
352
      
 
353
    def __init__(self, parent, id):
 
354
        wx.Panel.__init__(self, parent, id)
 
355
        
 
356
        # Process list and labels
 
357
        self.lblCurCmd = wx.StaticText(self, wx.ID_ANY, _("Running command:"))
 
358
        self.txtCurCmd = wx.TextCtrl(self, wx.ID_ANY, "")
 
359
        self.txtCurCmd.Enable(False)
 
360
        self.lblQueue = wx.StaticText(self, wx.ID_ANY, _("Commands left to run:") + '0')
 
361
 
 
362
        # Output window (fixed-width font)
 
363
        self.txtOut = wx.TextCtrl(self, wx.ID_ANY,
 
364
            style = wx.TE_MULTILINE | wx.TE_READONLY)
 
365
        self.txtOut.SetFont(wx.Font(10, wx.FONTFAMILY_TELETYPE,
 
366
            wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL))
 
367
        self.txtOut.AppendText(_("The following commands will be run:") + \
 
368
            "\n=========================================\n")
 
369
 
 
370
        # Log window font size widget
 
371
        self.lblFontSize = wx.StaticText(self, wx.ID_ANY, _("Log window font size:"))
 
372
        strFontSizes = ["8", "10", "12", "14"]
 
373
        self.cboFontSize = wx.ComboBox(self, wx.ID_ANY, "10", choices = strFontSizes,
 
374
            style = wx.CB_READONLY)
 
375
        wx.EVT_TEXT(self, self.cboFontSize.GetId(), self.OnFontSize)
 
376
 
 
377
        # Save log button
 
378
        self.btnSaveLog = wx.Button(self, wx.ID_ANY, _("Save log"))
 
379
        self.btnSaveLog.SetToolTipString(_("Save the output log to a file"))
 
380
        wx.EVT_BUTTON(self, self.btnSaveLog.GetId(), self.OnSaveLog)
 
381
 
 
382
        # Save as script button
 
383
        self.btnSaveAsScript = wx.Button(self, wx.ID_ANY, _("Save as script"))
 
384
        self.btnSaveAsScript.SetToolTipString(_("Save the commands as a script file"))
 
385
        wx.EVT_BUTTON(self, self.btnSaveAsScript.GetId(), self.OnSaveAsScript)
 
386
 
 
387
        # Keep track of who parent is
 
388
        self.parent = parent
 
389
 
 
390
        # Timer to produce continuous output and keep
 
391
        # running the next command
 
392
        self.idleTimer = wx.Timer(self, ID_CMD_TIMER)
 
393
        
 
394
        # Event handlers
 
395
        #wx.EVT_IDLE(self, self.OnIdle)
 
396
        wx.EVT_TIMER(self, ID_CMD_TIMER, self.OnIdleTime)
 
397
        wx.EVT_END_PROCESS(self, wx.ID_ANY, self.OnProcessEnded)
 
398
 
 
399
        # Current command sizer
 
400
        self.sizCurCmd = wx.BoxSizer(wx.HORIZONTAL)
 
401
        self.sizCurCmd.Add(self.lblCurCmd, 0, wx.EXPAND | wx.RIGHT, 6)
 
402
        self.sizCurCmd.Add(self.txtCurCmd, 1, wx.EXPAND)
 
403
        # Control sizer (may have more controls later)
 
404
        self.sizControl = wx.BoxSizer(wx.HORIZONTAL)
 
405
        self.sizControl.Add(self.lblQueue, 1, wx.EXPAND | wx.ALIGN_LEFT)
 
406
        self.sizControl.Add(self.lblFontSize, 0, wx.EXPAND | wx.ALIGN_RIGHT)
 
407
        self.sizControl.Add(self.cboFontSize, 0, wx.EXPAND | wx.ALIGN_RIGHT)
 
408
        self.sizControl.Add(self.btnSaveLog, 0, wx.EXPAND)
 
409
        self.sizControl.Add(self.btnSaveAsScript, 0, wx.EXPAND)
 
410
        # Main sizer
 
411
        self.sizMain = wx.BoxSizer(wx.VERTICAL)
 
412
        self.sizMain.Add(self.sizCurCmd, 0, wx.EXPAND | wx.BOTTOM, 6)
 
413
        self.sizMain.Add(self.txtOut, 3, wx.EXPAND | wx.BOTTOM, 6)
 
414
        self.sizMain.Add(self.sizControl, 0, wx.EXPAND | wx.BOTTOM, 6)
 
415
        self.SetSizer(self.sizMain)
 
416
 
 
417
    def __del__(self):
 
418
        """Detach any running process."""
 
419
        # Detach any running process
 
420
        if self.process:
 
421
            self.process.Detach()
 
422
 
 
423
    def OnFontSize(self, evt):
 
424
        """Change log window font size."""
 
425
        newFontSize = int(self.cboFontSize.GetValue())
 
426
        self.txtOut.SetFont(wx.Font(newFontSize, wx.FONTFAMILY_TELETYPE,
 
427
            wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL))
 
428
        #self.txtOut.Refresh()
 
429
 
 
430
    def OnSaveLog(self, evt):
 
431
        """Save output log to a file."""
 
432
        # Prompt for a filename
 
433
        outFileDlg = wx.FileDialog(self, _("Choose a filename to save to"),
 
434
            "", "", "*.*", wx.SAVE | wx.OVERWRITE_PROMPT)
 
435
        if outFileDlg.ShowModal() == wx.ID_OK:
 
436
            outFile = outFileDlg.GetPath()
 
437
            success = self.txtOut.SaveFile(outFile)
 
438
            if success:
 
439
                dlgAck = wx.MessageDialog(self,
 
440
                    _("The log was successfully saved to ") + outFile,
 
441
                    _("Log saved"), wx.OK | wx.ICON_INFORMATION)
 
442
            else:
 
443
                dlgAck = wx.MessageDialog(self,
 
444
                    _("The log could not be saved to ") + outFile + \
 
445
                    _("Maybe you don't have write permission to the given file?"),
 
446
                    _("Log not saved"), wx.OK | wx.ICON_INFORMATION)
 
447
 
 
448
            dlgAck.ShowModal()
 
449
            outFileDlg.Destroy()
 
450
 
 
451
    def OnSaveAsScript(self, evt):
 
452
        """Save commands as a script."""
 
453
        # Prompt for a filename
 
454
        msgExecFlagDlg = wx.MessageDialog(self, \
 
455
                    "Commands can only be run from partitions that have\n" \
 
456
                    "been mounted with the executable flag set. \n" \
 
457
                    "Consequently, in order to run this script, ensure that\n" \
 
458
                    "it is saved to an appropriate partition.", 
 
459
                    "Warning about partitions",
 
460
                    wx.OK | wx.ICON_ERROR)
 
461
        msgExecFlagDlg.ShowModal()
 
462
        msgExecFlagDlg.Destroy()
 
463
        outFileDlg = wx.FileDialog(self, _("Choose a filename to save to"),
 
464
            "", "", "*.*", wx.SAVE | wx.OVERWRITE_PROMPT)
 
465
        if outFileDlg.ShowModal() == wx.ID_OK:
 
466
            outFile = outFileDlg.GetPath()
 
467
            commandList = "#!/usr/bin/env bash\n"
 
468
            success = True
 
469
 
 
470
            for eachCommand in self.strCmdQueue:
 
471
                commandList = commandList + eachCommand + "\n"
 
472
            try:
 
473
                output = open(outFile,'w')
 
474
                output.writelines(commandList)
 
475
            except IOError:
 
476
                success = False
 
477
            else:
 
478
                output.close()
 
479
            os.chmod(outFile,0755)
 
480
            if success:
 
481
                dlgAck = wx.MessageDialog(self,
 
482
                    _("The commands were successfully saved to: \n") + outFile,
 
483
                    _("Script saved"), wx.OK | wx.ICON_INFORMATION)
 
484
            else:
 
485
                dlgAck = wx.MessageDialog(self,
 
486
                    _("The commands could not be saved to:\n") + outFile + "\n" + \
 
487
                    _("Maybe you don't have write permission to the given file?"),
 
488
                    _("Script not saved"), wx.OK | wx.ICON_INFORMATION)
 
489
 
 
490
            dlgAck.ShowModal()
 
491
            outFileDlg.Destroy()
 
492
 
 
493
    def OnIdleTime(self, evt):
 
494
        """Execute commands in the queue and print output to the log."""
 
495
        # If processing hasn't been started/enabled, do nothing
 
496
        if not self.idleTimer.IsRunning():
 
497
            return
 
498
        # If no process is currently executing, start the next
 
499
        # one in the queue.
 
500
        if self.process is None:
 
501
            self.StartNextProcess()
 
502
        # If a process is currently running, get its output and
 
503
        # print it to the command window
 
504
        if self.process is not None and self.pid != 0:
 
505
            stream = self.process.GetInputStream()
 
506
            if stream.CanRead():
 
507
                self.txtOut.AppendText(unicode(stream.read(), errors='ignore'))
 
508
            
 
509
    def StartNextProcess(self):
 
510
        """Start the next process in the queue."""
 
511
        # If there is a command in the queue
 
512
        if len(self.strCmdQueue) > 0:
 
513
            self.strCurCmd = self.strCmdQueue.pop(0)
 
514
            # Show the command that is being executed,
 
515
            # and the number remaining in queue
 
516
            self.txtCurCmd.SetValue(self.strCurCmd)
 
517
            self.txtOut.AppendText("Running command: %s\n" % self.strCurCmd)
 
518
            # Start the process running
 
519
            self.process = wx.Process(self)
 
520
            self.process.Redirect()
 
521
            self.pid = wx.Execute(self.strCurCmd, wx.EXEC_ASYNC, self.process)
 
522
            self.lblQueue.SetLabel("Commands left to run: %d" % \
 
523
                    len(self.strCmdQueue))
 
524
 
 
525
    def OnProcessEnded(self, evt):
 
526
        """Print any remaining output, and destroy the process."""
 
527
        # Get process exit status
 
528
        curExitStatus = evt.GetExitCode()
 
529
        # Print message to console if there was an error
 
530
        if curExitStatus != 0:
 
531
            print "ERROR:"
 
532
            print "The following command returned an exit status of %d:" % \
 
533
                    curExitStatus
 
534
            print self.strCurCmd
 
535
            print "Please report this bug on the tovid forum or IRC channel:"
 
536
            print "    Forum: http://www.createphpbb.com/phpbb/tovid.html"
 
537
            print "    IRC:   irc://irc.freenode.net/tovid"
 
538
            print "Include all the output shown above, as well as any output"
 
539
            print "shown in the log window of the tovid GUI."
 
540
            self.errorOccurred = True
 
541
        # Print any remaining output
 
542
        stream = self.process.GetInputStream()
 
543
        if stream.CanRead():
 
544
            self.txtOut.AppendText(unicode(stream.read(), errors='ignore'))
 
545
        self.process.Destroy()
 
546
        self.process = None
 
547
        # If there are more commands in the queue, start the next one
 
548
        if len(self.strCmdQueue) > 0:
 
549
            self.StartNextProcess()
 
550
        # Otherwise, stop running and update status messages
 
551
        else:
 
552
            self.idleTimer.Stop()
 
553
            self.txtCurCmd.SetValue("")
 
554
            self.lblQueue.SetLabel("Commands left to run: 0")
 
555
            # Let parent know that processing is done
 
556
            self.parent.ProcessingDone(self.errorOccurred)
 
557
            self.errorOccurred = False
 
558
    def Clear(self):
 
559
        """Clear out the command queue."""
 
560
        self.strCmdQueue = []
 
561
        self.txtCurCmd.SetValue("")
 
562
        self.txtOut.Clear()
 
563
        self.txtOut.AppendText("The following commands will be run:\n"
 
564
            "=========================================\n")
 
565
        self.lblQueue.SetLabel("Commands left to run: 0")
 
566
        self.process = None
 
567
 
 
568
    def Execute(self, command):
 
569
        """Put the given command-line string into the queue for execution."""
 
570
        self.strCmdQueue.append(command)
 
571
        self.txtOut.AppendText("%s\n------------------------\n" % command)
 
572
        # Update the queue size indicator
 
573
        self.lblQueue.SetLabel("Commands left to run: %d" % len(self.strCmdQueue))
 
574
        self.sizCurCmd.Layout()
 
575
 
 
576
    def Start(self):
 
577
        """Start processing all commands in queue."""
 
578
        # NOTE FOR FUTURE VERSION: Include capability to resume
 
579
        # a cancelled queue, or start over from the beginning
 
580
        # (requires keeping a copy of the original queue)
 
581
        # If already running, do nothing
 
582
        if self.idleTimer.IsRunning():
 
583
            return
 
584
        # Start running commands, if any remain to be executed
 
585
        if len(self.strCmdQueue) > 0:
 
586
            # Start the idle timer (1s interval)
 
587
            self.idleTimer.Start(1000)
 
588
        else:
 
589
            self.parent.ProcessingDone(self.errorOccurred)
 
590
 
 
591
    def Stop(self):
 
592
        """Stop processing and return current process to queue."""
 
593
        # Stop the idle timer and kill current process
 
594
        self.idleTimer.Stop()
 
595
        if self.process is not None:
 
596
            #wx.Kill(self.pid)
 
597
            self.process.Destroy()
 
598
            self.process = None
 
599
            # Return current command to queue
 
600
            self.strCmdQueue.insert(0, self.strCurCmd)
 
601
 
 
602
        # Reset indicators
 
603
        self.txtCurCmd.SetValue("")
 
604
        self.lblQueue.SetLabel("Commands left to run: %d" % len(self.strCmdQueue))
 
605
 
 
606
 
 
607
class DiscLayoutPanel(wx.Panel):
 
608
    """Panel for adding menus and videos to a disc, with configurations"""
 
609
 
 
610
    def __init__(self, parent, id):
 
611
        wx.Panel.__init__(self, parent, id)
 
612
 
 
613
        self.numMenus = 0   # Layout begins with no menus
 
614
        self.numGroups = 0   # Layout begins with no groups
 
615
        self.discFormat = 'dvd'
 
616
        self.discTVSystem = 'ntsc'
 
617
        self.parent = parent
 
618
        self.curConfig = TovidConfig()
 
619
        self.lastUsedPath = ""
 
620
 
 
621
        # Set up controls and sizers
 
622
        # Buttons and their tooltips
 
623
        self.btnAddVideos = wx.Button(self, wx.ID_ANY, "Add video(s)")
 
624
        #self.btnAddSlides = wx.Button(self, wx.ID_ANY, "Add slide(s)")
 
625
        self.btnAddGroup = wx.Button(self, wx.ID_ANY, "Add a group")
 
626
        self.btnAddMenu = wx.Button(self, wx.ID_ANY, "Add menu")
 
627
        self.btnMoveUp = wx.Button(self, wx.ID_ANY, "Move up")
 
628
        self.btnMoveDown = wx.Button(self, wx.ID_ANY, "Move down")
 
629
        self.btnRemove = wx.Button(self, wx.ID_ANY, "Remove")
 
630
        self.btnAddVideos.SetToolTipString("Add video files under this menu")
 
631
        #self.btnAddSlides.SetToolTipString("Add still-image files under this menu")
 
632
        self.btnAddGroup.SetToolTipString("Add group of videos under this menu")
 
633
        self.btnAddMenu.SetToolTipString("Add a navigation menu to the disc; "
 
634
            "you must add at least one navigation menu before adding any videos.")
 
635
        self.btnMoveUp.SetToolTipString("Move the current item up")
 
636
        self.btnMoveDown.SetToolTipString("Move the current item down")
 
637
        self.btnRemove.SetToolTipString("Remove the current item from the disc")
 
638
        wx.EVT_BUTTON(self, self.btnAddVideos.GetId(), self.OnAddVideos)
 
639
#        wx.EVT_BUTTON(self, self.btnAddSlides.GetId(), self.OnAddSlides)
 
640
        wx.EVT_BUTTON(self, self.btnAddGroup.GetId(), self.OnAddGroup)
 
641
        wx.EVT_BUTTON(self, self.btnAddMenu.GetId(), self.OnAddMenu)
 
642
        wx.EVT_BUTTON(self, self.btnMoveUp.GetId(), self.OnCuritemUp)
 
643
        wx.EVT_BUTTON(self, self.btnMoveDown.GetId(), self.OnCuritemDown)
 
644
        wx.EVT_BUTTON(self, self.btnRemove.GetId(), self.OnRemoveCuritem)
 
645
        # All buttons except AddMenu disabled to start with
 
646
        self.btnAddVideos.Enable(False)
 
647
        #self.btnAddSlides.Enable(False)
 
648
        self.btnAddGroup.Enable(False)
 
649
        self.btnMoveUp.Enable(False)
 
650
        self.btnMoveDown.Enable(False)
 
651
        self.btnRemove.Enable(False)
 
652
 
 
653
        # The disc layout tree
 
654
        self.discTree = FlexTreeCtrl(self, wx.ID_ANY, style = wx.TR_SINGLE \
 
655
                | wx.TR_HAS_BUTTONS | wx.TR_EDIT_LABELS | wx.SUNKEN_BORDER)
 
656
        # Icons to use in the tree
 
657
        iconSize = (16, 16)
 
658
        self.listIcons = wx.ImageList(iconSize[0], iconSize[1])
 
659
        self.idxIconMenu = self.listIcons.Add(MenuIcon())
 
660
        self.idxIconSlide = self.listIcons.Add(SlideIcon())
 
661
        self.idxIconGroup = self.listIcons.Add(GroupIcon())
 
662
        self.idxIconVideo = self.listIcons.Add(VideoIcon())
 
663
        self.idxIconDisc = self.listIcons.Add(DiscIcon())
 
664
        self.discTree.SetImageList(self.listIcons)
 
665
 
 
666
        # Root of disc. Non-deletable.
 
667
        self.rootItem = self.discTree.AddRoot("Untitled disc", self.idxIconDisc)
 
668
        self.discTree.SetPyData(self.rootItem, DiscOptions())
 
669
        self.discTree.SetToolTipString("Navigation layout of the disc. "
 
670
            "Use the buttons below to add elements. Click on the title of "
 
671
            "an element to edit it.")
 
672
        wx.EVT_TREE_SEL_CHANGED(self.discTree, self.discTree.GetId(), \
 
673
                self.OnTreeSelChanged)
 
674
        wx.EVT_TREE_END_LABEL_EDIT(self.discTree, self.discTree.GetId(), \
 
675
                self.OnTreeItemEdit)
 
676
        self.discTree.Expand(self.rootItem)
 
677
        # topItem starts out being root
 
678
        self.topItem = self.rootItem
 
679
        
 
680
        # Horizontal box sizer to hold tree manipulation buttons
 
681
        self.sizBtn = wx.GridSizer(2, 3, 0, 0)
 
682
        self.sizBtn.AddMany([ (self.btnAddMenu, 1, wx.EXPAND),
 
683
                               (self.btnRemove, 1, wx.EXPAND),
 
684
                               (self.btnMoveUp, 1, wx.EXPAND),
 
685
                               (self.btnAddVideos, 1, wx.EXPAND),
 
686
                               (self.btnAddGroup, 1, wx.EXPAND),
 
687
                               (self.btnMoveDown, 1, wx.EXPAND) ])
 
688
 
 
689
        # Outer vertical box sizer to hold tree and button-box
 
690
        self.sizTree = wx.BoxSizer(wx.VERTICAL)
 
691
        self.sizTree.Add(self.discTree, 1, wx.EXPAND | wx.ALL, 0)
 
692
        self.sizTree.Add(self.sizBtn, 0, wx.EXPAND)
 
693
 
 
694
        # Panel to contain disc options (format, tvsys, etc.)
 
695
        self.panDisc = DiscPanel(self, wx.ID_ANY)
 
696
        self.panDisc.SetOptions(self.discTree.GetPyData(self.rootItem))
 
697
        # Panels to contain video/menu/group encoding options
 
698
        self.panVideoOptions = VideoPanel(self, wx.ID_ANY)
 
699
        self.panMenuOptions = MenuPanel(self, wx.ID_ANY)
 
700
        self.panGroupOptions = GroupPanel(self, wx.ID_ANY)
 
701
        # Hide currently-unused options panels
 
702
        self.panVideoOptions.Hide()
 
703
        self.panMenuOptions.Hide()
 
704
        self.panGroupOptions.Hide()
 
705
        # Sizers to hold options panel (and more later?)
 
706
        self.sizOptions = wx.BoxSizer(wx.VERTICAL)
 
707
        self.sizOptions.Add(self.panDisc, 1, wx.EXPAND | wx.ALL, 8)
 
708
 
 
709
        # Horizontal splitter to hold tree and options panel
 
710
        self.sizTreeOpts = wx.BoxSizer(wx.HORIZONTAL)
 
711
        self.sizTreeOpts.Add(self.sizTree, 2, wx.EXPAND | wx.ALL, 8)
 
712
        self.sizTreeOpts.Add(self.sizOptions, 3, wx.EXPAND | wx.ALL, 0)
 
713
 
 
714
        # Main splitter to hold panels
 
715
        self.sizMain = wx.BoxSizer(wx.VERTICAL)
 
716
        self.sizMain.Add(self.sizTreeOpts, 1, wx.EXPAND | wx.ALL, 0)
 
717
        self.SetSizer(self.sizMain)
 
718
 
 
719
        # Select DVD/NTSC by default
 
720
        self.SetDiscFormat('dvd')
 
721
        self.SetDiscTVSystem('ntsc')
 
722
 
 
723
    def OnTreeSelChanged(self, evt):
 
724
        """Update controls when a tree selection changes."""
 
725
        curItem = self.discTree.GetSelection()
 
726
        curOpts = self.discTree.GetPyData(curItem)
 
727
        curType = curOpts.type
 
728
        curParent = self.discTree.GetItemParent(curItem)
 
729
 
 
730
        # If root is selected, dis/enable unusable buttons
 
731
        if curItem == self.rootItem:
 
732
            self.btnMoveUp.Enable(False)
 
733
            self.btnMoveDown.Enable(False)
 
734
            self.btnRemove.Enable(False)
 
735
            self.btnAddVideos.Enable(False)
 
736
            if self.numMenus == 0:
 
737
                self.btnAddMenu.Enable(True)
 
738
            else:
 
739
                self.btnAddMenu.Enable(False)
 
740
            #self.btnAddSlides.Enable(False)
 
741
            self.btnAddGroup.Enable(False)
 
742
        # Otherwise, enable usable buttons
 
743
        else:
 
744
            # Can always remove non-root items
 
745
            self.btnRemove.Enable(True)
 
746
            # Can only move up/down for items with siblings
 
747
            if self.discTree.GetChildrenCount(curParent, False) > 1:
 
748
                self.btnMoveUp.Enable(True)
 
749
                self.btnMoveDown.Enable(True)
 
750
            else:
 
751
                self.btnMoveUp.Enable(False)
 
752
                self.btnMoveDown.Enable(False)
 
753
            # If a non top-level menu is selected, enable the
 
754
            # AddVideos/AddSlides/AddGroup buttons
 
755
            if curType == ID_MENU and curItem != self.topItem:
 
756
                self.btnAddVideos.Enable(True)
 
757
                #self.btnAddSlides.Enable(True)
 
758
                self.btnAddGroup.Enable(True)
 
759
                if self.numMenus == 1:
 
760
                    self.btnAddMenu.Enable(True)
 
761
                else:
 
762
                    self.btnAddMenu.Enable(False)
 
763
            elif curType == ID_GROUP and curItem != self.topItem:
 
764
                self.btnAddVideos.Enable(True)
 
765
                self.btnAddGroup.Enable(False)
 
766
                self.btnAddMenu.Enable(False)
 
767
                #self.btnAddSlides.Enable(True)
 
768
            # Otherwise, disable them
 
769
            else:
 
770
                self.btnAddVideos.Enable(False)
 
771
                #self.btnAddSlides.Enable(False)
 
772
                self.btnAddGroup.Enable(False)
 
773
                self.btnAddMenu.Enable(True)
 
774
 
 
775
        # If disc was selected, show disc options
 
776
        if curType == ID_DISC:
 
777
            self.sizOptions.Remove(0)
 
778
            self.sizOptions.Add(self.panDisc, 1, wx.EXPAND | wx.ALL, 8)
 
779
            self.panMenuOptions.Hide()
 
780
            self.panVideoOptions.Hide()
 
781
            self.panGroupOptions.Hide()
 
782
            self.panDisc.Show()
 
783
            self.sizOptions.Layout()
 
784
 
 
785
            # Set appropriate guide text
 
786
            self.curConfig.panGuide.SetTask(ID_TASK_DISC_SELECTED)
 
787
 
 
788
        # For a video, show video encoding options
 
789
        elif curType == ID_VIDEO:
 
790
            # Remove existing panel and show panVideoOptions
 
791
            self.sizOptions.Remove(0)
 
792
            self.panVideoOptions.SetOptions(curOpts)
 
793
            self.sizOptions.Add(self.panVideoOptions, 1, wx.EXPAND | wx.ALL, 8)
 
794
            self.panDisc.Hide()
 
795
            self.panMenuOptions.Hide()
 
796
            self.panGroupOptions.Hide()
 
797
            self.panVideoOptions.Show()
 
798
            self.sizOptions.Layout()
 
799
 
 
800
            # Set appropriate guide text
 
801
            self.curConfig.panGuide.SetTask(ID_TASK_VIDEO_SELECTED)
 
802
 
 
803
        # For a menu, show menu encoding options
 
804
        elif curType == ID_MENU:
 
805
            # Do I need to do this?
 
806
            self.RefreshItem(curItem)
 
807
 
 
808
            # Remove existing panel and show panMenuOptions
 
809
            self.sizOptions.Remove(0)
 
810
            self.panMenuOptions.SetOptions(curOpts)
 
811
            self.sizOptions.Add(self.panMenuOptions, 1, wx.EXPAND | wx.ALL, 8)
 
812
            self.panDisc.Hide()
 
813
            self.panVideoOptions.Hide()
 
814
            self.panMenuOptions.Show()
 
815
            self.sizOptions.Layout()
 
816
        
 
817
            # Set appropriate guide text
 
818
            self.curConfig.panGuide.SetTask(ID_TASK_MENU_SELECTED)
 
819
 
 
820
        # For a group, show group options
 
821
        elif curType == ID_GROUP:
 
822
            # Remove existing panel and show panGroupOptions
 
823
            self.sizOptions.Remove(0)
 
824
            self.panGroupOptions.SetOptions(curOpts)
 
825
            self.sizOptions.Add(self.panGroupOptions, 1, wx.EXPAND | wx.ALL, 8)
 
826
            self.panDisc.Hide()
 
827
            self.panMenuOptions.Hide()
 
828
            self.panVideoOptions.Hide()
 
829
            self.panGroupOptions.Show()
 
830
            self.sizOptions.Layout()
 
831
 
 
832
            # Set appropriate guide text
 
833
            self.curConfig.panGuide.SetTask(ID_TASK_GROUP_SELECTED)
 
834
 
 
835
    def OnTreeItemEdit(self, evt):
 
836
        """Update controls when a tree item's title is edited."""
 
837
        if not evt.IsEditCancelled():
 
838
            curItem = evt.GetItem()
 
839
            curOpts = self.discTree.GetPyData(curItem)
 
840
            curOpts.title = evt.GetLabel()
 
841
            # Assign outPrefix based on title
 
842
            curOpts.outPrefix = curOpts.title.replace(' ', '_')
 
843
 
 
844
            # Update the appropriate panel
 
845
            if curOpts.type == ID_DISC:
 
846
                self.panDisc.SetOptions(curOpts)
 
847
            elif curOpts.type == ID_MENU:
 
848
                self.panMenuOptions.SetOptions(curOpts)
 
849
            elif curOpts.type == ID_VIDEO:
 
850
                self.panVideoOptions.SetOptions(curOpts)
 
851
                
 
852
                # Update the titles shown list in the menu panel
 
853
                
 
854
                # Get the parent menu
 
855
                curParent = self.discTree.GetItemParent(curItem)
 
856
                # and the options for the parent menu
 
857
                parentOpts = self.discTree.GetPyData(curParent)
 
858
                if parentOpts.type == ID_MENU:
 
859
                    # Get the text of the item as it was before it the change
 
860
                    curText = self.discTree.GetItemText(curItem)
 
861
                    # find the title index to change
 
862
                    for titleIndex in range(len(parentOpts.titles)):
 
863
                        # compare title with old text
 
864
                        if parentOpts.titles[titleIndex] == curText:
 
865
                            # item found, change it
 
866
                            parentOpts.titles[titleIndex] = evt.GetLabel()
 
867
                            # get out of the loop
 
868
                            break
 
869
            elif curOpts.type == ID_GROUP:
 
870
                self.panGroupOptions.SetOptions(curOpts)
 
871
                
 
872
                # Update the titles shown list in the menu panel
 
873
                
 
874
                # Get the parent menu
 
875
                curParent = self.discTree.GetItemParent(curItem)
 
876
                # and the options for the parent menu
 
877
                parentOpts = self.discTree.GetPyData(curParent)
 
878
                # Get the text of the item as it was before it the change
 
879
                curText = self.discTree.GetItemText(curItem)
 
880
                # find the title index to change
 
881
                for titleIndex in range(len(parentOpts.titles)):
 
882
                    # compare title with old text
 
883
                    if parentOpts.titles[titleIndex] == curText:
 
884
                        # item found, change it
 
885
                        parentOpts.titles[titleIndex] = evt.GetLabel()
 
886
                        # get out of the loop
 
887
                        break
 
888
 
 
889
    def OnAddMenu(self, evt):
 
890
        """Append a menu to the disc."""
 
891
        self.numMenus += 1
 
892
        # If this is the second menu on the disc, and a top menu does
 
893
        # not already exist, create a top menu and insert the current
 
894
        # menu after it
 
895
        if self.numMenus == 2 and self.topItem == self.rootItem:
 
896
            oldMenu, cookie = VER_GetFirstChild(self.discTree, self.topItem)
 
897
            self.topItem = self.discTree.AppendItem(self.rootItem,
 
898
                "Main menu", self.idxIconMenu)
 
899
            # Create a new top menu at the root of the tree
 
900
            self.discTree.SetPyData(self.topItem,
 
901
                MenuOptions(self.discFormat, self.discTVSystem, \
 
902
                        "Main menu", True))
 
903
            copiedMenu = self.discTree.AppendItemMove(self.topItem, oldMenu)
 
904
            # Refresh the copied menu
 
905
            self.RefreshItem(copiedMenu)
 
906
            self.discTree.Expand(copiedMenu)
 
907
            # One more menu (top menu); commented to preserve menu numbering
 
908
            #self.numMenus += 1
 
909
 
 
910
        # New menu is appended under topItem
 
911
        curItem = self.discTree.AppendItem(self.topItem,
 
912
            "Untitled menu %d" % self.numMenus, self.idxIconMenu)
 
913
        self.discTree.SetPyData(curItem, MenuOptions(self.discFormat,
 
914
            self.discTVSystem, "Untitled menu %d" % self.numMenus))
 
915
        # Refresh the current item (for empty menus, just adds the "Back" button)
 
916
        self.RefreshItem(curItem)
 
917
        # Expand, show, and select the new menu
 
918
        self.discTree.EnsureVisible(curItem)
 
919
        self.discTree.Expand(curItem)
 
920
        self.discTree.SelectItem(curItem)
 
921
 
 
922
        # If tree has more than one item, enable encoding button
 
923
        if self.discTree.GetCount() > 1:
 
924
            self.parent.EncodeOK(True)
 
925
 
 
926
        # Refresh the top item to include any added menus
 
927
        self.RefreshItem(self.topItem)
 
928
 
 
929
        # Set appropriate guide text
 
930
        self.curConfig.panGuide.SetTask(ID_TASK_MENU_ADDED)
 
931
 
 
932
    def OnAddVideos(self, evt):
 
933
        """Add videos to the disc, under the currently-selected menu."""
 
934
        curParent = self.discTree.GetSelection()
 
935
 
 
936
        # If there are no menus yet, create one before adding
 
937
        # the videos under it
 
938
        if self.numMenus < 1:
 
939
            self.OnAddMenu(wx.CommandEvent())
 
940
 
 
941
        # If there are menus, but none is selected, ask user to
 
942
        # select a menu before adding videos
 
943
        elif self.numMenus > 0 and \
 
944
             self.discTree.GetPyData(curParent).type != ID_MENU and \
 
945
             self.discTree.GetPyData(curParent).type != ID_GROUP:
 
946
            # Show a message dialog
 
947
            msgDlg = wx.MessageDialog(self, "Please select a menu or group before adding videos",
 
948
                "No menu or group selected",
 
949
                wx.OK | wx.ICON_EXCLAMATION)
 
950
            msgDlg.ShowModal()
 
951
            msgDlg.Destroy()
 
952
            return
 
953
 
 
954
        # Open a file dialog for user to choose videos to add
 
955
        addFileDlg= wx.FileDialog(self, "Select video files", self.lastUsedPath,
 
956
            "", "*.*", wx.OPEN | wx.MULTIPLE)
 
957
        if addFileDlg.ShowModal() == wx.ID_OK:
 
958
            # Get all the filenames that were selected
 
959
            strPathnames = addFileDlg.GetPaths()
 
960
            # Remember the last used directory
 
961
            self.lastUsedPath = os.path.dirname(strPathnames[0])
 
962
            # Store the directory name for later use
 
963
            addFileDlg.Destroy()
 
964
        else:
 
965
            return
 
966
 
 
967
        # Append items as children of the current selection
 
968
        for curFile in range(len(strPathnames)):
 
969
            # Make sure path exists
 
970
            if not os.path.exists(strPathnames[ curFile ]):
 
971
                return
 
972
 
 
973
            # Use file basename, underscores replaced with spaces,
 
974
            # for the default title
 
975
            curTitle = os.path.basename(strPathnames[ curFile ]).replace('_', ' ')
 
976
            curItem = self.discTree.AppendItem(curParent, curTitle, self.idxIconVideo)
 
977
            curOpts = VideoOptions(self.discFormat, self.discTVSystem,
 
978
                strPathnames[ curFile ])
 
979
            self.discTree.SetPyData(curItem, curOpts)
 
980
            curStats = VideoStatSeeker(curOpts)
 
981
            curStats.start()
 
982
 
 
983
        # If tree has more than one item, enable encoding button
 
984
        if self.discTree.GetCount() > 1:
 
985
            self.parent.EncodeOK(True)
 
986
 
 
987
        self.discTree.EnsureVisible(curItem)
 
988
 
 
989
        # Refresh the parent to include all added videos
 
990
        self.RefreshItem(curParent)
 
991
        # Refresh the panel view of the parent menu
 
992
        self.discTree.SelectItem(curParent)
 
993
        curOpts = self.discTree.GetPyData(curParent)
 
994
 
 
995
        if self.discTree.GetPyData(curParent).type == ID_MENU:
 
996
            self.panMenuOptions.SetOptions(curOpts)
 
997
        elif self.discTree.GetPyData(curParent).type == ID_GROUP:
 
998
            self.panGroupOptions.SetOptions(curOpts)    
 
999
        # Set appropriate guide text
 
1000
        self.curConfig.panGuide.SetTask(ID_TASK_VIDEO_ADDED)
 
1001
 
 
1002
    def OnAddSlides(self, evt):
 
1003
        """Add slides to the disc structure. Not enabled yet."""
 
1004
        # Display a message and return
 
1005
        msgDlg = wx.MessageDialog(self, "Slideshows are not supported "
 
1006
            "in this version. Sorry!", "Slides not functional yet",
 
1007
            wx.OK | wx.ICON_EXCLAMATION)
 
1008
        msgDlg.ShowModal()
 
1009
        msgDlg.Destroy()
 
1010
        return
 
1011
 
 
1012
        # The real functionality, for when slides are supported
 
1013
        curParent = self.discTree.GetSelection()
 
1014
        # If a menu is not selected, print an error and return
 
1015
        if self.discTree.GetPyData(curParent).type != ID_MENU:
 
1016
            msgDlg = wx.MessageDialog(self, "Please select a menu before "
 
1017
               "adding slides.",
 
1018
               "No menu selected",
 
1019
               wx.OK | wx.ICON_EXCLAMATION)
 
1020
            msgDlg.ShowModal()
 
1021
            msgDlg.Destroy()
 
1022
            return
 
1023
 
 
1024
        # Open a file dialog for user to choose slides to add
 
1025
        addFileDlg= wx.FileDialog(self, "Select image files", "", "", "*.*",
 
1026
            wx.OPEN | wx.MULTIPLE)
 
1027
        if addFileDlg.ShowModal() == wx.ID_OK:
 
1028
            # Get all the filenames that were selected
 
1029
            strPathnames = addFileDlg.GetPaths()
 
1030
            # Store the directory name for later use
 
1031
            addFileDlg.Destroy()
 
1032
        else:
 
1033
            return
 
1034
 
 
1035
        # Append a new slide element containing the given list of files
 
1036
        curItem = self.discTree.AppendItem(curParent, "Untitled slides",
 
1037
            self.idxIconSlide)
 
1038
        curOpts = SlideOptions(self.discFormat, self.discTVSystem, strPathnames)
 
1039
        self.discTree.SetPyData(curItem, curOpts)
 
1040
 
 
1041
        self.discTree.EnsureVisible(curItem)
 
1042
 
 
1043
        # Refresh the parent to include all added videos
 
1044
        self.RefreshItem(curParent)
 
1045
 
 
1046
        # If tree has more than one item, enable encoding button
 
1047
        if self.discTree.GetCount() > 1:
 
1048
            self.parent.EncodeOK(True)
 
1049
 
 
1050
    def OnAddGroup(self, evt):
 
1051
        """Add group to the disc, under the currently-selected menu."""
 
1052
        self.numGroups += 1
 
1053
        curParent = self.discTree.GetSelection()
 
1054
 
 
1055
        # If there are no menus yet, create one before adding
 
1056
        # the videos under it
 
1057
        if self.numMenus < 1:
 
1058
            self.OnAddMenu(wx.CommandEvent())
 
1059
 
 
1060
        # If there are menus, but none is selected, ask user to
 
1061
        # select a menu before adding group
 
1062
        elif self.numMenus > 0 and \
 
1063
             self.discTree.GetPyData(curParent).type != ID_MENU:
 
1064
            # Show a message dialog
 
1065
            msgDlg = wx.MessageDialog(self, "Please select a menu before adding a group",
 
1066
                "No menu selected",
 
1067
                wx.OK | wx.ICON_EXCLAMATION)
 
1068
            msgDlg.ShowModal()
 
1069
            msgDlg.Destroy()
 
1070
            return
 
1071
 
 
1072
        # New menu is appended under curParent
 
1073
        curItem = self.discTree.AppendItem(curParent,
 
1074
            "Untitled group %d" % self.numGroups, self.idxIconGroup)
 
1075
        self.discTree.SetPyData(curItem, GroupOptions("Untitled group %d" % self.numGroups))
 
1076
 
 
1077
        # Refresh the current item (for empty menus, just adds the "Back" button)
 
1078
        self.RefreshItem(curItem)
 
1079
 
 
1080
        # Refresh the parent to include all added group
 
1081
        self.RefreshItem(curParent)
 
1082
 
 
1083
        # Expand, show, and select the new menu
 
1084
        self.discTree.EnsureVisible(curItem)
 
1085
        self.discTree.Expand(curItem)
 
1086
        self.discTree.SelectItem(curItem)
 
1087
    
 
1088
        # Set appropriate guide text
 
1089
        self.curConfig.panGuide.SetTask(ID_TASK_GROUP_ADDED)
 
1090
 
 
1091
 
 
1092
    def OnCuritemUp(self, evt):
 
1093
        """Move the currently-selected item up in the tree."""
 
1094
        self.discTree.MoveItemUp()
 
1095
        # Refresh the parent
 
1096
        curParent = self.discTree.GetItemParent(self.discTree.GetSelection())
 
1097
        self.RefreshItem(curParent)
 
1098
 
 
1099
    def OnCuritemDown(self, evt):
 
1100
        """Move the currently-selected item down in the tree."""
 
1101
        self.discTree.MoveItemDown()
 
1102
        # Refresh the parent
 
1103
        curParent = self.discTree.GetItemParent(self.discTree.GetSelection())
 
1104
        self.RefreshItem(curParent)
 
1105
 
 
1106
    def OnRemoveCuritem(self, evt):
 
1107
        """Remove the currently-selected item and its descendants from the
 
1108
        tree."""
 
1109
        curItem = self.discTree.GetSelection()
 
1110
        curParent = self.discTree.GetItemParent(curItem)
 
1111
 
 
1112
        # If root is selected, do nothing
 
1113
        if curItem == self.rootItem:
 
1114
            return
 
1115
 
 
1116
        # If the top item is selected, verify before deletion
 
1117
        elif curItem.IsOk() and curItem == self.topItem:
 
1118
            dlgConfirm = wx.MessageDialog(self,
 
1119
                "This will remove all menus, groups and videos\n"
 
1120
                "from the disc layout. Proceed?",
 
1121
                "Confirm removal", wx.YES_NO | wx.ICON_QUESTION)
 
1122
            if dlgConfirm.ShowModal() == wx.ID_YES:
 
1123
                self.discTree.Delete(curItem)
 
1124
                # Top item goes back to being root
 
1125
                self.topItem = self.rootItem
 
1126
            dlgConfirm.Destroy()
 
1127
            # Back to having 0 menus
 
1128
            self.numMenus = 0
 
1129
            self.btnAddMenu.Enable(True)
 
1130
 
 
1131
        # Make sure the item isn't root or topItem before being deleted
 
1132
        elif curItem.IsOk():
 
1133
            # If deleting a menu, confirm deletion and
 
1134
            # decrement the menu counter
 
1135
            if self.discTree.GetPyData(curItem).type == ID_MENU:
 
1136
                dlgConfirm = wx.MessageDialog(self,
 
1137
                    "This will remove the currently selected\n"
 
1138
                    "menu, along with all videos, groups and stills\n"
 
1139
                    "under it. Proceed?",
 
1140
                    "Confirm removal", wx.YES_NO | wx.ICON_QUESTION)
 
1141
                if dlgConfirm.ShowModal() == wx.ID_NO:
 
1142
                    return
 
1143
                self.numMenus -= 1
 
1144
            # If deleting a group, confirm deletion and
 
1145
            # decrement the group counter
 
1146
            elif self.discTree.GetPyData(curItem).type == ID_GROUP:
 
1147
                dlgConfirm = wx.MessageDialog(self,
 
1148
                    "This will remove the currently selected\n"
 
1149
                    "group, along with all videos \n"
 
1150
                    "under it. Proceed?",
 
1151
                    "Confirm removal", wx.YES_NO | wx.ICON_QUESTION)
 
1152
                if dlgConfirm.ShowModal() == wx.ID_NO:
 
1153
                    return
 
1154
                self.numGroups -= 1
 
1155
            # Delete the current item
 
1156
            self.discTree.Delete(curItem)
 
1157
 
 
1158
        # Refresh the parent
 
1159
        self.RefreshItem(curParent)
 
1160
 
 
1161
        # If only one item remains, disable encode button
 
1162
        if self.discTree.GetCount() < 2:
 
1163
            self.parent.EncodeOK(False)
 
1164
 
 
1165
 
 
1166
    def SetOptions(self, options):
 
1167
        """Set the encoding options associated with the current item."""
 
1168
        self.discTree.SetPyData(self.discTree.GetSelection(), options)
 
1169
 
 
1170
    def GetOptions(self):
 
1171
        """Get the encoding options associated with the current item."""
 
1172
        return self.discTree.GetPyData(self.discTree.GetSelection())
 
1173
 
 
1174
    def SetDiscFormat(self, format):
 
1175
        """Set the disc format (DVD, VCD, SVCD)."""
 
1176
        self.discFormat = format 
 
1177
        # Make video panel controls appropriate for this disc format
 
1178
        self.panVideoOptions.SetDiscFormat(format)
 
1179
        # Make menu panel controls appropriate for this disc format
 
1180
        self.panMenuOptions.SetDiscFormat(format)
 
1181
        # Make all encoding options in the disc compliant
 
1182
        format = self.discTree.GetPyData(self.rootItem).format
 
1183
        refs = self.discTree.GetReferenceList(self.rootItem)
 
1184
        for curItem in refs:
 
1185
            if curItem.type != ID_DISC:
 
1186
                curItem.SetDiscFormat(format)
 
1187
 
 
1188
    def SetDiscTVSystem(self, tvsys):
 
1189
        """Set the disc TV system (NTSC, PAL)."""
 
1190
        self.discTVSystem = tvsys
 
1191
        # Make video panel controls appropriate for this disc TVsystem
 
1192
        self.panVideoOptions.SetDiscTVSystem(tvsys)
 
1193
        # Make all encoding options in the disc compliant
 
1194
        tvsys = self.discTree.GetPyData(self.rootItem).tvsys
 
1195
        refs = self.discTree.GetReferenceList(self.rootItem)
 
1196
        for curItem in refs:
 
1197
            # Menus and slides need to know TV system
 
1198
            if curItem.type != ID_DISC:
 
1199
                curItem.SetDiscTVSystem(tvsys)
 
1200
 
 
1201
    def RefreshItem(self, curItem):
 
1202
        """Refresh the given tree item and make sure it is up-to-date.
 
1203
        Should be called for an item after its children have changed."""
 
1204
        curOpts = self.discTree.GetPyData(curItem)
 
1205
        # If it's a menu, fill it with the titles listed below it
 
1206
        if curOpts.type == ID_MENU:
 
1207
            curOpts.titles = []
 
1208
            curChild, cookie = VER_GetFirstChild(self.discTree, curItem)
 
1209
            while curChild.IsOk():
 
1210
                curOpts.titles.append(self.discTree.GetItemText(curChild))
 
1211
                curChild, cookie = self.discTree.GetNextChild(curItem, cookie)
 
1212
            # If this is not a top menu, add a "Back" title (link)
 
1213
            if not curOpts.isTopMenu and self.numMenus > 1:
 
1214
                curOpts.titles.append("Back")
 
1215
        elif curOpts.type == ID_GROUP:
 
1216
            curOpts.groupMemberCount = 0
 
1217
            curChild, cookie = VER_GetFirstChild(self.discTree, curItem)
 
1218
            while curChild.IsOk():
 
1219
                curOpts.groupMemberCount = curOpts.groupMemberCount + 1
 
1220
                curChild, cookie = self.discTree.GetNextChild(curItem, cookie)
 
1221
         
 
1222
    def UseForAllItems(self, opts):
 
1223
        """Use the given options for all videos/menus/slides."""
 
1224
        # Get references for all items
 
1225
        refs = self.discTree.GetReferenceList(self.rootItem)
 
1226
        # Count how many items are changed
 
1227
        countItems = 0
 
1228
        # Apply options to all items in the tree of the same type
 
1229
        # Don't copy to self
 
1230
        for curItem in refs:
 
1231
            if curItem != opts:
 
1232
                if curItem.type == opts.type:
 
1233
                    curItem.CopyFrom(opts)
 
1234
                    countItems += 1
 
1235
        return countItems
 
1236
 
 
1237
    def CheckForNoDuplicateOutputFiles(self, panel):
 
1238
        """Check for when two files have same output name"""       
 
1239
        # Get references for all items
 
1240
        refs = self.discTree.GetReferenceList(self.rootItem)
 
1241
        for curItem in refs:
 
1242
            #Check that no other references have same output name as this one
 
1243
            if curItem.type == ID_DISC:
 
1244
                #No possibility of duplication
 
1245
                continue;
 
1246
            elif curItem.type == ID_MENU:
 
1247
                outputFileName = "%s" % (curItem.outPrefix)
 
1248
            elif curItem.type == ID_VIDEO:
 
1249
                outputFileName = "%s" % (curItem.outPrefix)
 
1250
            elif curItem.type == ID_SLIDE:
 
1251
                continue;
 
1252
            elif curItem.type == ID_GROUP:
 
1253
                outputFileName = "%s" % (curItem.outPrefix)
 
1254
            sameNameCount = 0
 
1255
            for otherItem in refs:
 
1256
                if otherItem.type == ID_DISC:
 
1257
                    #No possibility of duplication
 
1258
                    continue;
 
1259
                elif otherItem.type == ID_MENU:
 
1260
                    otherOutputFileName = "%s" % (otherItem.outPrefix)
 
1261
                elif otherItem.type == ID_VIDEO:
 
1262
                    otherOutputFileName = "%s" % (otherItem.outPrefix)
 
1263
                elif otherItem.type == ID_SLIDE:
 
1264
                    continue;
 
1265
                elif otherItem.type == ID_GROUP:
 
1266
                    otherOutputFileName = "%s" % (otherItem.outPrefix)
 
1267
                if otherOutputFileName == outputFileName:
 
1268
                    sameNameCount = sameNameCount + 1
 
1269
                if sameNameCount > 1:
 
1270
                    msgImageFileMissingDlg = wx.MessageDialog(panel, \
 
1271
                       "Two menus, groups or videos have been given the same label.\n" \
 
1272
                       "Currently, this is not allowed.\n" \
 
1273
                       "This label is: %s\n\n" \
 
1274
                       "Please choose unique names." % (outputFileName),
 
1275
                       "Duplicate labels",
 
1276
                       wx.OK | wx.ICON_ERROR)
 
1277
                    msgImageFileMissingDlg.ShowModal()
 
1278
                    msgImageFileMissingDlg.Destroy()
 
1279
                    return False;
 
1280
        return True
 
1281
 
 
1282
    def CheckMenuCountLimitsNotExceeded(self, panel):
 
1283
        """Ensure the menu limit is not exceeded"""    
 
1284
        MAX_BUTTONS_ON_DVD_MENU = 36
 
1285
        MAX_BUTTONS_ON_SVCD_MENU = 9
 
1286
 
 
1287
        # Get all items in tree
 
1288
        items = self.discTree.GetItemList(self.rootItem)
 
1289
        for curItem in items:
 
1290
            curOpts = self.discTree.GetPyData(curItem)
 
1291
            if curOpts.type == ID_MENU:
 
1292
                #Need to check there are not too many items in menu
 
1293
                menuCount = self.discTree.GetChildrenCount(curItem, False)
 
1294
                tooManyButtonsOnMenu = False
 
1295
                if self.discFormat == 'dvd' and menuCount > MAX_BUTTONS_ON_DVD_MENU:
 
1296
                    tooManyButtonsOnMenu = True
 
1297
                elif self.discFormat in [ 'vcd', 'svcd' ] and menuCount > MAX_BUTTONS_ON_SVCD_MENU:
 
1298
                    tooManyButtonsOnMenu = True
 
1299
 
 
1300
                if tooManyButtonsOnMenu == True:
 
1301
                    msgTooManyButtonsDlg = wx.MessageDialog(panel, \
 
1302
                       "The number of buttons that can be on a menu is limited.\n" \
 
1303
                       "For DVDs, this limit is %s.\n" \
 
1304
                       "For (S)VCDs, this limit is %s\n\n" \
 
1305
                       "Currently, a menu has %s.\n" \
 
1306
                       "Please correct this." \
 
1307
                            % (MAX_BUTTONS_ON_DVD_MENU, MAX_BUTTONS_ON_SVCD_MENU, menuCount),
 
1308
                       "Too many buttons on menu",
 
1309
                       wx.OK | wx.ICON_ERROR)
 
1310
                    msgTooManyButtonsDlg.ShowModal()
 
1311
                    msgTooManyButtonsDlg.Destroy()
 
1312
                    return False;
 
1313
        return True
 
1314
 
 
1315
    def CheckForNoGroupsWhenNotDVD(self, panel):
 
1316
        """Check for invalid characters in filenames etc"""       
 
1317
        if self.discFormat in [ 'vcd', 'svcd' ] and self.numGroups > 0:
 
1318
            msgGroupsWhenSVCDDlg = wx.MessageDialog(panel, \
 
1319
                "Tovid does not currently support groups for (S)VCDs\n" \
 
1320
                "Please remove all groups or change to a DVD disc.",
 
1321
                "Groups not supported for (S)VCDs",
 
1322
                wx.OK | wx.ICON_ERROR)
 
1323
            msgGroupsWhenSVCDDlg.ShowModal()
 
1324
            msgGroupsWhenSVCDDlg.Destroy()
 
1325
            return False;
 
1326
        return True
 
1327
 
 
1328
    def PerformSanityCheckOnFiles(self, panel):
 
1329
        """Check for invalid characters in filenames etc"""       
 
1330
        # Get references for all items
 
1331
        refs = self.discTree.GetReferenceList(self.rootItem)
 
1332
        if self.CheckForNoGroupsWhenNotDVD(panel) == False:
 
1333
            return False
 
1334
        for curItem in refs:
 
1335
            if curItem.RelevantFilesAreOK(panel) == False:
 
1336
                return False
 
1337
        if self.CheckMenuCountLimitsNotExceeded(panel) == False:
 
1338
            return False
 
1339
        if self.CheckForNoDuplicateOutputFiles(panel) == False:
 
1340
            return False
 
1341
 
 
1342
        return True
 
1343
 
 
1344
    def GetAllCommands(self):
 
1345
        """Return an array of strings containing all encoding commands to be
 
1346
        executed."""
 
1347
        # Get references for all items
 
1348
        refs = self.discTree.GetReferenceList(self.rootItem)
 
1349
        # Send the reference list to the root DiscOptions item
 
1350
        # (so it can generate the authoring command)
 
1351
        discOpts = self.discTree.GetPyData(self.rootItem)
 
1352
        discOpts.SetLayout(refs)
 
1353
        # Pop root command off, since it needs to be
 
1354
        # put at the end of the command list
 
1355
        strDiscCmd = refs.pop(0).GetCommand()
 
1356
        # Append command associated with each item
 
1357
        commands = []
 
1358
        for curItem in refs:
 
1359
            #NB, groups do not actually require commands of their own
 
1360
            curCommand = curItem.GetCommand()
 
1361
            if curCommand != "":
 
1362
                commands.append(curCommand)
 
1363
        # Append root command
 
1364
        commands.append(strDiscCmd)
 
1365
        return commands
 
1366
 
 
1367
    def GetIcon(self, element):
 
1368
        if isinstance(element, Disc):
 
1369
            return self.idxIconDisc
 
1370
        elif isinstance(element, Menu):
 
1371
            return self.idxIconMenu
 
1372
        elif isinstance(element, Group):
 
1373
            return self.idxIconGroup
 
1374
        else:
 
1375
            return self.idxIconVideo
 
1376
 
 
1377
 
 
1378
class DiscPanel(wx.Panel):
 
1379
    """Panel for choosing disc format (DVD/VCD/SVCD, PAL/NTSC)"""
 
1380
 
 
1381
    def __init__(self, parent, id):
 
1382
        wx.Panel.__init__(self, parent, id)
 
1383
        
 
1384
        # Global configuration
 
1385
        self.curConfig = TovidConfig()
 
1386
 
 
1387
        # Disc options associated with this panel
 
1388
        self.curOptions = DiscOptions()
 
1389
        self.parent = parent
 
1390
 
 
1391
        # Disc format selector
 
1392
        self.lblDiscFormat = wx.StaticText(self, wx.ID_ANY, \
 
1393
                "Choose what kind of video disc you want to make:")
 
1394
        strFormats = ["VCD: Low-quality, up to about one hour of video",
 
1395
            "SVCD: Medium quality, 30 to 70 minutes of video",
 
1396
            "DVD: Range of quality, from 1 to 8 hours of video"]
 
1397
 
 
1398
        
 
1399
        self.rbFormat = wx.RadioBox(self, wx.ID_ANY, "Disc format", \
 
1400
                wx.DefaultPosition, wx.DefaultSize, strFormats, 1, \
 
1401
                wx.RA_SPECIFY_COLS)
 
1402
        self.rbFormat.SetToolTipString("Select what disc format you want to use."
 
1403
            " For VCD and SVCD, you can use a normal CD-recordable drive. For"
 
1404
            " DVD, you need a DVD-recordable drive.")
 
1405
        self.rbFormat.SetSelection(ID_FMT_DVD)
 
1406
        wx.EVT_RADIOBOX(self, self.rbFormat.GetId(), self.OnFormat)
 
1407
        
 
1408
        # Disc TV system selector
 
1409
        strTVSystems = ["NTSC: Used in the Americas and East Asia",
 
1410
                        "NTSC Film: Same as NTSC, with a film frame rate",
 
1411
                        "PAL: Used in most of Europe, Asia, and Africa"]
 
1412
        self.rbTVSystem = wx.RadioBox(self, wx.ID_ANY, "TV format", \
 
1413
                wx.DefaultPosition, wx.DefaultSize, strTVSystems, 1, \
 
1414
                wx.RA_SPECIFY_COLS)
 
1415
        self.rbTVSystem.SetToolTipString("Select NTSC or PAL, depending " \
 
1416
                "on what kind of TV you want to play the disc on.")
 
1417
        self.rbTVSystem.SetSelection(ID_TVSYS_NTSC)
 
1418
        wx.EVT_RADIOBOX(self, self.rbTVSystem.GetId(), self.OnTVSystem)
 
1419
 
 
1420
        # Disc options heading
 
1421
        self.txtHeading = HeadingText(self, wx.ID_ANY, "Disc")
 
1422
 
 
1423
        # Output directory
 
1424
        self.lblOutputDirectory = wx.StaticText(self, wx.ID_ANY, \
 
1425
                "Output directory:")
 
1426
        self.txtOutputDirectory = wx.TextCtrl(self, wx.ID_ANY,
 
1427
                self.curConfig.strOutputDirectory)
 
1428
        self.txtOutputDirectory.SetToolTipString("Type the full path of a "
 
1429
            "directory where you want to save finished videos and disc images, "
 
1430
            "or use the browse button. Should have 2-6GB of free space.")
 
1431
        self.btnBrowseOutputDirectory = wx.Button(self, wx.ID_ANY, "Browse")
 
1432
        wx.EVT_BUTTON(self, self.btnBrowseOutputDirectory.GetId(),
 
1433
            self.OnBrowseOutputDirectory)
 
1434
        wx.EVT_TEXT(self, self.txtOutputDirectory.GetId(),
 
1435
            self.OnEditOutputDirectory)
 
1436
 
 
1437
        # Sizer to hold working directory controls
 
1438
        self.sizDirs = wx.BoxSizer(wx.HORIZONTAL)
 
1439
        self.sizDirs.AddMany([\
 
1440
            (self.lblOutputDirectory, 0, wx.ALIGN_RIGHT | wx.ALL, 8),
 
1441
            (self.txtOutputDirectory, 1, wx.EXPAND | wx.ALL, 8),
 
1442
            (self.btnBrowseOutputDirectory, 0, wx.ALL, 8)
 
1443
            ])
 
1444
 
 
1445
        # Sizer to hold controls
 
1446
        self.sizMain = wx.BoxSizer(wx.VERTICAL)
 
1447
        self.sizMain.Add(self.txtHeading, 0, wx.EXPAND | wx.ALL, 8)
 
1448
        self.sizMain.Add(self.lblDiscFormat, 0, wx.EXPAND | wx.ALL, 8)
 
1449
        self.sizMain.Add(self.rbFormat, 0, wx.EXPAND | wx.ALL, 8)
 
1450
        self.sizMain.Add(self.rbTVSystem, 0, wx.EXPAND | wx.ALL, 8)
 
1451
        self.sizMain.Add(self.sizDirs, 0, wx.EXPAND | wx.ALL, 8)
 
1452
        self.SetSizer(self.sizMain)
 
1453
 
 
1454
    def OnFormat(self, evt):
 
1455
        """Set disc format according to radiobox."""
 
1456
        self.curOptions.format = util.ID_to_text('format', evt.GetInt())
 
1457
        # Tell parent to adjust disc format
 
1458
        self.parent.SetDiscFormat(self.curOptions.format)
 
1459
 
 
1460
    def OnTVSystem(self, evt):
 
1461
        """Set disc TV system according to radiobox."""
 
1462
        self.curOptions.tvsys = util.ID_to_text('tvsys', evt.GetInt())
 
1463
        # Tell parent to adjust disc TVSystem
 
1464
        self.parent.SetDiscTVSystem(self.curOptions.tvsys)
 
1465
 
 
1466
    def OnBrowseOutputDirectory(self, evt):
 
1467
        """Browse for output directory."""
 
1468
        workDirDlg = wx.DirDialog(self, "Select a directory for output",
 
1469
            style = wx.DD_DEFAULT_STYLE | wx.DD_NEW_DIR_BUTTON)
 
1470
        workDirDlg.SetPath(self.txtOutputDirectory.GetValue())
 
1471
        # Show the dialog
 
1472
        if workDirDlg.ShowModal() == wx.ID_OK:
 
1473
            self.curConfig.strOutputDirectory = workDirDlg.GetPath()
 
1474
            self.txtOutputDirectory.SetValue(self.curConfig.strOutputDirectory)
 
1475
            workDirDlg.Destroy()
 
1476
 
 
1477
    def OnEditOutputDirectory(self, evt):
 
1478
        """Update Config with newly-entered output directory."""
 
1479
        self.curConfig.strOutputDirectory = self.txtOutputDirectory.GetValue()
 
1480
 
 
1481
    def SetOptions(self, discOpts):
 
1482
        """Set control values based on the provided DiscOptions"""
 
1483
        self.curOptions = discOpts
 
1484
        self.rbFormat.SetSelection(util.text_to_ID(self.curOptions.format))
 
1485
        self.rbTVSystem.SetSelection(util.text_to_ID(self.curOptions.tvsys))
 
1486
        self.txtHeading.SetLabel("Disc options: %s" % self.curOptions.title)
 
1487
 
 
1488
 
 
1489
class EncodingPanel(wx.Panel):
 
1490
    """Encoding setup panel. Allow selection of output directory, display
 
1491
    estimated encoding and final size requirements, controls and log window for
 
1492
    running all encoding commands.
 
1493
    """
 
1494
    def __init__(self, parent, id):
 
1495
        wx.Panel.__init__(self, parent, id)
 
1496
        self.parent = parent
 
1497
        self.curConfig = TovidConfig()
 
1498
        # Command window
 
1499
        self.panCmdList = CommandOutputPanel(self, wx.ID_ANY)
 
1500
        # Start/interrupt button
 
1501
        self.btnStartStop = wx.Button(self, wx.ID_ANY, "Start encoding")
 
1502
        self.btnStartStop.SetToolTipString("Begin encoding and preparing " \
 
1503
            "all videos and menus on the disc")
 
1504
        # Button events
 
1505
        wx.EVT_BUTTON(self, self.btnStartStop.GetId(), self.OnStartStop)
 
1506
        # Sizer to hold controls
 
1507
        self.sizMain = wx.BoxSizer(wx.VERTICAL)
 
1508
        #self.sizMain.Add(self.sizEstimates, 0, wx.EXPAND | wx.ALL, 8)
 
1509
        self.sizMain.Add(self.panCmdList, 1, wx.EXPAND | wx.ALL, 8)
 
1510
        self.sizMain.Add(self.btnStartStop, 0, wx.EXPAND | wx.ALL, 8)
 
1511
        self.SetSizer(self.sizMain)
 
1512
 
 
1513
    def OnBrowseOutDir(self, evt):
 
1514
        """Browse for output directory."""
 
1515
        outDirDlg = wx.DirDialog(self, "Select a directory for output",
 
1516
            style = wx.DD_DEFAULT_STYLE | wx.DD_NEW_DIR_BUTTON)
 
1517
        if outDirDlg.ShowModal() == wx.ID_OK:
 
1518
            self.curConfig.strOutputDirectory = outDirDlg.GetPath()
 
1519
            self.txtOutDir.SetValue(self.curConfig.strOutputDirectory)
 
1520
            outDirDlg.Destroy()
 
1521
 
 
1522
    def OnStartStop(self, evt):
 
1523
        """Start/suspend/resume encoding."""
 
1524
        # If currently running, stop/suspend processing
 
1525
        if self.panCmdList.idleTimer.IsRunning():
 
1526
 
 
1527
# Disable 'suspend encoding' since it's not working
 
1528
            # Disable button temporarily, to allow processes to die
 
1529
#            self.btnStartStop.Enable(False)
 
1530
#            self.panCmdList.Stop()
 
1531
#            self.btnStartStop.SetLabel("Resume encoding")
 
1532
#            self.btnStartStop.SetToolTipString("Resume the encoding process " \
 
1533
#                "where it left off")
 
1534
            # Show message that processing stopped
 
1535
#            msgStopped = wx.MessageDialog(self,
 
1536
#                "Encoding is now suspended. You can continue\n" \
 
1537
#                "by selecting \"Resume encoding\".",
 
1538
#                "Encoding suspended", wx.OK | wx.ICON_INFORMATION)
 
1539
#            msgStopped.ShowModal()
 
1540
            # Give processes time to die before re-enabling button
 
1541
#            os.system("sleep 2s")
 
1542
#            self.btnStartStop.Enable(True)
 
1543
            pass
 
1544
        # Not running; start/resume processing
 
1545
        else:
 
1546
            self.curConfig.panGuide.SetTask(ID_TASK_ENCODING_STARTED)
 
1547
            self.panCmdList.Start()
 
1548
            self.btnStartStop.Enable(False)
 
1549
            self.btnStartStop.SetLabel("Currently encoding...")
 
1550
#            self.btnStartStop.SetToolTipString("Interrupt the current " \
 
1551
#                "encoding process and return the current command to the queue")
 
1552
 
 
1553
    def SetCommands(self, commands):
 
1554
        """Set command-list to be executed from DiscLayoutPanel."""
 
1555
        for cmd in commands:
 
1556
            self.panCmdList.Execute(cmd)
 
1557
 
 
1558
    def SetOutputDirectory(self, outDir):
 
1559
        """Set the output directory to use."""
 
1560
        self.txtOutDir.SetValue(outDir)
 
1561
          
 
1562
    def ProcessingDone(self, errorOccurred):
 
1563
        """Signify that processing (video encoding) is done."""
 
1564
        self.parent.btnBurn.Enable(False)
 
1565
        # Let user know if error(s) occurred
 
1566
        if errorOccurred:
 
1567
            msgError = wx.MessageDialog(self,
 
1568
                "Error(s) occurred during encoding. If you want to\n" \
 
1569
                "help improve this software, please file a bug report\n" \
 
1570
                "containing a copy of the output log. Unfortunately,\n" \
 
1571
                "this also means you won't be able to continue to\n" \
 
1572
                "burning your disc. Sorry for the inconvenience!",
 
1573
                "Error occurred during encoding", wx.ICON_ERROR | wx.OK)
 
1574
            msgError.ShowModal()
 
1575
        # Show success message and enable burning
 
1576
        else:
 
1577
            strSuccess = "Done encoding. You may now proceed " \
 
1578
                "with burning the disc."
 
1579
            msgSuccess = wx.MessageDialog(self, strSuccess, "Success!",
 
1580
                wx.ICON_INFORMATION | wx.OK)
 
1581
            msgSuccess.ShowModal()
 
1582
 
 
1583
            # Burning is OK now
 
1584
            self.parent.BurnOK(True)
 
1585
 
 
1586
        # Re-enable buttons
 
1587
        self.btnStartStop.SetLabel("Start encoding")
 
1588
        self.btnStartStop.Enable(True)
 
1589
 
 
1590
class GroupPanel(wx.Panel):
 
1591
    """A panel showing controls appropriate to encoding a group of videoes."""
 
1592
    def __init__(self, parent, id):
 
1593
        wx.Panel.__init__(self, parent, id)
 
1594
 
 
1595
        # Class data
 
1596
        self.curOptions = GroupOptions()
 
1597
        self.parent = parent
 
1598
 
 
1599
        # Group options heading
 
1600
        self.txtHeading = HeadingText(self, wx.ID_ANY, "Group options")
 
1601
 
 
1602
        # Add controls to main vertical sizer
 
1603
        self.sizMain = wx.BoxSizer(wx.VERTICAL)
 
1604
        self.sizMain.Add(self.txtHeading, 0, wx.EXPAND | wx.ALL, 8)
 
1605
        self.SetSizer(self.sizMain)
 
1606
 
 
1607
    def SetOptions(self, groupOpts):
 
1608
        """Set control values based on the provided GroupOptions."""
 
1609
        self.curOptions = groupOpts
 
1610
 
 
1611
        self.txtHeading.SetLabel("Group options: %s" % self.curOptions.title)
 
1612
 
 
1613
    def GetOptions(self):
 
1614
        """Return the currently-set encoding options."""
 
1615
        return self.curOptions
 
1616
 
 
1617
class GuidePanel(wx.Panel):
 
1618
    """A panel showing live context-sensitive help."""
 
1619
 
 
1620
    def __init__(self, parent, id):
 
1621
        wx.Panel.__init__(self, parent, id)
 
1622
        # Heading
 
1623
        self.txtHeading = HeadingText(self, wx.ID_ANY, "What to do next")
 
1624
        # Guide text box
 
1625
        self.txtGuide = wx.TextCtrl(self, wx.ID_ANY, "",
 
1626
            style = wx.TE_MULTILINE | wx.TE_READONLY)
 
1627
        # Initialize text
 
1628
        self.InitTaskText()
 
1629
        self.SetTask(ID_TASK_GETTING_STARTED)
 
1630
        # Main sizer
 
1631
        self.sizMain = wx.BoxSizer(wx.VERTICAL)
 
1632
        self.sizMain.Add(self.txtHeading, 0, wx.EXPAND | wx.ALL, 8)
 
1633
        self.sizMain.Add(self.txtGuide, 1, wx.EXPAND | wx.ALL, 8)
 
1634
        self.SetSizer(self.sizMain)
 
1635
 
 
1636
    def InitTaskText(self):
 
1637
        """Initialize task-based guide text for all tasks."""
 
1638
        self.strGettingStarted = _("Welcome to the tovid GUI. This program is " \
 
1639
            "designed to help you create a video disc (VCD, SVCD, or DVD) " \
 
1640
            "from videos that you provide. All you need is a CD or DVD " \
 
1641
            "recorder, and one or more videos in almost any format.\n\n" \
 
1642
            "If you don't want to see this help window, select the \"View\" " \
 
1643
            "menu, and then \"Show guide\". Do the same if you want to turn " \
 
1644
            "this help window back on at any time.\n\n" \
 
1645
            "First choose the type of disc you want to create. " \
 
1646
            "VCD and SVCD can be burned to normal CD-R discs with a CD-R(W) drive. " \
 
1647
            "For DVD, you will need a DVD-recordable drive. VCD is a low-resolution " \
 
1648
            "format that can hold about 1 hour of video; SVCD is a medium-resolution " \
 
1649
            "format that also holds about 1 hour of video. DVD is a very versatile " \
 
1650
            "format that can hold anywhere from 1 to 9 hours of video, depending " \
 
1651
            "on the resolution and quality you choose.\n\n" \
 
1652
            "You should also select NTSC or PAL, depending on what kind of TV " \
 
1653
            "you have. NTSC is most popular in the United States and East Asia, " \
 
1654
            "while PAL is commonly used in most of Europe, Asia, and Africa. " \
 
1655
            "(If you aren't sure which one to use, check the manual for your TV " \
 
1656
            "or DVD player.) For NTSC, you can choose between regular NTSC and " \
 
1657
            "NTSC film; use NTSC film to encode using a film frame rate (appropriate " \
 
1658
            "for source videos using a film framerate, or for converting from PAL).\n\n" \
 
1659
            "You can also select a directory to use for output. Output files will " \
 
1660
            "go here, as well as any temporary files created during encoding. " \
 
1661
            "Make sure the directory has plenty of space (2-6 GB).\n\n" \
 
1662
            "Click \"Add menu\" when you are ready to begin adding content to " \
 
1663
            "your disc.")
 
1664
        self.strDiscSelected = _("The disc item in the disc layout tree is " \
 
1665
            "now selected. You can change the title of the current disc " \
 
1666
            "by clicking once on the disc title in the tree.\n\n" \
 
1667
            "When you select the disc item in the tree, a set of disc options " \
 
1668
            "are displayed. Here, you can choose what kind of disc to create " \
 
1669
            "(VCD, SVCD, or DVD) and television system to use (NTSC or PAL). " \
 
1670
            "You can also choose a working directory. This is where " \
 
1671
            "encoded videos and temporary files created during encoding will be " \
 
1672
            "stored. Make sure you choose a directory with plenty of space " \
 
1673
            "(2-6 GB).\n\n")
 
1674
        self.strMenuAdded = _("A menu has been added to the disc. Menus are " \
 
1675
            "represented by a menu icon in the disc layout tree. The first menu " \
 
1676
            "shown in the tree is the first thing you will see when you " \
 
1677
            "put the disc in your DVD player. A menu may have other menus " \
 
1678
            "or videos listed below it. You can add videos now by selecting a menu " \
 
1679
            "and choosing \"Add video(s)\".\n\n")
 
1680
        self.strMenuSelected = _("A menu is now selected. You can change the " \
 
1681
            "title of the menu by clicking once on its title in the disc layout " \
 
1682
            "tree.\n\n" \
 
1683
            "When you select a menu, a set of options are displayed. You can " \
 
1684
            "select a background image or audio clip to use for the menu. " \
 
1685
            "Background images can be in any standard image format such as " \
 
1686
            "JPEG, PNG, BMP, or GIF, and audio clips can be in any standard " \
 
1687
            "audio format such as WAV or MP3. It's best to choose a short " \
 
1688
            "audio clip (30 to 60 seconds in length).\n\n" \
 
1689
            "Each menu has a list of titles, for each item below the menu in the " \
 
1690
            "disc layout tree. These titles will be displayed on the finished menu " \
 
1691
            "in a certain font and color; you can specify what font and color(s) to " \
 
1692
            "use in the menu options panel.\n\n" \
 
1693
            "If you want to use the same background, font, and color settings for " \
 
1694
            "all the menus on your disc, first create all the menus, and configure " \
 
1695
            "one of them the way you like. Then select \"Use these settings for "\
 
1696
            "all menus\". The current settings will be applied to all other " \
 
1697
            "existing menus on the disc.")
 
1698
        self.strVideoAdded = _("One or more videos have been added to the disc. " \
 
1699
            "Notice that your videos have been added to the list of titles in " \
 
1700
            "the currently-selected menu.\n\n" \
 
1701
            "Videos are represented by a film-strip icon in the disc layout tree. " \
 
1702
            "Click on a video in the tree to view options for that video.")
 
1703
        self.strVideoSelected = _("A video is now selected. You can change the " \
 
1704
            "title of the video by clicking once on its title in the disc layout " \
 
1705
            "tree.\n\n" \
 
1706
            "Videos can be in almost any video format, and tovid will convert them " \
 
1707
            "for you. When you select a video, a set of options are displayed. " \
 
1708
            "Here, you can choose what resolution and aspect ratio to use for the " \
 
1709
            "video\n\n" \
 
1710
            "You can preview the video in GMplayer using the \"Preview video\" " \
 
1711
            "button. This is useful if you aren't sure what aspect ratio or " \
 
1712
            "resolution is most appropriate for the video.\n\n" \
 
1713
            "If you want to use the same resolution, bitrate and other options for all " \
 
1714
            "the videos on your disc, first add all the videos you want, and " \
 
1715
            "configure one of them the way you like. Then select \"Use these settings " \
 
1716
            "for all videos\". The current settings will be applied to all other " \
 
1717
            "existing videos on the disc.\n\n" \
 
1718
            "Use the \"Remove\" button to remove a video from the disc. You can " \
 
1719
            "also use the \"Move up\" and \"Move down\" buttons to rearrange the " \
 
1720
            "video within the tree.\n\n" \
 
1721
            "When you are satisfied with the titles, menu options, and video options " \
 
1722
            "for your disc, select \"2. Encode\" to proceed with encoding " \
 
1723
            "and authoring your disc.")
 
1724
        self.strGroupAdded = _("One or more groups have been added to the disc. " \
 
1725
            "Notice that your groups have been added to the list of titles in " \
 
1726
            "the currently-selected menu.\n\n" \
 
1727
            "Groups are represented by a multiple film icon in the disc layout tree. " \
 
1728
            "Click on a group in the tree to view options for that group.")
 
1729
        self.strGroupSelected = _("A group is now selected. You can change the " \
 
1730
            "title of the group by clicking once on its title in the disc layout " \
 
1731
            "tree.\n\n" \
 
1732
            "When you are satisfied with the titles, menu, group and video options " \
 
1733
            "for your disc, select \"2. Encode\" to proceed with encoding " \
 
1734
            "and authoring your disc.")
 
1735
        self.strPrepEncoding = _("You are now ready to begin the process of encoding " \
 
1736
            "all the menus and videos you have selected for your disc.\n\n" \
 
1737
            "Here, a log window displays a list of all the commands that will be " \
 
1738
            "executed. Select \"Start encoding\" to begin.\n\n" \
 
1739
            "If you change your mind and want to go back to the disc layout panel, " \
 
1740
            "select \"1. Layout\".")
 
1741
        self.strEncodingStarted = _("The encoding process has begun. Each of the " \
 
1742
            "necessary commands will be executed sequentially. Output from the " \
 
1743
            "currently-running command is displayed in the log window, so you " \
 
1744
            "can monitor its progress. The currently-running command is shown " \
 
1745
            "above the log window, and the number of commands remaining is shown " \
 
1746
            "below the log window.\n\n" \
 
1747
            "Be advised that video encoding can be a very time-consuming process! " \
 
1748
            "A disc with one hour of video content may take from 1-3 hours to encode, " \
 
1749
            "depending on your CPU speed. You may need to leave this running for " \
 
1750
            "several hours in order for your disc to finish authoring.")
 
1751
 
 
1752
    def SetTask(self, curTask = ID_TASK_GETTING_STARTED):
 
1753
        """Show the appropriate guide text for the given task."""
 
1754
        if curTask == ID_TASK_GETTING_STARTED:
 
1755
            self.txtHeading.SetLabel(_("Getting started"))
 
1756
            self.txtGuide.SetValue(self.strGettingStarted)
 
1757
        elif curTask == ID_TASK_MENU_ADDED:
 
1758
            self.txtHeading.SetLabel(_("Menu added"))
 
1759
            self.txtGuide.SetValue(self.strMenuAdded)
 
1760
        elif curTask == ID_TASK_VIDEO_ADDED:
 
1761
            self.txtHeading.SetLabel(_("Video added"))
 
1762
            self.txtGuide.SetValue(self.strVideoAdded)
 
1763
        elif curTask == ID_TASK_DISC_SELECTED:
 
1764
            self.txtHeading.SetLabel(_("Disc selected"))
 
1765
            self.txtGuide.SetValue(self.strDiscSelected)
 
1766
        elif curTask == ID_TASK_MENU_SELECTED:
 
1767
            self.txtHeading.SetLabel(_("Menu selected"))
 
1768
            self.txtGuide.SetValue(self.strMenuSelected)
 
1769
        elif curTask == ID_TASK_VIDEO_SELECTED:
 
1770
            self.txtHeading.SetLabel(_("Video selected"))
 
1771
            self.txtGuide.SetValue(self.strVideoSelected)
 
1772
        elif curTask == ID_TASK_PREP_ENCODING:
 
1773
            self.txtHeading.SetLabel(_("Encoding preparation"))
 
1774
            self.txtGuide.SetValue(self.strPrepEncoding)
 
1775
        elif curTask == ID_TASK_ENCODING_STARTED:
 
1776
            self.txtHeading.SetLabel(_("Encoding started"))
 
1777
            self.txtGuide.SetValue(self.strEncodingStarted)
 
1778
        elif curTask == ID_TASK_GROUP_ADDED:
 
1779
            self.txtHeading.SetLabel(_("Group added"))
 
1780
            self.txtGuide.SetValue(self.strGroupAdded)
 
1781
        elif curTask == ID_TASK_GROUP_SELECTED:
 
1782
            self.txtHeading.SetLabel(_("Video selected"))
 
1783
            self.txtGuide.SetValue(self.strGroupSelected)
 
1784
 
 
1785
 
 
1786
class HidablePanel(wx.Panel):
 
1787
    """A panel that can be hidden from view.
 
1788
 
 
1789
    The panel may be horizontal or vertical, and contains show/hide controls
 
1790
    along with one additional window or sizer, added via the Add() method.
 
1791
 
 
1792
    To use HidablePanel, first declare a HidablePanel object. Then, create the
 
1793
    wx.Window object that will go into the HidablePanel, passing the
 
1794
    HidablePanel as its parent. Add the HidablePanel to the desired location
 
1795
    (inside another sizer). Finally, call SetParent with the containing sizer as
 
1796
    the argument, to let the HidablePanel know what sizer contains it.
 
1797
    """
 
1798
 
 
1799
    def __init__(self, parent, id, orientation = wx.VERTICAL):
 
1800
        """Initialize hidable panel. orientation is wx.VERTICAL or
 
1801
        wx.HORIZONTAL. A vertical panel has the hide controls at the top and
 
1802
        extends downwards; a horizontal one has controls on the left and extends
 
1803
        rightwards.
 
1804
        """
 
1805
        wx.Panel.__init__(self, parent, id)
 
1806
        # Reference to contained content (window or sizer)
 
1807
        self.content = None
 
1808
        self.sizParent = None
 
1809
        # Show/hide button
 
1810
        self.btnShowHide = wx.ToggleButton(self, wx.ID_ANY, _("More >>"))
 
1811
        self.btnShowHide.SetValue(True)
 
1812
        wx.EVT_TOGGLEBUTTON(self, self.btnShowHide.GetId(), self.ShowHide)
 
1813
 
 
1814
        self.sizMain = wx.BoxSizer(orientation)
 
1815
        self.sizMain.Add(self.btnShowHide, 0)
 
1816
        self.SetSizer(self.sizMain)
 
1817
 
 
1818
    def SetContent(self, newContent):
 
1819
        """Set the window/sizer that the panel will contain."""
 
1820
        self.content = newContent
 
1821
        self.sizMain.Add(self.content, 1, wx.EXPAND)
 
1822
        self.sizMain.SetItemMinSize(self.content, 200, 200)
 
1823
        self.sizMain.Layout()
 
1824
    
 
1825
    def SetParent(self, parent):
 
1826
        """Set the parent sizer (the sizer that holds the HidablePanel)."""
 
1827
        self.sizParent = parent
 
1828
 
 
1829
    def ShowHide(self, evt):
 
1830
        """Show/hide the main part of the sizer based on state of
 
1831
        btnShowHide."""
 
1832
        # If button is down, show content
 
1833
        if self.btnShowHide.GetValue() == True:
 
1834
            self.sizMain.Add(self.content)
 
1835
            self.content.Show()
 
1836
            self.sizMain.Layout()
 
1837
        # Otherwise, hide content
 
1838
        else:
 
1839
            self.sizMain.Remove(self.content)
 
1840
            self.content.Hide()
 
1841
            self.sizMain.Layout()
 
1842
        # Layout parent, if it has been set
 
1843
        if self.sizParent != None:
 
1844
            self.sizParent.Layout()
 
1845
 
 
1846
 
 
1847
class MenuPanel(wx.Panel):
 
1848
    """A panel showing controls appropriate to generating a menu"""
 
1849
    def __init__(self, parent, id):
 
1850
        wx.Panel.__init__(self, parent, id)
 
1851
        self.curOptions = MenuOptions()
 
1852
        self.curColorData = wx.ColourData()
 
1853
        self.parent = parent
 
1854
        self.sboxBG = wx.StaticBox(self, wx.ID_ANY, "Menu background options")
 
1855
        self.sizBG = wx.StaticBoxSizer(self.sboxBG, wx.VERTICAL)
 
1856
 
 
1857
        # Background image/audio selection controls =======================\
 
1858
        ## Menu title
 
1859
        self.lblMenuTitle = wx.StaticText(self, wx.ID_ANY, "Menu Header:")
 
1860
        self.txtMenuTitle = wx.TextCtrl(self, wx.ID_ANY)
 
1861
        self.txtMenuTitle.SetToolTipString(\
 
1862
            "Enter a header for your menu. Leave blank if you" \
 
1863
            " don't want one.")
 
1864
        wx.EVT_TEXT(self, self.txtMenuTitle.GetId(), self.OnMenuTitle)
 
1865
        ## Menu title font size
 
1866
        self.lblMenuTitleFontSize = wx.StaticText(self, wx.ID_ANY, "Size:")
 
1867
        self.txtMenuTitleFontSize = wx.TextCtrl(self, wx.ID_ANY)
 
1868
        self.txtMenuTitleFontSize.SetToolTipString(\
 
1869
                "Specify the header's font size")
 
1870
        wx.EVT_TEXT(self, self.txtMenuTitleFontSize.GetId(), \
 
1871
                self.OnMenuTitleFontSize)
 
1872
        self.sizMenuTitleFontSize = wx.BoxSizer(wx.HORIZONTAL)
 
1873
        self.sizMenuTitleFontSize.Add(self.lblMenuTitleFontSize, 0,
 
1874
            wx.ALIGN_CENTER_VERTICAL | wx.ALL, 4)
 
1875
        self.sizMenuTitleFontSize.Add(self.txtMenuTitleFontSize, 1, 
 
1876
            wx.EXPAND | wx.ALL, 4)
 
1877
        ## Image
 
1878
        self.lblBGImage = wx.StaticText(self, wx.ID_ANY, "Image:")
 
1879
        self.txtBGImage = wx.TextCtrl(self, wx.ID_ANY)
 
1880
        self.txtBGImage.SetToolTipString(\
 
1881
            "Type the full name of the image file you want to use in the" \
 
1882
            " background of the menu, or use the browse button.")
 
1883
        self.btnBrowseBGImage = wx.Button(self, wx.ID_ANY, "Choose image...")
 
1884
        self.btnBrowseBGImage.SetToolTipString("Browse for an image file to " \
 
1885
            "use for the background of the menu")
 
1886
        wx.EVT_TEXT(self, self.txtBGImage.GetId(), self.OnBGImage)
 
1887
        wx.EVT_BUTTON(self, self.btnBrowseBGImage.GetId(), self.OnBrowseBGImage)
 
1888
        ## Audio
 
1889
        self.lblBGAudio = wx.StaticText(self, wx.ID_ANY, "Audio:")
 
1890
        self.txtBGAudio = wx.TextCtrl(self, wx.ID_ANY)
 
1891
        self.txtBGAudio.SetToolTipString(\
 
1892
            "Type the full name of the audio file " \
 
1893
            "you want to play while the menu is shown, " \
 
1894
            "or use the browse button.")
 
1895
        self.btnBrowseBGAudio = wx.Button(self, wx.ID_ANY, "Choose audio...")
 
1896
        self.btnBrowseBGAudio.SetToolTipString(\
 
1897
            "Browse for an audio file to " \
 
1898
            "play while the menu is shown")
 
1899
        wx.EVT_TEXT(self, self.txtBGAudio.GetId(), self.OnBGAudio)
 
1900
        wx.EVT_BUTTON(self, self.btnBrowseBGAudio.GetId(), self.OnBrowseBGAudio)
 
1901
        ## Length
 
1902
        self.lblMenuLength = wx.StaticText(self, wx.ID_ANY, "Length (sec):")
 
1903
        self.txtMenuLength = wx.TextCtrl(self, wx.ID_ANY)
 
1904
        self.txtMenuLength.SetToolTipString(\
 
1905
            "Set the length of the menu in seconds. " \
 
1906
            "Leave blank to use the full-length of your audio file.")
 
1907
        wx.EVT_TEXT(self, self.txtMenuLength.GetId(), self.OnMenuLength)
 
1908
        ## Group background controls together
 
1909
        self.sizBGInner = wx.FlexGridSizer(4, 3, 4, 8)
 
1910
        self.sizBGInner.AddMany([
 
1911
            (self.lblMenuTitle, 0, wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL),
 
1912
            (self.txtMenuTitle, 0, wx.EXPAND),
 
1913
            (self.sizMenuTitleFontSize), 
 
1914
            (self.lblBGImage, 0, wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL),
 
1915
            (self.txtBGImage, 1, wx.EXPAND),
 
1916
            (self.btnBrowseBGImage, 0, wx.EXPAND),
 
1917
            (self.lblBGAudio, 0, wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL),
 
1918
            (self.txtBGAudio, 1, wx.EXPAND),
 
1919
            (self.btnBrowseBGAudio, 0, wx.EXPAND), 
 
1920
            (self.lblMenuLength, 0, wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL),
 
1921
            (self.txtMenuLength, 1),
 
1922
            ((0, 0), 0) ])
 
1923
        self.sizBGInner.AddGrowableCol(1)
 
1924
        ## Add inner sizer to outer staticbox sizer
 
1925
        self.sizBG.Add(self.sizBGInner, 0, wx.EXPAND | wx.ALL, 8)
 
1926
 
 
1927
        # Menu font and size controls =====================================\
 
1928
        self.lblFontFace = wx.StaticText(self, wx.ID_ANY, "Font:")
 
1929
        self.btnFontChooserDialog = wx.Button(self, wx.ID_ANY, "Default")
 
1930
        self.btnFontChooserDialog.SetToolTipString(\
 
1931
                "Select a font to use for the menu text")
 
1932
        wx.EVT_BUTTON(self, self.btnFontChooserDialog.GetId(), \
 
1933
                self.OnFontSelection)
 
1934
        self.lblFontSize = wx.StaticText(self, wx.ID_ANY, "Size:")
 
1935
        self.txtFontSize = wx.TextCtrl(self, wx.ID_ANY)
 
1936
        self.txtFontSize.SetToolTipString(\
 
1937
                "Specify the font size")
 
1938
        wx.EVT_TEXT(self, self.txtFontSize.GetId(), \
 
1939
                self.OnFontSize)
 
1940
        ## Place font items together
 
1941
        self.sizFontFace = wx.BoxSizer(wx.HORIZONTAL)
 
1942
        self.sizFontFace.AddMany([\
 
1943
                (self.lblFontFace, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 4),
 
1944
                (self.btnFontChooserDialog, 3, wx.ALL, 4),
 
1945
                (self.lblFontSize, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 4),
 
1946
                (self.txtFontSize, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 4) ])
 
1947
        # Text alignment
 
1948
        self.lblAlignment = wx.StaticText(self, wx.ID_ANY, "Alignment:")
 
1949
        strAlignments = ['Top left', 'Top center', 'Top right', \
 
1950
                         'Left', 'Middle', 'Right', \
 
1951
                         'Bottom left', 'Bottom center', 'Bottom right']
 
1952
        self.chAlignment = wx.Choice(self, wx.ID_ANY, wx.DefaultPosition, \
 
1953
                wx.DefaultSize, strAlignments, name="Alignment")
 
1954
        self.chAlignment.SetToolTipString(\
 
1955
                "Select how the menu text should be aligned")
 
1956
        wx.EVT_CHOICE(self, self.chAlignment.GetId(), self.OnAlignment)
 
1957
        ## Place alignment items together
 
1958
        self.sizAlignment = wx.BoxSizer(wx.HORIZONTAL)
 
1959
        self.sizAlignment.AddMany([ \
 
1960
                (self.lblAlignment, 0, wx.ALL| wx.ALIGN_CENTER_VERTICAL, 4),
 
1961
                (self.chAlignment, 0, wx.ALL, 4) ])
 
1962
        # Menu text fill controls
 
1963
        self.lblTextFill = wx.StaticText(self, wx.ID_ANY, "Fill text with:")
 
1964
        strFillTypes = ['Color', 'Fractal', 'Gradient', 'Pattern']
 
1965
        self.chFillType = wx.Choice(self, wx.ID_ANY, wx.DefaultPosition, \
 
1966
            wx.DefaultSize, strFillTypes, name="Fill type")
 
1967
        self.chFillType.SetToolTipString("Click to choose text fill style")
 
1968
        wx.EVT_CHOICE(self, self.chFillType.GetId(), self.OnFillType)
 
1969
        ## color box 1
 
1970
        self.chkFillColor = wx.CheckBox(self, wx.ID_ANY, style=wx.ALIGN_RIGHT,
 
1971
            label="None?")
 
1972
        wx.EVT_CHECKBOX(self, self.chkFillColor.GetId(), self.OnColorNone)
 
1973
        self.btnFillColor = wx.Button(self, wx.ID_ANY, "Choose...")
 
1974
        self.btnFillColor.SetToolTipString("Choose a color")
 
1975
        self.btnFillColor.SetBackgroundColour(self.curOptions.color1)
 
1976
        self.sizFillColor1 = wx.BoxSizer(wx.HORIZONTAL)
 
1977
        self.sizFillColor1.Add(self.btnFillColor, 1)
 
1978
        self.sizFillColor1.Add(self.chkFillColor, 0, wx.ALIGN_CENTER_VERTICAL)
 
1979
        wx.EVT_BUTTON(self, self.btnFillColor.GetId(), self.OnFillColor)
 
1980
        ## color box 2
 
1981
        self.chkFillColor2 = wx.CheckBox(self, wx.ID_ANY, style=wx.ALIGN_RIGHT,
 
1982
            label="None?")
 
1983
        wx.EVT_CHECKBOX(self, self.chkFillColor2.GetId(), self.OnColorNone)
 
1984
        self.btnFillColor2 = wx.Button(self, wx.ID_ANY, "Choose...")
 
1985
        self.btnFillColor2.SetToolTipString("Choose a color")
 
1986
        self.btnFillColor2.SetBackgroundColour(self.curOptions.color2)
 
1987
        self.sizFillColor2 = wx.BoxSizer(wx.HORIZONTAL)
 
1988
        self.sizFillColor2.Add(self.btnFillColor2, 1)
 
1989
        self.sizFillColor2.Add(self.chkFillColor2, 0, 
 
1990
            wx.ALIGN_CENTER_VERTICAL)
 
1991
        wx.EVT_BUTTON(self, self.btnFillColor2.GetId(), self.OnFillColor2)
 
1992
        ## pattern drop box
 
1993
        self.dictPatternTypes = { 'bricks':           'bricks', 
 
1994
                                  'circles':          'circles', 
 
1995
                                  'small squares':    'crosshatch', 
 
1996
                                  'big squares':      'hs_cross', 
 
1997
                                  'crosshatching':    'hs_diagcross', 
 
1998
                                  'hexagons':         'hexagons', 
 
1999
                                  'fish scales':      'smallfishscales', 
 
2000
                                  'sawtooth lines':   'horizontalsaw', 
 
2001
                                  '45 deg lines':     'hs_bdiagonal', 
 
2002
                                  '-45 deg lines':    'hs_fdiagonal', 
 
2003
                                  'vertical lines':   'hs_vertical', 
 
2004
                                  'horizontal lines': 'hs_horizontal' }
 
2005
        liPatternTypes = self.dictPatternTypes.keys()
 
2006
        liPatternTypes.sort()
 
2007
        self.cbPattern = wx.ComboBox(self, wx.ID_ANY, style=wx.CB_DROPDOWN, 
 
2008
            value="bricks", choices=liPatternTypes, name="Pattern types")
 
2009
        self.cbPattern.SetToolTipString("Choose a pattern to fill the text " \
 
2010
            "with, or enter an ImageMagick pattern name")
 
2011
        wx.EVT_TEXT(self, self.cbPattern.GetId(), self.OnPattern)
 
2012
        ## Place text fill items together
 
2013
        self.sizTextFill = wx.BoxSizer(wx.HORIZONTAL)
 
2014
        self.sizTextFill.AddMany([
 
2015
            (self.lblTextFill, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 4),
 
2016
            (self.chFillType, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 4),
 
2017
            (self.cbPattern, 1, wx.EXPAND | wx.ALL, 4),
 
2018
            (self.sizFillColor1, 1, wx.ALL, 4),
 
2019
            (self.sizFillColor2, 1, wx.ALL, 4) ])
 
2020
        ## Hide unselected fill controls
 
2021
        self.sizTextFill.Hide(self.sizFillColor2)
 
2022
        self.sizTextFill.Hide(self.cbPattern)
 
2023
 
 
2024
        # Menu text outline controls
 
2025
        self.lblStrokeColor = wx.StaticText(self, wx.ID_ANY, "Text outline:")
 
2026
        self.chkStrokeColor = wx.CheckBox(self, wx.ID_ANY, style=wx.ALIGN_RIGHT,
 
2027
            label="None?")
 
2028
        wx.EVT_CHECKBOX(self, self.chkStrokeColor.GetId(), self.OnColorNone)
 
2029
        self.btnStrokeColor = wx.Button(self, wx.ID_ANY, "Choose...")
 
2030
        self.btnStrokeColor.SetToolTipString("Choose the outline color")
 
2031
        self.btnStrokeColor.SetBackgroundColour(self.curOptions.colorStroke)
 
2032
        wx.EVT_BUTTON(self, self.btnStrokeColor.GetId(), self.OnStrokeColor)
 
2033
        self.lblStrokeWidth = wx.StaticText(self, wx.ID_ANY, "Width (px):")
 
2034
        self.txtStrokeWidth = wx.TextCtrl(self, wx.ID_ANY)
 
2035
        self.txtStrokeWidth.SetToolTipString("Width of ouline in pixels")
 
2036
        wx.EVT_TEXT(self, self.txtStrokeWidth.GetId(), self.OnStrokeWidth)
 
2037
        ## Group the 'None?' checkbox and StrokeColor button
 
2038
        self.sizStrokeColor = wx.BoxSizer(wx.VERTICAL)
 
2039
        self.sizStrokeColor.Add(self.btnStrokeColor, 1, wx.EXPAND)
 
2040
        self.sizStrokeColor.Add(self.chkStrokeColor, 0, 
 
2041
            wx.ALIGN_CENTER_HORIZONTAL)
 
2042
        ## Group the text outline controls together
 
2043
        self.sizTextOutline = wx.BoxSizer(wx.HORIZONTAL)
 
2044
        self.sizTextOutline.AddMany([
 
2045
            (self.lblStrokeColor, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 4),
 
2046
            (self.sizStrokeColor, 1, wx.ALL, 4),
 
2047
            (self.lblStrokeWidth, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 4),
 
2048
            (self.txtStrokeWidth, 1, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 4) ])
 
2049
 
 
2050
        # Put all menu text controls in a static box
 
2051
        self.sboxTextFormat = wx.StaticBox(self, wx.ID_ANY, "Menu text format")
 
2052
        self.sizTextFormat = wx.StaticBoxSizer(self.sboxTextFormat, wx.VERTICAL)
 
2053
        self.sizTextFormat.AddMany([\
 
2054
          (self.sizFontFace, 0, wx.EXPAND),
 
2055
          (self.sizAlignment, 0, wx.EXPAND),
 
2056
          (self.sizTextFill, 0, wx.EXPAND),
 
2057
          (self.sizTextOutline, 0, wx.EXPAND) ])
 
2058
 
 
2059
        # DVD buttons ======================================================\
 
2060
        # Shape
 
2061
        strButtons = ['>', '~', 'play', 'movie']
 
2062
        self.lblButton = wx.StaticText(self, wx.ID_ANY, "Button shape:")
 
2063
        self.lblButtonPreview = wx.StaticText(self, wx.ID_ANY, "Preview:")
 
2064
        self.lblButtonExample = wx.StaticText(self, wx.ID_ANY)
 
2065
        self.lblButtonExample.SetLabel(self.curOptions.button)
 
2066
        self.lblButtonExample.SetFont(self.curOptions.buttonFont)
 
2067
        self.cbButton = wx.ComboBox(self, wx.ID_ANY, value='>', \
 
2068
            style=wx.CB_DROPDOWN, choices=strButtons)
 
2069
        self.cbButton.SetToolTipString(\
 
2070
            "Choose the shape of the DVD buttons."\
 
2071
            "You may also type your own button (can only be 1 character).")
 
2072
        wx.EVT_TEXT(self, self.cbButton.GetId(), self.OnButton)
 
2073
        ## group shape controls together
 
2074
        self.sizDVDShapes = wx.BoxSizer(wx.HORIZONTAL)
 
2075
        self.sizDVDShapes.AddMany([ 
 
2076
            (self.lblButton, 0, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | \
 
2077
                wx.ALL, 4), 
 
2078
            (self.cbButton, 1, wx.EXPAND | wx.ALL, 4),
 
2079
            (self.lblButtonPreview, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 4),
 
2080
            (self.lblButtonExample, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 4) ])
 
2081
        # Font
 
2082
        self.lblButtonFont = wx.StaticText(self, wx.ID_ANY, "Font:")
 
2083
        self.chkLockButtonFont = wx.CheckBox(self, wx.ID_ANY, 
 
2084
            style=wx.ALIGN_RIGHT, label="Lock?")
 
2085
        wx.EVT_CHECKBOX(self, self.chkLockButtonFont.GetId(), 
 
2086
            self.OnLockButtonFont)
 
2087
        self.btnButtonFontChooserDialog = wx.Button(self, wx.ID_ANY, "Default")
 
2088
        self.btnButtonFontChooserDialog.SetToolTipString(\
 
2089
                "Select a font to use for the buttons")
 
2090
        wx.EVT_BUTTON(self, self.btnButtonFontChooserDialog.GetId(), \
 
2091
                self.OnButtonFontSelection)
 
2092
        ## group font controls together
 
2093
        self.sizButtonFont = wx.BoxSizer(wx.HORIZONTAL)
 
2094
        self.sizButtonFont.AddMany([
 
2095
            (self.lblButtonFont, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 4),
 
2096
            (self.btnButtonFontChooserDialog, 1, wx.EXPAND | wx.ALL, 4),
 
2097
            (self.chkLockButtonFont, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 4) ])
 
2098
        # Colors
 
2099
        self.lblHiColor = wx.StaticText(self, wx.ID_ANY, _("Highlight:"))
 
2100
        self.lblSelColor = wx.StaticText(self, wx.ID_ANY, _("Selection:"))
 
2101
        self.chkHiColor = wx.CheckBox(self, wx.ID_ANY, style=wx.ALIGN_RIGHT,
 
2102
            label="None?")
 
2103
        self.chkSelColor = wx.CheckBox(self, wx.ID_ANY, style=wx.ALIGN_RIGHT,
 
2104
            label="None?")
 
2105
        wx.EVT_CHECKBOX(self, self.chkHiColor.GetId(), self.OnColorNone)
 
2106
        wx.EVT_CHECKBOX(self, self.chkSelColor.GetId(), self.OnColorNone)
 
2107
        self.btnHiColor = wx.Button(self, wx.ID_ANY, _("Choose..."))
 
2108
        self.btnSelColor = wx.Button(self, wx.ID_ANY, _("Choose..."))
 
2109
        self.btnHiColor.SetToolTipString(_("Choose the color used "
 
2110
            "to highlight menu items as they are navigated"))
 
2111
        self.btnSelColor.SetToolTipString(_("Choose the color used "
 
2112
            "when a menu item is chosen or activated for playback"))
 
2113
        self.btnHiColor.SetBackgroundColour(self.curOptions.colorHi)
 
2114
        self.btnSelColor.SetBackgroundColour(self.curOptions.colorSel)
 
2115
        wx.EVT_BUTTON(self, self.btnHiColor.GetId(), self.OnHiColor)
 
2116
        wx.EVT_BUTTON(self, self.btnSelColor.GetId(), self.OnSelColor)
 
2117
        ## group checkboxes and color
 
2118
        self.sizDVDHighlightColor = wx.BoxSizer(wx.VERTICAL)
 
2119
        self.sizDVDHighlightColor.AddMany([
 
2120
            (self.btnHiColor, 0, wx.EXPAND),
 
2121
            (self.chkHiColor, 0, wx.ALIGN_CENTER_HORIZONTAL)  ])
 
2122
        self.sizDVDSelectColor = wx.BoxSizer(wx.VERTICAL)
 
2123
        self.sizDVDSelectColor.AddMany([
 
2124
            (self.btnSelColor, 0, wx.EXPAND),
 
2125
            (self.chkSelColor, 0, wx.ALIGN_CENTER_HORIZONTAL) ])
 
2126
        ## group highlight and selection color conttrols together
 
2127
        self.sizDVDButtonColors = wx.BoxSizer(wx.HORIZONTAL)
 
2128
        self.sizDVDButtonColors.AddMany([
 
2129
            (self.lblHiColor, 0, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | \
 
2130
                wx.ALL, 4), 
 
2131
            (self.sizDVDHighlightColor, 0, wx.EXPAND | wx.ALL, 4), 
 
2132
            (self.lblSelColor, 0, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | \
 
2133
                wx.ALL, 4), 
 
2134
            (self.sizDVDSelectColor, 1, wx.EXPAND | wx.ALL, 4) ])
 
2135
        # Outline
 
2136
        self.lblButtonOutline = wx.StaticText(self, wx.ID_ANY, "Outline:")
 
2137
        self.chkButtonOutline = wx.CheckBox(self, wx.ID_ANY, 
 
2138
            style=wx.ALIGN_RIGHT, label="None?")
 
2139
        self.chkButtonOutline.SetToolTipString(\
 
2140
            "Should the buttons be outlined?")
 
2141
        wx.EVT_CHECKBOX(self, self.chkButtonOutline.GetId(), 
 
2142
            self.OnColorNone)
 
2143
        self.btnButtonOutlineColor = wx.Button(self, wx.ID_ANY, "Choose...")
 
2144
        self.btnButtonOutlineColor.SetToolTipString(\
 
2145
            "Choose a color for the button outline.")
 
2146
        wx.EVT_BUTTON(self, self.btnButtonOutlineColor.GetId(), 
 
2147
            self.OnButtonOutlineColor)
 
2148
        ## group outline together
 
2149
        self.sizDVDOutline = wx.BoxSizer(wx.HORIZONTAL)
 
2150
        self.sizDVDOutline.AddMany([ 
 
2151
            (self.lblButtonOutline, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 4),
 
2152
            (self.btnButtonOutlineColor, 1, wx.EXPAND | wx.ALL, 4),
 
2153
            (self.chkButtonOutline, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 4) ])
 
2154
        # Put DVD button controls in a static box
 
2155
        self.sboxDVDButtonStyle = wx.StaticBox(self, wx.ID_ANY, 
 
2156
            "DVD button style")
 
2157
        self.sizDVDButtonStyle = wx.StaticBoxSizer(self.sboxDVDButtonStyle, 
 
2158
            wx.VERTICAL)
 
2159
        self.sizDVDButtonStyle.AddMany([
 
2160
            (self.sizDVDShapes, 0),
 
2161
            (self.sizButtonFont, 0, wx.EXPAND),
 
2162
            (self.sizDVDButtonColors, 0, wx.EXPAND),
 
2163
            (self.sizDVDOutline, 0) ])
 
2164
 
 
2165
        # Vertical sizer putting sizTextFormat above sizDVDButtonStyle ====\
 
2166
        self.sizTextAndButtons = wx.BoxSizer(wx.VERTICAL)
 
2167
        self.sizTextAndButtons.Add(self.sizTextFormat, 0, wx.EXPAND, 0)
 
2168
        self.sizTextAndButtons.Add(self.sizDVDButtonStyle, 1, wx.EXPAND, 0)
 
2169
 
 
2170
        # List of titles in this menu ======================================\
 
2171
        self.lblTitleList = wx.StaticText(self, wx.ID_ANY, 
 
2172
            _("Titles shown in this menu"))
 
2173
        self.lbTitles = wx.ListBox(self, wx.ID_ANY)
 
2174
        self.lbTitles.SetToolTipString(_("This lists all the sub-menus "
 
2175
            "and videos that are under this menu. To change the text that "
 
2176
            "appears here, edit the titles in the disc layout tree."))
 
2177
        self.lbTitles.Enable(False)
 
2178
        # Sizer to hold title list
 
2179
        self.sizTitles = wx.BoxSizer(wx.VERTICAL)
 
2180
        self.sizTitles.AddMany([ (self.lblTitleList, 0),
 
2181
                                  (self.lbTitles, 1, wx.EXPAND) ])
 
2182
 
 
2183
        # Horizontal sizer holding sizTextAndButtons and sizTitles =========\
 
2184
        self.sizTextTitles = wx.BoxSizer(wx.HORIZONTAL)
 
2185
        self.sizTextTitles.Add(self.sizTextAndButtons, 2, 0)
 
2186
        self.sizTextTitles.Add(self.sizTitles, 1, wx.EXPAND | wx.LEFT, 10)
 
2187
 
 
2188
        # Menu options heading
 
2189
        self.txtHeading = HeadingText(self, wx.ID_ANY, "Menu options")
 
2190
 
 
2191
        # Button to copy menu options to all menus on disc =================\
 
2192
        self.btnUseForAll = wx.Button(self, wx.ID_ANY,
 
2193
            "Use these settings for all menus")
 
2194
        self.btnUseForAll.SetToolTipString("Apply the current menu settings,"
 
2195
            " including background image, audio, text alignment, text color" \
 
2196
            " and font, to all menus on the disc.")
 
2197
        wx.EVT_BUTTON(self, self.btnUseForAll.GetId(), self.OnUseForAll)
 
2198
 
 
2199
        # Main sizer to hold all controls ==================================\
 
2200
        self.sizMain = wx.BoxSizer(wx.VERTICAL)
 
2201
        self.sizMain.Add(self.txtHeading, 0, wx.EXPAND | wx.ALL, 8)
 
2202
        self.sizMain.Add(self.sizBG, 0, wx.EXPAND | wx.ALL, 8)
 
2203
        self.sizMain.Add(self.sizTextTitles, 1, wx.EXPAND | wx.ALL, 8)
 
2204
        self.sizMain.Add(self.btnUseForAll, 0, wx.EXPAND | wx.ALL, 8)
 
2205
        self.SetSizer(self.sizMain)
 
2206
 
 
2207
    def OnMenuTitle(self, evt):
 
2208
        """Set the menu's main title in curOptions whenever text is altered."""
 
2209
        self.curOptions.menutitle = self.txtMenuTitle.GetValue()
 
2210
 
 
2211
    def OnMenuTitleFontSize(self, evt):
 
2212
        """Set the menu title font size whenever text is altered"""
 
2213
        self.curOptions.titlefontsize = self.txtMenuTitleFontSize.GetValue()
 
2214
 
 
2215
    def OnBGImage(self, evt):
 
2216
        """Set the background image in curOptions whenever text is altered."""
 
2217
        self.curOptions.background = self.txtBGImage.GetValue()
 
2218
 
 
2219
    def OnBGAudio(self, evt):
 
2220
        """Set the background audio in curOptions whenever text is altered."""
 
2221
        self.curOptions.audio = self.txtBGAudio.GetValue()
 
2222
 
 
2223
    def OnMenuLength(self, evt):
 
2224
        """Set the menu length in curOptions whenever text is altered."""
 
2225
        self.curOptions.menulength = self.txtMenuLength.GetValue()
 
2226
 
 
2227
    def OnBrowseBGImage(self, evt):
 
2228
        """Show a file dialog and set the background image."""
 
2229
        inFileDlg = wx.FileDialog(self, "Select an image file", "", "", \
 
2230
                "*.*", wx.OPEN)
 
2231
        if inFileDlg.ShowModal() == wx.ID_OK:
 
2232
            self.txtBGImage.SetValue(inFileDlg.GetPath())
 
2233
            inFileDlg.Destroy()
 
2234
 
 
2235
    def OnBrowseBGAudio(self, evt):
 
2236
        """Show a file dialog and set the background audio."""
 
2237
        inFileDlg = wx.FileDialog(self, "Select an audio file", "", "", \
 
2238
                "*.*", wx.OPEN)
 
2239
        if inFileDlg.ShowModal() == wx.ID_OK:
 
2240
            self.txtBGAudio.SetValue(inFileDlg.GetPath())
 
2241
            inFileDlg.Destroy()
 
2242
 
 
2243
    def OnAlignment(self, evt):
 
2244
        """Set the text alignment according to the choice box selection."""
 
2245
        self.curOptions.alignment = util.ID_to_text(\
 
2246
            'alignment', evt.GetSelection())
 
2247
 
 
2248
    def OnFillType(self, evt):
 
2249
        """Set the fill type and show/hide the appropriate controls."""
 
2250
        self.fillType = util.ID_to_text('fillType', evt.GetSelection())
 
2251
        self.curOptions.fillType = self.fillType
 
2252
        # Only show the first color box for 'Color'
 
2253
        if self.fillType == "Color":
 
2254
            self.sizTextFill.Show(self.sizFillColor1)
 
2255
            self.sizTextFill.Hide(self.sizFillColor2)
 
2256
            self.sizTextFill.Hide(self.cbPattern)
 
2257
            self.btnFillColor.SetLabel("Choose...")
 
2258
            self.sizFillColor1.Show(self.chkFillColor)
 
2259
        # Show both color boxes for 'Fractal' or 'Gradient'
 
2260
        elif self.fillType == "Fractal" or self.fillType == "Gradient":
 
2261
            self.chkFillColor.SetValue(False)   # enable color 1
 
2262
            self.btnFillColor.Enable()
 
2263
            self.sizTextFill.Show(self.sizFillColor1)
 
2264
            self.sizTextFill.Show(self.sizFillColor2)
 
2265
            self.sizTextFill.Hide(self.cbPattern)
 
2266
            self.btnFillColor.SetLabel("From...")
 
2267
            self.btnFillColor2.SetLabel("To...")
 
2268
            self.sizFillColor1.Hide(self.chkFillColor)
 
2269
            self.sizFillColor2.Hide(self.chkFillColor2)
 
2270
            self.chkStrokeColor.Enable(True)
 
2271
            self.curOptions.fillColor1 = "rgb(%d,%d,%d)" % \
 
2272
                ( self.curOptions.color1.Red(), 
 
2273
                  self.curOptions.color1.Green(), 
 
2274
                  self.curOptions.color1.Blue()   )
 
2275
        # Only show the pattern drop box for 'pattern'
 
2276
        elif self.fillType == "Pattern":
 
2277
            self.sizTextFill.Hide(self.sizFillColor1)
 
2278
            self.sizTextFill.Hide(self.sizFillColor2)
 
2279
            self.sizTextFill.Show(self.cbPattern)
 
2280
            self.chkStrokeColor.Enable(True)
 
2281
        else:
 
2282
            print "DEBUG: invalid FillType: %d" % evt.GetSelection()
 
2283
            print "DEBUG: selection was: %s" % selection
 
2284
 
 
2285
        self.sizTextFill.Layout()
 
2286
        self.sizTextFormat.Layout()
 
2287
        self.sizTextAndButtons.Layout()
 
2288
        self.sizTextTitles.Layout()
 
2289
 
 
2290
    def OnColorNone(self, evt):
 
2291
        """When checked, don't use a color box's color, but 'none' instead."""
 
2292
        if evt.GetId() == self.chkFillColor.GetId():
 
2293
            if evt.IsChecked():
 
2294
                self.btnFillColor.Enable(False)
 
2295
                self.curOptions.fillColor1 = "none"
 
2296
                # Can't have both no fill and no stroke, so set stroke
 
2297
                self.chkStrokeColor.Enable(False)
 
2298
                self.chkStrokeColor.SetValue(False)
 
2299
                self.curOptions.textStrokeColor = "rgb(%d,%d,%d)" % \
 
2300
                    ( self.curOptions.colorStroke.Red(),
 
2301
                      self.curOptions.colorStroke.Green(),
 
2302
                      self.curOptions.colorStroke.Blue()   )
 
2303
            else:
 
2304
                self.btnFillColor.Enable(True)
 
2305
                self.curOptions.fillColor1 = "rgb(%d,%d,%d)" % \
 
2306
                    ( self.curOptions.color1.Red(), 
 
2307
                      self.curOptions.color1.Green(), 
 
2308
                      self.curOptions.color1.Blue()   )
 
2309
                # Re-enable disabled stroke color controls
 
2310
                self.chkStrokeColor.Enable(True)
 
2311
 
 
2312
        elif evt.GetId() == self.chkFillColor2.GetId():
 
2313
            if evt.IsChecked():
 
2314
                self.btnFillColor2.Enable(False)
 
2315
                self.curOptions.fillColor2 = "none"
 
2316
            else:
 
2317
                self.btnFillColor2.Enable(True)
 
2318
                self.curOptions.fillColor2 = "rgb(%d,%d,%d)" % \
 
2319
                    ( self.curOptions.color2.Red(), 
 
2320
                      self.curOptions.color2.Green(), 
 
2321
                      self.curOptions.color2.Blue()   )
 
2322
 
 
2323
        elif evt.GetId() == self.chkStrokeColor.GetId():
 
2324
            if evt.IsChecked():
 
2325
                self.btnStrokeColor.Enable(False)
 
2326
                self.curOptions.textStrokeColor = "none"
 
2327
                # Can't have both no color and no fill, so set fill
 
2328
                self.chkFillColor.Enable(False)
 
2329
                self.chkFillColor.SetValue(False)
 
2330
                self.curOptions.fillColor1 = "rgb(%d,%d,%d)" % \
 
2331
                    ( self.curOptions.color1.Red(), 
 
2332
                      self.curOptions.color1.Green(), 
 
2333
                      self.curOptions.color1.Blue()   )
 
2334
            else:
 
2335
                self.btnStrokeColor.Enable(True)
 
2336
                self.curOptions.textStrokeColor = "rgb(%d,%d,%d)" % \
 
2337
                    ( self.curOptions.colorStroke.Red(),
 
2338
                      self.curOptions.colorStroke.Green(),
 
2339
                      self.curOptions.colorStroke.Blue()   )
 
2340
                # Re-enable fill controls
 
2341
                self.chkFillColor.Enable(True)
 
2342
 
 
2343
        elif evt.GetId() == self.chkButtonOutline.GetId():
 
2344
            if evt.IsChecked():
 
2345
                self.btnButtonOutlineColor.Enable(False)
 
2346
                self.curOptions.buttonOutlineColor = "none"
 
2347
                # Can't have both no fill and no outline, so set fill
 
2348
                self.chkHiColor.Enable(False)
 
2349
                self.chkSelColor.Enable(False)
 
2350
                self.curOptions.highlightColor = "rgb(%d,%d,%d)" % \
 
2351
                    ( self.curOptions.colorHi.Red(),
 
2352
                      self.curOptions.colorHi.Green(),
 
2353
                      self.curOptions.colorHi.Blue()   )
 
2354
                self.curOptions.selectColor = "rgb(%d,%d,%d)" % \
 
2355
                    ( self.curOptions.colorSel.Red(),
 
2356
                      self.curOptions.colorSel.Green(),
 
2357
                      self.curOptions.colorSel.Blue()   )
 
2358
            else:
 
2359
                self.btnButtonOutlineColor.Enable(True)
 
2360
                self.curOptions.buttonOutlineColor = "rgb(%d,%d,%d)" % \
 
2361
                    ( self.curOptions.colorButtonOutline.Red(),
 
2362
                      self.curOptions.colorButtonOutline.Green(),
 
2363
                      self.curOptions.colorButtonOutline.Blue()   )
 
2364
                # Re-enable button fill controls
 
2365
                self.chkHiColor.Enable(True)
 
2366
                self.chkSelColor.Enable(True)
 
2367
 
 
2368
        elif evt.GetId() == self.chkHiColor.GetId():
 
2369
            if evt.IsChecked():
 
2370
                self.btnHiColor.Enable(False)
 
2371
                self.curOptions.highlightColor = "none"
 
2372
                # Can't have both no fill and no stroke, so set outline
 
2373
                self.chkButtonOutline.Enable(False)
 
2374
                self.curOptions.buttonOutlineColor = "rgb(%d,%d,%d)" % \
 
2375
                    ( self.curOptions.colorButtonOutline.Red(),
 
2376
                      self.curOptions.colorButtonOutline.Green(),
 
2377
                      self.curOptions.colorButtonOutline.Blue()   )
 
2378
            else:
 
2379
                self.btnHiColor.Enable(True)
 
2380
                self.curOptions.highlightColor = "rgb(%d,%d,%d)" % \
 
2381
                    ( self.curOptions.colorHi.Red(),
 
2382
                      self.curOptions.colorHi.Green(),
 
2383
                      self.curOptions.colorHi.Blue()   )
 
2384
                # Re-enable button outline controls
 
2385
                if self.chkSelColor.IsChecked():
 
2386
                    pass
 
2387
                else:
 
2388
                    self.chkButtonOutline.Enable(True)
 
2389
        
 
2390
        elif evt.GetId() == self.chkSelColor.GetId():
 
2391
            if evt.IsChecked():
 
2392
                self.btnSelColor.Enable(False)
 
2393
                self.curOptions.selectColor = "none"
 
2394
                # Can't have both no fill and no stroke, so set outline
 
2395
                self.chkButtonOutline.Enable(False)
 
2396
                self.curOptions.buttonOutlineColor = "rgb(%d,%d,%d)" % \
 
2397
                    ( self.curOptions.colorButtonOutline.Red(),
 
2398
                      self.curOptions.colorButtonOutline.Green(),
 
2399
                      self.curOptions.colorButtonOutline.Blue()   )
 
2400
            else:
 
2401
                self.btnSelColor.Enable(True)
 
2402
                self.curOptions.selectColor = "rgb(%d,%d,%d)" % \
 
2403
                    ( self.curOptions.colorSel.Red(),
 
2404
                      self.curOptions.colorSel.Green(),
 
2405
                      self.curOptions.colorSel.Blue()   )
 
2406
                # Re-enable button outline controls
 
2407
                if self.chkHiColor.IsChecked():
 
2408
                    pass
 
2409
                else:
 
2410
                    self.chkButtonOutline.Enable(True)
 
2411
        
 
2412
        else:
 
2413
            print "DEBUG: ", evt.IsChecked(), self.GetId()
 
2414
            print "DEBUG: ", self.chkFillColor.GetId(), self.chkFillColor2.GetId()
 
2415
 
 
2416
    def OnPattern(self, evt):
 
2417
        """Set the patten that will fill the menu text."""
 
2418
        pattern = self.cbPattern.GetValue()
 
2419
        if pattern in self.dictPatternTypes.keys():
 
2420
            self.curOptions.pattern = self.dictPatternTypes[pattern]
 
2421
        else:
 
2422
            self.curOptions.pattern = pattern
 
2423
 
 
2424
    def OnFontSelection(self, evt):
 
2425
        """Show a font selection dialog and set the font."""
 
2426
        dlgFontChooserDialog = FontChooserDialog(self, wx.ID_ANY,
 
2427
            self.curOptions.font.GetFaceName())
 
2428
        if dlgFontChooserDialog.ShowModal() == wx.ID_OK:
 
2429
            strFontName = dlgFontChooserDialog.GetSelectedFont().GetFaceName()
 
2430
            self.curOptions.font = wx.Font(10, wx.FONTFAMILY_DEFAULT,
 
2431
                wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, strFontName)
 
2432
            # Button shows selected font name in selected font
 
2433
            self.btnFontChooserDialog.SetFont(self.curOptions.font)
 
2434
            self.btnFontChooserDialog.SetLabel(strFontName)
 
2435
            self.UpdateButtonFontChooserButton()
 
2436
 
 
2437
    def OnFontSize(self, evt):
 
2438
        """Set the font size whenever text is altered"""
 
2439
        self.curOptions.fontsize = self.txtFontSize.GetValue()
 
2440
        self.UpdateButtonExample()
 
2441
 
 
2442
    def OnFillColor(self, evt):
 
2443
        """Display a color dialog to select the text color."""
 
2444
        self.curColorData.SetColour(self.curOptions.color1)
 
2445
        dlgColor = wx.ColourDialog(self, self.curColorData)
 
2446
        if dlgColor.ShowModal() == wx.ID_OK:
 
2447
            self.curColorData = dlgColor.GetColourData()
 
2448
            self.curOptions.color1 = self.curColorData.GetColour()
 
2449
            self.btnFillColor.SetBackgroundColour(self.curOptions.color1)
 
2450
            self.curOptions.fillColor1 = "rgb(%d,%d,%d)" % \
 
2451
                ( self.curOptions.color1.Red(), 
 
2452
                  self.curOptions.color1.Green(), 
 
2453
                  self.curOptions.color1.Blue()   )
 
2454
 
 
2455
    def OnFillColor2(self, evt):
 
2456
        """Display a color dialog to select the second fill color."""
 
2457
        self.curColorData.SetColour(self.curOptions.color2)
 
2458
        dlgColor = wx.ColourDialog(self, self.curColorData)
 
2459
        if dlgColor.ShowModal() == wx.ID_OK:
 
2460
            self.curColorData = dlgColor.GetColourData()
 
2461
            self.curOptions.color2 = self.curColorData.GetColour()
 
2462
            self.btnFillColor2.SetBackgroundColour(self.curOptions.color2)
 
2463
            self.curOptions.fillColor2 = "rgb(%d,%d,%d)" % \
 
2464
                ( self.curOptions.color2.Red(), 
 
2465
                  self.curOptions.color2.Green(), 
 
2466
                  self.curOptions.color2.Blue()   )
 
2467
 
 
2468
    def OnStrokeColor(self, evt):
 
2469
        """Display a color dialog to select the text stroke color."""
 
2470
        self.curColorData.SetColour(self.curOptions.colorStroke)
 
2471
        dlgColor = wx.ColourDialog(self, self.curColorData)
 
2472
        if dlgColor.ShowModal() == wx.ID_OK:
 
2473
            self.curColorData = dlgColor.GetColourData()
 
2474
            self.curOptions.colorStroke = self.curColorData.GetColour()
 
2475
            self.btnStrokeColor.SetBackgroundColour(
 
2476
                self.curOptions.colorStroke)
 
2477
            self.curOptions.textStrokeColor = "rgb(%d,%d,%d)" % \
 
2478
                ( self.curOptions.colorStroke.Red(),
 
2479
                  self.curOptions.colorStroke.Green(),
 
2480
                  self.curOptions.colorStroke.Blue() )
 
2481
 
 
2482
    def OnStrokeWidth(self, evt):
 
2483
        """Update the outline width whenever text is updated."""
 
2484
        self.curOptions.textStrokeWidth = self.txtStrokeWidth.GetValue()
 
2485
 
 
2486
    def OnButtonFontSelection(self, evt):
 
2487
        """Show a font selection dialog and set the font."""
 
2488
        dlgButtonFontChooserDialog = FontChooserDialog(self, wx.ID_ANY,
 
2489
            self.curOptions.font.GetFaceName())
 
2490
        if dlgButtonFontChooserDialog.ShowModal() == wx.ID_OK:
 
2491
            strFontName = \
 
2492
                dlgButtonFontChooserDialog.GetSelectedFont().GetFaceName()
 
2493
            self.curOptions.buttonFont = wx.Font(10, wx.FONTFAMILY_DEFAULT,
 
2494
                wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, strFontName)
 
2495
            # Button shows selected font name in selected font
 
2496
            self.btnButtonFontChooserDialog.SetFont(self.curOptions.buttonFont)
 
2497
            self.btnButtonFontChooserDialog.SetLabel(\
 
2498
                self.curOptions.buttonFont.GetFaceName())
 
2499
            self.UpdateButtonExample()
 
2500
 
 
2501
    def OnLockButtonFont(self, evt):
 
2502
        """Lock/unlock the -button-font chooser"""
 
2503
        if evt.IsChecked():
 
2504
            self.btnButtonFontChooserDialog.Enable(False)
 
2505
        else:
 
2506
            self.btnButtonFontChooserDialog.Enable(True)
 
2507
 
 
2508
    def UpdateButtonExample(self):
 
2509
        """Redraw the button example acc to existing options' constraints"""
 
2510
        # 'play' or 'movie' buttons change font and button
 
2511
        newButton = self.curOptions.button
 
2512
        if newButton != "play" and newButton != "movie":
 
2513
            newFontName = self.curOptions.buttonFont.GetFaceName()
 
2514
        else:
 
2515
            newFontName = "Webdings"
 
2516
            if newButton == "play":
 
2517
                newButton = '4'
 
2518
            else:
 
2519
                newButton = u'\u220f'
 
2520
 
 
2521
        try:
 
2522
            newFont = wx.Font(pointSize=int(self.curOptions.fontsize), 
 
2523
                          family=wx.FONTFAMILY_DEFAULT,
 
2524
                          style=wx.FONTSTYLE_NORMAL,
 
2525
                          weight=wx.FONTWEIGHT_BOLD,
 
2526
                          face=newFontName)
 
2527
        except ValueError:
 
2528
            pass
 
2529
        else:
 
2530
            self.lblButtonExample.SetLabel(newButton)
 
2531
            self.lblButtonExample.SetFont(newFont)
 
2532
            self.sizButtonFont.Layout()
 
2533
            self.sizDVDButtonStyle.Layout()
 
2534
            self.sizTextAndButtons.Layout()
 
2535
            self.sizTextTitles.Layout()
 
2536
 
 
2537
    def UpdateButtonFontChooserButton(self):
 
2538
        """Change font/label of -button-font chooser acc to curOptions"""
 
2539
        # For normal buttons, follow the text font (unless locked)
 
2540
        if self.curOptions.button != "play" and self.curOptions.button != "movie":
 
2541
            if self.chkLockButtonFont.IsChecked():
 
2542
                return
 
2543
            else:
 
2544
                self.curOptions.buttonFont = self.curOptions.font
 
2545
                newFont = self.curOptions.buttonFont
 
2546
                newFontName = self.curOptions.buttonFont.GetFaceName()
 
2547
        # Otherwise, set the font name in a readable font
 
2548
        else:
 
2549
            newFont = wx.Font(12, wx.FONTFAMILY_DEFAULT,
 
2550
                wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL,
 
2551
                False, "Default")
 
2552
            newFontName = "Webdings"
 
2553
        self.btnButtonFontChooserDialog.SetLabel(newFontName)
 
2554
        self.btnButtonFontChooserDialog.SetFont(newFont)
 
2555
        self.UpdateButtonExample()
 
2556
 
 
2557
    def OnHiColor(self, evt):
 
2558
        """Display a color dialog to select the text highlight color."""
 
2559
        self.curColorData.SetColour(self.curOptions.colorHi)
 
2560
        dlgColor = wx.ColourDialog(self, self.curColorData)
 
2561
        if dlgColor.ShowModal() == wx.ID_OK:
 
2562
            self.curColorData = dlgColor.GetColourData()
 
2563
            self.curOptions.colorHi = self.curColorData.GetColour()
 
2564
            self.btnHiColor.SetBackgroundColour(self.curOptions.colorHi)
 
2565
            self.curOptions.highlightColor = "rgb(%d,%d,%d)" % \
 
2566
                ( self.curOptions.colorHi.Red(),
 
2567
                  self.curOptions.colorHi.Green(),
 
2568
                  self.curOptions.colorHi.Blue()   )
 
2569
 
 
2570
    def OnSelColor(self, evt):
 
2571
        """Display a color dialog to select the text selection color."""
 
2572
        self.curColorData.SetColour(self.curOptions.colorSel)
 
2573
        dlgColor = wx.ColourDialog(self, self.curColorData)
 
2574
        if dlgColor.ShowModal() == wx.ID_OK:
 
2575
            self.curColorData = dlgColor.GetColourData()
 
2576
            self.curOptions.colorSel = self.curColorData.GetColour()
 
2577
            self.btnSelColor.SetBackgroundColour(self.curOptions.colorSel)
 
2578
            self.curOptions.selectColor = "rgb(%d,%d,%d)" % \
 
2579
                ( self.curOptions.colorSel.Red(),
 
2580
                  self.curOptions.colorSel.Green(),
 
2581
                  self.curOptions.colorSel.Blue()   )
 
2582
 
 
2583
    def OnButton(self, evt):
 
2584
        """Set the button character whenever text is altered."""
 
2585
        self.curOptions.button = self.cbButton.GetValue()
 
2586
        # En/Disable font selection for buttons
 
2587
        if self.curOptions.button == ">" or self.curOptions.button == "~":
 
2588
            self.btnButtonFontChooserDialog.Enable(True)
 
2589
            self.chkLockButtonFont.SetValue(False)
 
2590
            self.chkLockButtonFont.Enable(True)
 
2591
        elif self.curOptions.button == "play" or self.curOptions.button == "movie":
 
2592
            self.btnButtonFontChooserDialog.Enable(False)
 
2593
            self.chkLockButtonFont.SetValue(True)
 
2594
            self.chkLockButtonFont.Enable(False)
 
2595
        self.UpdateButtonExample()
 
2596
        self.UpdateButtonFontChooserButton()
 
2597
 
 
2598
    def OnButtonOutlineColor(self, evt):
 
2599
        """Display a color dialog to select the button outline color."""
 
2600
        self.curColorData.SetColour(self.curOptions.colorButtonOutline)
 
2601
        dlgColor = wx.ColourDialog(self, self.curColorData)
 
2602
        if dlgColor.ShowModal() == wx.ID_OK:
 
2603
            self.curColorData = dlgColor.GetColourData()
 
2604
            self.curOptions.colorButtonOutline = self.curColorData.GetColour()
 
2605
            self.btnButtonOutlineColor.SetBackgroundColour(
 
2606
                self.curOptions.colorButtonOutline)
 
2607
            self.curOptions.buttonOutlineColor = "rgb(%d,%d,%d)" % \
 
2608
                ( self.curOptions.colorButtonOutline.Red(), 
 
2609
                  self.curOptions.colorButtonOutline.Green(),
 
2610
                  self.curOptions.colorButtonOutline.Blue()   )
 
2611
 
 
2612
    def OnUseForAll(self, evt):
 
2613
        """Use the current menu settings for all menus on disc."""
 
2614
        countItems = self.parent.UseForAllItems(self.curOptions)
 
2615
        # Display acknowledgement
 
2616
        dlgAck = wx.MessageDialog(self,
 
2617
            "The current menu settings were copied to\n"
 
2618
            "%d other menus on the disc." % countItems,
 
2619
            "Settings copied", wx.OK | wx.ICON_INFORMATION)
 
2620
        dlgAck.ShowModal()
 
2621
      
 
2622
    def SetOptions(self, menuOpts):
 
2623
        """Set control values based on the provided MenuOptions."""
 
2624
        self.curOptions = menuOpts
 
2625
 
 
2626
        self.txtHeading.SetLabel("Menu options: %s" % self.curOptions.title)
 
2627
        # Background
 
2628
        self.txtMenuTitleFontSize.SetValue(self.curOptions.titlefontsize)
 
2629
        self.txtMenuTitle.SetValue(self.curOptions.menutitle)
 
2630
        self.txtBGImage.SetValue(self.curOptions.background)
 
2631
        self.txtBGAudio.SetValue(self.curOptions.audio or '')
 
2632
        self.txtMenuLength.SetValue(self.curOptions.menulength)
 
2633
        # Menu text
 
2634
        self.btnFontChooserDialog.SetFont(self.curOptions.font)
 
2635
        if self.curOptions.font.GetFaceName() == "":
 
2636
            self.btnFontChooserDialog.SetLabel("Default")
 
2637
        else:
 
2638
            self.btnFontChooserDialog.SetLabel(
 
2639
                self.curOptions.font.GetFaceName() )
 
2640
        self.txtFontSize.SetValue(self.curOptions.fontsize)
 
2641
        self.chAlignment.SetSelection(
 
2642
            util.text_to_ID(self.curOptions.alignment))
 
2643
        self.chFillType.SetSelection(util.text_to_ID(self.curOptions.fillType))
 
2644
        self.btnFillColor.SetBackgroundColour(self.curOptions.color1)
 
2645
        self.btnFillColor2.SetBackgroundColour(self.curOptions.color2)
 
2646
        self.btnStrokeColor.SetBackgroundColour(self.curOptions.colorStroke)
 
2647
        self.txtStrokeWidth.SetValue(self.curOptions.textStrokeWidth)
 
2648
        # DVD buttons
 
2649
        self.btnButtonFontChooserDialog.SetFont(self.curOptions.buttonFont)
 
2650
        if self.curOptions.buttonFont.GetFaceName() == "":
 
2651
            self.btnButtonFontChooserDialog.SetLabel("Default")
 
2652
        else:
 
2653
            self.btnButtonFontChooserDialog.SetLabel(
 
2654
                self.curOptions.buttonFont.GetFaceName() )
 
2655
        self.btnHiColor.SetBackgroundColour(self.curOptions.colorHi)
 
2656
        self.btnSelColor.SetBackgroundColour(self.curOptions.colorSel)
 
2657
        self.cbButton.SetValue(self.curOptions.button)
 
2658
        self.btnButtonOutlineColor.SetBackgroundColour(
 
2659
            self.curOptions.colorButtonOutline)
 
2660
        # Titles
 
2661
        self.lbTitles.Set(self.curOptions.titles)
 
2662
 
 
2663
    def GetOptions(self):
 
2664
        """Get currently-set encoding options."""
 
2665
        return self.curOptions
 
2666
 
 
2667
    def SetDiscFormat(self, format):
 
2668
        """Enable/disable controls appropriate to the given disc format."""
 
2669
        if format == 'dvd':
 
2670
            self.sizDVDButtonStyle.ShowItems(True)
 
2671
        elif format in [ 'vcd', 'svcd' ]:
 
2672
            self.sizDVDButtonStyle.ShowItems(False)
 
2673
 
 
2674
 
 
2675
class VideoPanel(wx.Panel):
 
2676
    """A panel showing controls appropriate to encoding a video."""
 
2677
    def __init__(self, parent, id):
 
2678
        wx.Panel.__init__(self, parent, id)
 
2679
 
 
2680
        # Class data
 
2681
        self.curOptions = VideoOptions()
 
2682
        self.parent = parent
 
2683
 
 
2684
        # File information display
 
2685
        self.lblInFile = wx.StaticText(self, wx.ID_ANY, "Filename:")
 
2686
        self.txtInFile = wx.StaticText(self, wx.ID_ANY, "None")
 
2687
 
 
2688
        # Statix box and sizer to hold file info
 
2689
        self.sboxFileInfo = wx.StaticBox(self, wx.ID_ANY, "Video information")
 
2690
        self.sizFileInfo = wx.StaticBoxSizer(self.sboxFileInfo, wx.HORIZONTAL)
 
2691
        self.sizFileInfo.Add(self.lblInFile, 0, wx.EXPAND | wx.ALL, 6)
 
2692
        self.sizFileInfo.Add(self.txtInFile, 1, wx.EXPAND | wx.ALL, 6)
 
2693
        
 
2694
        # Radio buttons
 
2695
        # Format-selection radio buttons
 
2696
        outFormatList = ['352x240 VCD',
 
2697
                         '480x480 SVCD',
 
2698
                         '720x480 DVD',
 
2699
                         '352x480 Half-DVD',
 
2700
                         '352x240 VCD on DVD' ]
 
2701
 
 
2702
        # Aspect ratio radio buttons
 
2703
        aspectList = ['4:3 Fullscreen TV',
 
2704
                      '16:9 Widescreen TV',
 
2705
                      '2.35:1 Theatrical widescreen']
 
2706
 
 
2707
        # Radio boxes and tooltips
 
2708
        self.rbResolution = wx.RadioBox(self, wx.ID_ANY, "Output resolution",
 
2709
            wx.DefaultPosition, wx.DefaultSize,
 
2710
            outFormatList, 1, wx.RA_SPECIFY_COLS)
 
2711
        wx.EVT_RADIOBOX(self, self.rbResolution.GetId(), self.OnFormat)
 
2712
        self.rbResolution.SetToolTipString("Select which resolution you want to "
 
2713
            "encode your video in. The available resolutions are shown depending on "
 
2714
            "whether you are making a VCD, SVCD, or DVD disc.")
 
2715
        self.rbAspect = wx.RadioBox(self, wx.ID_ANY, "Aspect ratio of input",
 
2716
            wx.DefaultPosition, wx.DefaultSize,
 
2717
            aspectList, 1, wx.RA_SPECIFY_COLS)
 
2718
        wx.EVT_RADIOBOX(self, self.rbAspect.GetId(), self.OnAspect)
 
2719
        self.rbAspect.SetToolTipString("Select which aspect ratio the original video "
 
2720
            "is in. If it is roughly TV-shaped, use '4:3'. If it is more than "
 
2721
            "twice as wide as it is tall, use '2.35:1'. If it's somewhere in "
 
2722
            "between, use '16:9'.")
 
2723
 
 
2724
        # Sizer for radioboxes
 
2725
        self.sizResAspect = wx.BoxSizer(wx.HORIZONTAL)
 
2726
        self.sizResAspect.Add(self.rbResolution, 1, wx.EXPAND | wx.ALL)
 
2727
        self.sizResAspect.Add(self.rbAspect, 1, wx.EXPAND | wx.ALL)
 
2728
                
 
2729
        # Direct-entry CLI option box
 
2730
        self.lblCLIOptions = wx.StaticText(self, wx.ID_ANY, "Custom options:")
 
2731
        self.txtCLIOptions = wx.TextCtrl(self, wx.ID_ANY, "")
 
2732
        self.txtCLIOptions.SetToolTipString("Type custom tovid command-line "
 
2733
            "options that you'd like to use, separated by spaces. Warning:"
 
2734
            "Please make sure you know what you are doing!")
 
2735
        wx.EVT_TEXT(self, self.txtCLIOptions.GetId(), self.OnCLIOptions)
 
2736
        self.sizCLIOptions = wx.BoxSizer(wx.HORIZONTAL)
 
2737
        self.sizCLIOptions.Add(self.lblCLIOptions, 0, wx.EXPAND | wx.ALL, 8)
 
2738
        self.sizCLIOptions.Add(self.txtCLIOptions, 1, wx.EXPAND | wx.ALL, 8)
 
2739
 
 
2740
        #Combo boxes for extra options
 
2741
        self.lblQuality = wx.StaticText(self, wx.ID_ANY, "Quality: ")
 
2742
        self.cbQuality = wx.ComboBox(self, wx.ID_ANY, choices = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"], style=wx.CB_DROPDOWN|wx.CB_READONLY)
 
2743
        self.cbQuality.SetToolTipString("Choose compression level: \n" \
 
2744
                                        "1 = Low quality but small files\n" \
 
2745
                                        "10 = High quality but large files")
 
2746
        wx.EVT_TEXT(self, self.cbQuality.GetId(), self.OnQualityChange)
 
2747
 
 
2748
        self.cbQuality.SetValue("8")
 
2749
        self.lblQualityPlaceHolder = wx.StaticText(self, wx.ID_ANY, " ")
 
2750
 
 
2751
        self.lblEncoding = wx.StaticText(self, wx.ID_ANY, "Encoding: ")
 
2752
        self.cbEncodingMethod = wx.ComboBox(self, wx.ID_ANY, choices = ["mpeg2enc", "ffmpeg"], style=wx.CB_DROPDOWN|wx.CB_READONLY)
 
2753
        self.cbEncodingMethod.SetValue("mpeg2enc")
 
2754
        self.cbEncodingMethod.SetToolTipString("Choose encoding method: \n" \
 
2755
                                        "mpeg2enc - Generally a bit better image but slower to encode\n" \
 
2756
                                        "ffmpeg - Generally a bit worse image but faster to encode\n" \
 
2757
                                        "NB, at lower quality settings, ffmpeg may be better.")
 
2758
        wx.EVT_TEXT(self, self.cbEncodingMethod.GetId(), self.OnEncodingChange)
 
2759
        self.lblTypePlaceHolder = wx.StaticText(self, wx.ID_ANY, " ")
 
2760
 
 
2761
        # Sizers for combo boxes
 
2762
        self.sizQuality = wx.BoxSizer(wx.HORIZONTAL)
 
2763
        self.sizQuality.Add(self.lblQuality, 0, wx.EXPAND | wx.ALL)
 
2764
        self.sizQuality.Add(self.cbQuality, 0, wx.EXPAND | wx.ALL)
 
2765
        self.sizQuality.Add(self.lblQualityPlaceHolder, 1, wx.EXPAND | wx.ALL)
 
2766
 
 
2767
        self.sizType = wx.BoxSizer(wx.HORIZONTAL)
 
2768
        self.sizType.Add(self.lblEncoding, 0, wx.EXPAND | wx.ALL)
 
2769
        self.sizType.Add(self.cbEncodingMethod, 0, wx.EXPAND | wx.ALL)
 
2770
        self.sizType.Add(self.lblTypePlaceHolder, 1, wx.EXPAND | wx.ALL)
 
2771
 
 
2772
        self.sizQualityType = wx.BoxSizer(wx.HORIZONTAL)
 
2773
        self.sizQualityType.Add(self.sizQuality, 1, wx.EXPAND | wx.ALL)
 
2774
        self.sizQualityType.Add(self.sizType, 1, wx.EXPAND | wx.ALL)
 
2775
 
 
2776
        # Sizer to hold all encoding options
 
2777
        self.sizEncOpts = wx.BoxSizer(wx.VERTICAL)
 
2778
        self.sizEncOpts.Add(self.sizResAspect, 1, wx.EXPAND | wx.ALL)
 
2779
        self.sizEncOpts.Add(self.sizQualityType, 0, wx.EXPAND | wx.ALL)
 
2780
        self.sizEncOpts.Add(self.sizCLIOptions, 0, wx.EXPAND | wx.ALL)
 
2781
 
 
2782
        # Button to preview the video
 
2783
        self.btnPreview = wx.Button(self, wx.ID_ANY, "Preview video")
 
2784
        self.btnPreview.SetToolTipString("Play the video using mplayer")
 
2785
        wx.EVT_BUTTON(self, self.btnPreview.GetId(), self.OnPreview)
 
2786
 
 
2787
        # Button to copy video options to all videos on disc
 
2788
        self.btnUseForAll = wx.Button(self, wx.ID_ANY,
 
2789
            "Use these settings for all videos")
 
2790
        self.btnUseForAll.SetToolTipString("Apply the current video"
 
2791
            " settings, including resolution, aspect ratio, and"
 
2792
            " custom command-line options, to all videos on the disc.")
 
2793
        wx.EVT_BUTTON(self, self.btnUseForAll.GetId(), self.OnUseForAll)
 
2794
 
 
2795
        # Video options heading
 
2796
        self.txtHeading = HeadingText(self, wx.ID_ANY, "Video options")
 
2797
 
 
2798
 
 
2799
        # Add controls to main vertical sizer
 
2800
        self.sizMain = wx.BoxSizer(wx.VERTICAL)
 
2801
        self.sizMain.Add(self.txtHeading, 0, wx.EXPAND | wx.ALL, 8)
 
2802
        self.sizMain.Add(self.sizFileInfo, 0, wx.EXPAND | wx.ALL, 8)
 
2803
        self.sizMain.Add(self.sizEncOpts, 1, wx.EXPAND | wx.ALL, 8)
 
2804
        self.sizMain.Add(self.btnPreview, 0, wx.EXPAND | wx.ALL, 8)
 
2805
        self.sizMain.Add(self.btnUseForAll, 0, wx.EXPAND | wx.ALL, 8)
 
2806
        self.SetSizer(self.sizMain)
 
2807
 
 
2808
    def OnFormat(self, evt):
 
2809
        """Set appropriate format based on radio button selection."""
 
2810
        # Convert integer value to text representation
 
2811
        # (e.g., ID_FMT_DVD to 'dvd')
 
2812
        self.curOptions.format = util.ID_to_text('format', evt.GetInt())
 
2813
 
 
2814
    def OnAspect(self, evt):
 
2815
        """Set aspect ratio based on radio button selection."""
 
2816
        self.curOptions.aspect = util.ID_to_text('aspect', evt.GetInt())
 
2817
 
 
2818
    def OnQualityChange(self, evt):
 
2819
        """Update quality options when the user changes the combobox."""
 
2820
        self.curOptions.quality = self.cbQuality.GetValue()
 
2821
 
 
2822
    def OnEncodingChange(self, evt):
 
2823
        """Update encoding method when the user changes the combobox."""
 
2824
        self.curOptions.encodingMethod = self.cbEncodingMethod.GetValue()
 
2825
 
 
2826
    def OnCLIOptions(self, evt):
 
2827
        """Update custom CLI options when the user edits the textbox."""
 
2828
        self.curOptions.addoptions = self.txtCLIOptions.GetValue()
 
2829
 
 
2830
    def OnUseForAll(self, evt):
 
2831
        """Use the current video settings for all videos on disc."""
 
2832
        countItems = self.parent.UseForAllItems(self.curOptions)
 
2833
        # Display acknowledgement
 
2834
        dlgAck = wx.MessageDialog(self,
 
2835
            "The current video settings were copied to\n"
 
2836
            "%d other videos on the disc." % countItems,
 
2837
            "Settings copied", wx.OK | wx.ICON_INFORMATION)
 
2838
        dlgAck.ShowModal()
 
2839
 
 
2840
    def OnPreview(self, evt):
 
2841
        """Preview the video in mplayer."""
 
2842
        strCommand = "gmplayer \"%s\"" % self.curOptions.inFile
 
2843
        wx.Execute(strCommand, wx.EXEC_SYNC)
 
2844
 
 
2845
 
 
2846
    def SetOptions(self, videoOpts):
 
2847
        """Set control values based on the provided VideoOptions."""
 
2848
        self.curOptions = videoOpts
 
2849
 
 
2850
        self.txtHeading.SetLabel("Video options: %s" % self.curOptions.title)
 
2851
        self.txtInFile.SetLabel(self.curOptions.inFile)
 
2852
        self.rbResolution.SetSelection(util.text_to_ID(self.curOptions.format))
 
2853
        self.rbAspect.SetSelection(util.text_to_ID(self.curOptions.aspect))
 
2854
        self.cbQuality.SetValue(self.curOptions.quality)
 
2855
        self.cbEncodingMethod.SetValue(self.curOptions.encodingMethod)
 
2856
        self.txtCLIOptions.SetValue(self.curOptions.addoptions)
 
2857
 
 
2858
    def GetOptions(self):
 
2859
        """Return the currently-set encoding options."""
 
2860
        return self.curOptions
 
2861
 
 
2862
    def SetDiscFormat(self, format):
 
2863
        """Enable/disable controls to suit DVD, VCD, or SVCD-compliance."""
 
2864
        # For DVD, disable non-DVD output formats
 
2865
        if format == 'dvd':
 
2866
            for rbItem in [ID_FMT_DVD, ID_FMT_HALFDVD, ID_FMT_DVDVCD]:
 
2867
                self.rbResolution.EnableItem(rbItem, True)
 
2868
            for rbItem in [ID_FMT_SVCD, ID_FMT_VCD]:
 
2869
                self.rbResolution.EnableItem(rbItem, False)
 
2870
        # For VCD, enable only VCD output and disable bitrate controls
 
2871
        elif format == 'vcd':
 
2872
            for rbItem in range(0, 5):
 
2873
                self.rbResolution.EnableItem(rbItem, False)
 
2874
            self.rbResolution.EnableItem(ID_FMT_VCD, True)
 
2875
        # For SVCD, enable only SVCD output
 
2876
        elif format == 'svcd':
 
2877
            for rbItem in range(0, 5):
 
2878
                self.rbResolution.EnableItem(rbItem, False)
 
2879
            self.rbResolution.EnableItem(ID_FMT_SVCD, True)
 
2880
        # Unknown format?
 
2881
        else:
 
2882
            print "VideoPanel.SetDiscFormat: Unknown format %s" % format 
 
2883
    
 
2884
    def SetDiscTVSystem(self, format):
 
2885
        """Set NTSC or PAL, and show appropriate controls."""
 
2886
        # Display NTSC resolutions in format radiobox
 
2887
        if format in [ 'ntsc', 'ntscfilm' ]:
 
2888
            self.rbResolution.SetItemLabel(ID_FMT_VCD, '352x240 VCD')
 
2889
            self.rbResolution.SetItemLabel(ID_FMT_SVCD, '480x480 SVCD')
 
2890
            self.rbResolution.SetItemLabel(ID_FMT_DVD, '720x480 DVD')
 
2891
            self.rbResolution.SetItemLabel(ID_FMT_HALFDVD, '352x480 Half-DVD')
 
2892
            self.rbResolution.SetItemLabel(ID_FMT_DVDVCD, '352x240 VCD on DVD')
 
2893
        # Display PAL resolutions in format radiobox
 
2894
        elif format == 'pal':
 
2895
            self.rbResolution.SetItemLabel(ID_FMT_VCD, '352x288 VCD')
 
2896
            self.rbResolution.SetItemLabel(ID_FMT_SVCD, '480x576 SVCD')
 
2897
            self.rbResolution.SetItemLabel(ID_FMT_DVD, '720x576 DVD')
 
2898
            self.rbResolution.SetItemLabel(ID_FMT_HALFDVD, '352x576 Half-DVD')
 
2899
            self.rbResolution.SetItemLabel(ID_FMT_DVDVCD, '352x288 VCD on DVD')
 
2900
        # Unknown format?
 
2901
        else:
 
2902
            print "VideoPanel.SetDiscTVSystem: Unknown format %s" % format
 
2903
 
 
2904
# ************************************************************************
 
2905
#
 
2906
# todiscgui panels
 
2907
#
 
2908
# Note: Each panel stores a subset of todisc command-line options in a
 
2909
# todisc_opts dictionary; GUI events should set dictionary items appropriately.
 
2910
#
 
2911
# todisc options that do not have GUI controls yet:
 
2912
#
 
2913
#    -debug
 
2914
#    -keepfiles
 
2915
#    -submenu-length
 
2916
#    -loop
 
2917
#    -menu-fontsize
 
2918
#    -tovidopts
 
2919
#    -thumb-fontsize
 
2920
#
 
2921
# ************************************************************************
 
2922
 
 
2923
class PlaylistTabPanel(wx.Panel):
 
2924
    def __init__(self, parent, id):
 
2925
        wx.Panel.__init__(self, parent, id)
 
2926
        # todisc options controlled by this panel, and default values
 
2927
        # (as a dictionary of option/value pairs)
 
2928
        self.todisc_opts = {\
 
2929
            'dvd': True,
 
2930
            'svcd': False,
 
2931
            'ntsc': True,
 
2932
            'pal': False,
 
2933
            'submenus': False,
 
2934
            'chapters': 6,
 
2935
            'files': [],
 
2936
            'titles': [],
 
2937
            'submenu-titles': [],
 
2938
            'submenu-audio': None,
 
2939
            'out': ''
 
2940
            }
 
2941
        self.szVideoInfoBox_staticbox = \
 
2942
            wx.StaticBox(self, wx.ID_ANY, _("Video Information"))
 
2943
        self.szTitleInfoBox_staticbox = \
 
2944
            wx.StaticBox(self, wx.ID_ANY, _("Title"))
 
2945
        self.szAudioInfoBox_staticbox = \
 
2946
            wx.StaticBox(self, wx.ID_ANY, _("Audio"))
 
2947
        self.discFormat = \
 
2948
            wx.RadioBox(self, wx.ID_ANY, _("Disc Format"),
 
2949
                        choices=[_("DVD"), _("SVCD")],
 
2950
                        majorDimension=1,
 
2951
                        style=wx.RA_SPECIFY_ROWS)
 
2952
        self.videoFormat = \
 
2953
            wx.RadioBox(self, wx.ID_ANY, _("Video Format"),
 
2954
                        choices=[_("NTSC"), _("PAL")],
 
2955
                        majorDimension=1,
 
2956
                        style=wx.RA_SPECIFY_ROWS)
 
2957
 
 
2958
        # Submenus checkbox
 
2959
        self.submenus = wx.CheckBox(self, wx.ID_ANY, _("Create submenus?"))
 
2960
        wx.EVT_CHECKBOX(self, self.submenus.GetId(), self.submenus_OnClick)
 
2961
 
 
2962
        self.lblChapters = \
 
2963
            wx.StaticText(self, wx.ID_ANY, _("Chapters per video:"))
 
2964
        self.chapters = \
 
2965
            wx.SpinCtrl(self, wx.ID_ANY, "6", min=0, max=100,
 
2966
                        style=wx.TE_RIGHT)
 
2967
        self.files = wx.ListBox(self, wx.ID_ANY, choices=[])
 
2968
 
 
2969
        # Thumb title
 
2970
        self.lblTitle = \
 
2971
            wx.StaticText(self, wx.ID_ANY, _("Video Thumb Title:"))
 
2972
        self.title = wx.TextCtrl(self, wx.ID_ANY, "")
 
2973
        #wx.EVT_TEXT(self, self.title.GetId(), self.title_OnEdit)
 
2974
 
 
2975
        # Submenu title
 
2976
        self.lblSubmenuTitle = \
 
2977
            wx.StaticText(self, wx.ID_ANY, _("Video Submenu Title:"))
 
2978
        self.submenu_title = wx.TextCtrl(self, wx.ID_ANY, "")
 
2979
        #wx.EVT_TEXT(self, self.submenu_title.GetId(), self.submenu_title_OnEdit)
 
2980
 
 
2981
        # Submenu audio
 
2982
        self.lblSubmenuAudio = \
 
2983
            wx.StaticText(self, wx.ID_ANY, _("Audio for Submenu:"))
 
2984
        self.submenu_audio = wx.TextCtrl(self, wx.ID_ANY, "")
 
2985
        #wx.EVT_TEXT(self, self.submenu_audio.GetId(), self.submenu_audio_OnEdit)
 
2986
        self.subaudio_all = \
 
2987
            wx.CheckBox(self, wx.ID_ANY, _("Use for all submenu audio?"))
 
2988
        #wx.EVT_BUTTON(self, self.subaudio_all.GetId(), self.subaudio_all_OnClick)
 
2989
        self.btnAudio = wx.Button(self, wx.ID_ANY, _("Browse"))
 
2990
        wx.EVT_BUTTON(self, self.btnAudio.GetId(), self.btnAudio_OnClick)
 
2991
 
 
2992
        # "Add Video" button
 
2993
        self.btnAddVideo = wx.Button(self, wx.ID_ANY, _("Add Video"))
 
2994
        wx.EVT_BUTTON(self, self.btnAddVideo.GetId(), self.btnAddVideo_OnClick)
 
2995
        # "Remove Video" button
 
2996
        self.btnRemoveVideo = wx.Button(self, wx.ID_ANY, _("Remove Video"))
 
2997
        wx.EVT_BUTTON(self, self.btnRemoveVideo.GetId(), \
 
2998
                      self.btnRemoveVideo_OnClick)
 
2999
 
 
3000
        self.static_line_4 = wx.StaticLine(self, wx.ID_ANY)
 
3001
        self.lblOut = wx.StaticText(self, wx.ID_ANY, _("Output"))
 
3002
 
 
3003
        # Output file textbox and browse button
 
3004
        self.out = wx.TextCtrl(self, wx.ID_ANY, "")
 
3005
        self.btnOut = wx.Button(self, wx.ID_ANY, _("Browse"))
 
3006
        wx.EVT_BUTTON(self, self.btnOut.GetId(), self.btnOut_OnClick)
 
3007
        
 
3008
        self.discFormat.SetSelection(0)
 
3009
        self.videoFormat.SetSelection(0)
 
3010
        self.files.SetMinSize((414, 225))
 
3011
        self.btnAddVideo.SetDefault()
 
3012
        self.btnRemoveVideo.Disable()
 
3013
        self.DisableSubmenus()
 
3014
        
 
3015
        szFormats = wx.FlexGridSizer(1, 2, 0, 5)
 
3016
        szFormats.Add(self.discFormat, 0,
 
3017
                      wx.EXPAND|wx.ALIGN_CENTER_HORIZONTAL, 0)
 
3018
        szFormats.Add(self.videoFormat, 0,
 
3019
                      wx.EXPAND|wx.ALIGN_CENTER_HORIZONTAL, 0)
 
3020
        szFormats.AddGrowableCol(0)
 
3021
        szFormats.AddGrowableCol(1)
 
3022
        
 
3023
        szChapters = wx.GridSizer(1, 2, 0, 0)
 
3024
        szChapters.Add(self.lblChapters, 0,
 
3025
                       wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
 
3026
        szChapters.Add(self.chapters, 0, wx.ALIGN_RIGHT|wx.ADJUST_MINSIZE, 0)
 
3027
        
 
3028
        szTitleInfo = wx.FlexGridSizer(2, 2, 5, 5)
 
3029
        szTitleInfo.Add(self.lblTitle, 0,
 
3030
                        wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
 
3031
        szTitleInfo.Add(self.title, 0, wx.EXPAND, 0)
 
3032
        szTitleInfo.Add(self.lblSubmenuTitle, 0,
 
3033
                        wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
 
3034
        szTitleInfo.Add(self.submenu_title, 0, wx.EXPAND, 0)
 
3035
        szTitleInfo.AddGrowableCol(1)
 
3036
        
 
3037
        szTitleInfoBox = \
 
3038
            wx.StaticBoxSizer(self.szTitleInfoBox_staticbox, wx.VERTICAL)
 
3039
        szTitleInfoBox.Add(szTitleInfo, 1, wx.EXPAND, 0)
 
3040
        
 
3041
        szAudioInfo = wx.FlexGridSizer(1, 2, 5, 5)
 
3042
        szAudioInfo.Add(self.lblSubmenuAudio, 0,
 
3043
                        wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
 
3044
        szAudioInfo.Add(self.submenu_audio, 0, wx.EXPAND, 0)
 
3045
        szAudioInfo.AddGrowableCol(1)
 
3046
        
 
3047
        szAudioOptions = wx.GridSizer(1, 2, 0, 5)
 
3048
        szAudioOptions.Add(self.subaudio_all, 0,
 
3049
                           wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
 
3050
        szAudioOptions.Add(self.btnAudio, 0,
 
3051
                           wx.ALIGN_RIGHT|wx.ADJUST_MINSIZE, 0)
 
3052
        szAudioInfoBox = \
 
3053
            wx.StaticBoxSizer(self.szAudioInfoBox_staticbox, wx.VERTICAL)
 
3054
        szAudioInfoBox.Add(szAudioInfo, 1, wx.EXPAND, 0)
 
3055
        szAudioInfoBox.Add(szAudioOptions, 1, wx.EXPAND, 0)
 
3056
        
 
3057
        szVideoInfo = wx.FlexGridSizer(3, 1, 5, 5)
 
3058
        szVideoInfo.Add(self.files, 0, wx.EXPAND, 0)
 
3059
        szVideoInfo.Add(szTitleInfoBox, 1, wx.EXPAND, 0)
 
3060
        szVideoInfo.Add(szAudioInfoBox, 1, wx.EXPAND, 0)
 
3061
        szVideoInfo.AddGrowableRow(0)
 
3062
        szVideoInfo.AddGrowableCol(0)
 
3063
        
 
3064
        szVideoInfoBox = \
 
3065
            wx.StaticBoxSizer(self.szVideoInfoBox_staticbox, wx.VERTICAL)
 
3066
        szVideoInfoBox.Add(szVideoInfo, 0, wx.EXPAND, 0)
 
3067
        
 
3068
        szVidButtons = wx.BoxSizer(wx.HORIZONTAL)
 
3069
        szVidButtons.Add(self.btnAddVideo, 1, wx.ADJUST_MINSIZE, 0)
 
3070
        szVidButtons.Add(self.btnRemoveVideo, 1, wx.ADJUST_MINSIZE, 0)
 
3071
        
 
3072
        szOutput = wx.FlexGridSizer(1, 3, 0, 7)
 
3073
        szOutput.Add(self.lblOut, 0, wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
 
3074
        szOutput.Add(self.out, 0, wx.EXPAND, 0)
 
3075
        szOutput.Add(self.btnOut, 0, wx.ADJUST_MINSIZE, 0)
 
3076
        szOutput.AddGrowableCol(1)
 
3077
        
 
3078
        szPlaylist = wx.FlexGridSizer(7, 1, 7, 0)
 
3079
        szPlaylist.Add(szFormats, 0, wx.EXPAND|wx.ALIGN_CENTER_HORIZONTAL, 0)
 
3080
        szPlaylist.Add(self.submenus, 0,
 
3081
                       wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
 
3082
        szPlaylist.Add(szChapters, 1, wx.EXPAND, 0)
 
3083
        szPlaylist.Add(szVideoInfoBox, 1, wx.EXPAND, 0)
 
3084
        szPlaylist.Add(szVidButtons, 1, wx.EXPAND, 0)
 
3085
        szPlaylist.Add(self.static_line_4, 0, wx.EXPAND, 0)
 
3086
        szPlaylist.Add(szOutput, 1, wx.EXPAND, 0)
 
3087
        szPlaylist.Fit(self)
 
3088
        szPlaylist.SetSizeHints(self)
 
3089
        szPlaylist.AddGrowableRow(3)
 
3090
        szPlaylist.AddGrowableCol(0)
 
3091
        
 
3092
        self.SetAutoLayout(True)
 
3093
        self.SetSizer(szPlaylist)
 
3094
        
 
3095
 
 
3096
    def EnableSubmenus(self):
 
3097
        self.submenu_title.Enable()
 
3098
        self.submenu_audio.Enable()
 
3099
        self.subaudio_all.Enable()
 
3100
        self.btnAudio.Enable()
 
3101
 
 
3102
    def DisableSubmenus(self):
 
3103
        self.submenu_title.Disable()
 
3104
        self.submenu_audio.Disable()
 
3105
        self.subaudio_all.Disable()
 
3106
        self.btnAudio.Disable()
 
3107
 
 
3108
    def submenus_OnClick(self, evt):
 
3109
        if (evt.IsChecked()):
 
3110
            self.EnableSubmenus()
 
3111
            self.GetParent().GetParent().nbMenus.EnableSubmenus()
 
3112
            self.todisc_opts['submenus'] = True
 
3113
        else:
 
3114
            self.DisableSubmenus()
 
3115
            self.GetParent().GetParent().nbMenus.DisableSubmenus()
 
3116
            self.todisc_opts['submenus'] = False
 
3117
 
 
3118
    def btnAddVideo_OnClick(self, evt):
 
3119
        dlgFile = wx.FileDialog(None)
 
3120
        if (dlgFile.ShowModal() == wx.ID_OK):
 
3121
            self.files.Append(dlgFile.GetPath())
 
3122
            if (not self.btnRemoveVideo.IsEnabled()):
 
3123
                self.btnRemoveVideo.Enable()
 
3124
 
 
3125
    def btnRemoveVideo_OnClick(self, evt):
 
3126
        self.files.Delete(self.files.GetSelection())
 
3127
        if (self.files.GetCount() == 0):
 
3128
            self.btnRemoveVideo.Disable()
 
3129
 
 
3130
    def btnAudio_OnClick(self, evt):
 
3131
        dlgFile = wx.FileDialog(None)
 
3132
        if (dlgFile.ShowModal() == wx.ID_OK):
 
3133
            self.submenu_audio.SetValue(dlgFile.GetPath())
 
3134
 
 
3135
    def btnOut_OnClick(self, evt):
 
3136
        dlgFile = wx.DirDialog(None)
 
3137
        if (dlgFile.ShowModal() == wx.ID_OK):
 
3138
            self.out.SetValue(dlgFile.GetPath())
 
3139
            self.tovid_opts['out'] = dlgFile.GetPath()
 
3140
 
 
3141
class MenuTabPanel(wx.Panel):
 
3142
    def __init__(self, parent, id):
 
3143
        wx.Panel.__init__(self, parent, id)
 
3144
        # todisc options controlled by this panel, and default values
 
3145
        # (as a dictionary of option/value pairs)
 
3146
        self.todisc_opts = {\
 
3147
            'menu-title': "My Video Collection",
 
3148
            'menu-font': wx.FontData(),
 
3149
            'title-colour': None,
 
3150
            'stroke-color': None,
 
3151
            'menu-fade': None,
 
3152
            'text-mist': None,
 
3153
            'text-mist-color': None,
 
3154
            'text-mist-opacity': None,
 
3155
            'bgaudio': None,
 
3156
            'bgimage': None,
 
3157
            'bgvideo': None,
 
3158
            'menu-audio-fade': None,
 
3159
            'submenu-audio-fade': None,
 
3160
            'static': None,
 
3161
            'menu-length': None,
 
3162
            'ani-submenus': None,
 
3163
            'submenu-title-color': None,
 
3164
            'submenu-stroke-color': None
 
3165
            }
 
3166
 
 
3167
        self.szMistOpacity_staticbox = \
 
3168
            wx.StaticBox(self, wx.ID_ANY, _("Text Mist Opacity"))
 
3169
        self.szMainMenuBox_staticbox = \
 
3170
            wx.StaticBox(self, wx.ID_ANY, _("Main Menu"))
 
3171
        self.szBackgroundBox_staticbox = \
 
3172
            wx.StaticBox(self, wx.ID_ANY, _("Background"))
 
3173
        self.szAudioFadeBox_staticbox = \
 
3174
            wx.StaticBox(self, wx.ID_ANY, _("Audio Fade"))
 
3175
        self.szMenuAnim_staticbox = \
 
3176
            wx.StaticBox(self, wx.ID_ANY, _("Menu Animations"))
 
3177
        self.szSubmenuBox_staticbox = \
 
3178
            wx.StaticBox(self, wx.ID_ANY, _("Submenus"))
 
3179
        
 
3180
        # Main menu title
 
3181
        self.lblMenuTitle = wx.StaticText(self, wx.ID_ANY, _("Title:"))
 
3182
        self.menu_title = wx.TextCtrl(self, wx.ID_ANY, "")
 
3183
        #wx.EVT_TEXT(self, self.menu_title.GetId(), self.menu_title_OnEdit)
 
3184
        
 
3185
        self.lblMenuFont = wx.StaticText(self, wx.ID_ANY, _("Title Font:"))
 
3186
        self.btnMenuFont = wx.Button(self, wx.ID_ANY, _("Default"))
 
3187
        self.lblTitleColor = wx.StaticText(self, wx.ID_ANY, _("Title Color:"))
 
3188
        self.btnTitleColor = wx.Button(self, wx.ID_ANY, _("Choose"))
 
3189
        self.lblStrokeColor = wx.StaticText(self, wx.ID_ANY, _("Stroke Color:"))
 
3190
        self.btnStrokeColor = wx.Button(self, wx.ID_ANY, _("Choose"))
 
3191
        self.static_line_3 = wx.StaticLine(self, wx.ID_ANY)
 
3192
        self.menu_fade = wx.CheckBox(self, wx.ID_ANY, _("Use menu fade?"))
 
3193
        self.static_line_2 = wx.StaticLine(self, wx.ID_ANY)
 
3194
        self.text_mist = wx.CheckBox(self, wx.ID_ANY, _("Use text mist?"))
 
3195
        self.lblTextMistColor = \
 
3196
            wx.StaticText(self, wx.ID_ANY, _("Text Mist Color:"))
 
3197
        self.btnTextMistColor = wx.Button(self, wx.ID_ANY, _("Choose"))
 
3198
        self.opacity = \
 
3199
            wx.Slider(self, wx.ID_ANY, 100, 0, 100,
 
3200
                      style=wx.SL_HORIZONTAL|wx.SL_AUTOTICKS|wx.SL_LABELS)
 
3201
 
 
3202
        # Background audio
 
3203
        self.lblBgAudio = wx.StaticText(self, wx.ID_ANY, _("Background Audio"))
 
3204
        self.bgaudio = wx.TextCtrl(self, wx.ID_ANY, "")
 
3205
        wx.EVT_TEXT(self, self.bgaudio.GetId(), self.bgaudio_OnEdit)
 
3206
        self.btnBgAudio = wx.Button(self, wx.ID_ANY, _("Browse"))
 
3207
 
 
3208
        # Background image
 
3209
        self.lblBgImage = wx.StaticText(self, wx.ID_ANY, _("Background Image"))
 
3210
        self.bgimage = wx.TextCtrl(self, wx.ID_ANY, "")
 
3211
        wx.EVT_TEXT(self, self.bgimage.GetId(), self.bgimage_OnEdit)
 
3212
        self.btnBgImage = wx.Button(self, wx.ID_ANY, _("Browse"))
 
3213
 
 
3214
        # Background video
 
3215
        self.lblBgVideo = wx.StaticText(self, wx.ID_ANY, _("Background Video"))
 
3216
        self.bgvideo = wx.TextCtrl(self, wx.ID_ANY, "")
 
3217
        wx.EVT_TEXT(self, self.bgvideo.GetId(), self.bgvideo_OnEdit)
 
3218
        self.btnBgVideo = wx.Button(self, wx.ID_ANY, _("Browse"))
 
3219
 
 
3220
        self.lblMenuAudioFade = wx.StaticText(self, wx.ID_ANY, _("Menu:"))
 
3221
        self.menu_audio_fade = wx.SpinCtrl(self, wx.ID_ANY, "", min=0, max=100)
 
3222
        self.lblSubmenuAudioFade = \
 
3223
            wx.StaticText(self, wx.ID_ANY, _("Submenu:"))
 
3224
        self.submenu_audio_fade = \
 
3225
            wx.SpinCtrl(self, wx.ID_ANY, "", min=0, max=100)
 
3226
        self.static = wx.CheckBox(self, wx.ID_ANY, _("Animate menus?"))
 
3227
        self.lblMenuLength = \
 
3228
            wx.StaticText(self, wx.ID_ANY, _("Menu animation length:"))
 
3229
        self.menu_length = wx.SpinCtrl(self, wx.ID_ANY, "", min=0, max=100)
 
3230
        self.lblLoop = wx.StaticText(self, wx.ID_ANY, _("Menu pause length:"))
 
3231
        self.loop = wx.SpinCtrl(self, wx.ID_ANY, "", min=0, max=100)
 
3232
        self.static_line_1 = wx.StaticLine(self, wx.ID_ANY)
 
3233
        self.ani_submenus = wx.CheckBox(self, wx.ID_ANY, _("Animate submenus?"))
 
3234
        self.lblSubmenuTitleColor = \
 
3235
            wx.StaticText(self, wx.ID_ANY, _("Title Color:"))
 
3236
        self.btnSubmenuTitleColor = wx.Button(self, wx.ID_ANY, _("Choose"))
 
3237
        self.lblSubmenuStrokeColor = \
 
3238
            wx.StaticText(self, wx.ID_ANY, _("Stroke Color:"))
 
3239
        self.btnSubmenuStrokeColor = wx.Button(self, wx.ID_ANY, _("Choose"))
 
3240
        
 
3241
        self.lblMenuTitle.SetFont(\
 
3242
            wx.Font(9, wx.DEFAULT, wx.NORMAL, wx.NORMAL, 0, "DejaVu Sans"))
 
3243
        self.lblMenuFont.SetFont(\
 
3244
            wx.Font(9, wx.DEFAULT, wx.NORMAL, wx.NORMAL, 0, "DejaVu Sans"))
 
3245
        self.lblTitleColor.SetFont(\
 
3246
            wx.Font(9, wx.DEFAULT, wx.NORMAL, wx.NORMAL, 0, "DejaVu Sans"))
 
3247
        self.lblStrokeColor.SetFont(\
 
3248
            wx.Font(9, wx.DEFAULT, wx.NORMAL, wx.NORMAL, 0, "DejaVu Sans"))
 
3249
        self.lblBgAudio.SetFont(\
 
3250
            wx.Font(9, wx.DEFAULT, wx.NORMAL, wx.NORMAL, 0, "DejaVu Sans"))
 
3251
        self.lblBgImage.SetFont(\
 
3252
            wx.Font(9, wx.DEFAULT, wx.NORMAL, wx.NORMAL, 0, "DejaVu Sans"))
 
3253
        self.static.SetValue(1)
 
3254
        self.lblSubmenuTitleColor.SetFont(\
 
3255
            wx.Font(9, wx.DEFAULT, wx.NORMAL, wx.NORMAL, 0, "DejaVu Sans"))
 
3256
        self.lblSubmenuStrokeColor.SetFont(\
 
3257
            wx.Font(9, wx.DEFAULT, wx.NORMAL, wx.NORMAL, 0, "DejaVu Sans"))
 
3258
        self.btnTextMistColor.Disable()
 
3259
        self.DisableSubmenus()
 
3260
        
 
3261
        szMenuTitle = wx.FlexGridSizer(1, 2, 0, 5)
 
3262
        szMenuTitle.Add(self.lblMenuTitle, 0,
 
3263
                        wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
 
3264
        szMenuTitle.Add(self.menu_title, 0, wx.EXPAND, 0)
 
3265
        szMenuTitle.AddGrowableCol(1)
 
3266
        
 
3267
        szTitleFont = wx.FlexGridSizer(2, 4, 0, 5)
 
3268
        szTitleFont.Add(self.lblMenuFont, 0,
 
3269
                        wx.ALIGN_CENTER_VERTICAL|wx.SHAPED, 0)
 
3270
        szTitleFont.Add(self.btnMenuFont, 0, wx.ADJUST_MINSIZE, 0)
 
3271
#        szTitleFont.Add(self.lblMenuFontSize, 0, wx.ALIGN_CENTER_VERTICAL|wx.SHAPED, 0)
 
3272
#        szTitleFont.Add(self.menu_fontsize, 0, wx.ALIGN_RIGHT|wx.ADJUST_MINSIZE, 0)
 
3273
        szTitleFont.Add(self.lblTitleColor, 0,
 
3274
                        wx.ALIGN_CENTER_VERTICAL|wx.SHAPED, 0)
 
3275
        szTitleFont.Add(self.btnTitleColor, 0, wx.ADJUST_MINSIZE, 0)
 
3276
        szTitleFont.Add(self.lblStrokeColor, 0,
 
3277
                        wx.ALIGN_CENTER_VERTICAL|wx.SHAPED, 0)
 
3278
        szTitleFont.Add(self.btnStrokeColor, 0,
 
3279
                        wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
 
3280
        szTitleFont.AddGrowableCol(1)
 
3281
        
 
3282
        szTextMist = wx.FlexGridSizer(1, 3, 0, 5)
 
3283
        szTextMist.Add(self.text_mist, 0,
 
3284
                       wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
 
3285
        szTextMist.Add(self.lblTextMistColor, 0,
 
3286
                       wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL, 0)
 
3287
        szTextMist.Add(self.btnTextMistColor, 0, wx.ADJUST_MINSIZE, 0)
 
3288
        szTextMist.AddGrowableCol(1)
 
3289
        
 
3290
        szMistOpacity = \
 
3291
            wx.StaticBoxSizer(self.szMistOpacity_staticbox, wx.VERTICAL)
 
3292
        szMistOpacity.Add(self.opacity, 0, wx.EXPAND, 0)
 
3293
        
 
3294
        szMainMenuBox = \
 
3295
            wx.StaticBoxSizer(self.szMainMenuBox_staticbox, wx.VERTICAL)
 
3296
        szMainMenuBox.Add(szMenuTitle, 0, wx.EXPAND, 0)
 
3297
        szMainMenuBox.Add(szTitleFont, 1,
 
3298
                          wx.EXPAND|wx.ALIGN_CENTER_HORIZONTAL, 0)
 
3299
        szMainMenuBox.Add(self.static_line_3, 0, wx.EXPAND, 0)
 
3300
        szMainMenuBox.Add(self.menu_fade, 0,
 
3301
                          wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
 
3302
        szMainMenuBox.Add(self.static_line_2, 0, wx.EXPAND, 0)
 
3303
        szMainMenuBox.Add(szTextMist, 0, wx.EXPAND, 0)
 
3304
        szMainMenuBox.Add(szMistOpacity, 0, wx.EXPAND, 0)
 
3305
        
 
3306
        szBackground = wx.FlexGridSizer(3, 3, 5, 5)
 
3307
        szBackground.Add(self.lblBgAudio, 0,
 
3308
                         wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
 
3309
        szBackground.Add(self.bgaudio, 0, wx.EXPAND, 0)
 
3310
        szBackground.Add(self.btnBgAudio, 0, wx.ADJUST_MINSIZE, 0)
 
3311
        szBackground.Add(self.lblBgImage, 0,
 
3312
                         wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
 
3313
        szBackground.Add(self.bgimage, 0, wx.EXPAND, 0)
 
3314
        szBackground.Add(self.btnBgImage, 0, wx.ADJUST_MINSIZE, 0)
 
3315
        szBackground.Add(self.lblBgVideo, 0,
 
3316
                         wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
 
3317
        szBackground.Add(self.bgvideo, 0, wx.EXPAND, 0)
 
3318
        szBackground.Add(self.btnBgVideo, 0, wx.ADJUST_MINSIZE, 0)
 
3319
        szBackground.AddGrowableCol(1)
 
3320
        
 
3321
        szBackgroundBox = \
 
3322
            wx.StaticBoxSizer(self.szBackgroundBox_staticbox, wx.VERTICAL)
 
3323
        szBackgroundBox.Add(szBackground, 1, wx.EXPAND, 0)
 
3324
        
 
3325
        szAudioFade = wx.FlexGridSizer(1, 4, 0, 5)
 
3326
        szAudioFade.Add(self.lblMenuAudioFade, 0,
 
3327
                        wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
 
3328
        szAudioFade.Add(self.menu_audio_fade, 0, wx.ADJUST_MINSIZE, 0)
 
3329
        szAudioFade.Add(self.lblSubmenuAudioFade, 0,
 
3330
                        wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
 
3331
        szAudioFade.Add(self.submenu_audio_fade, 0, wx.ADJUST_MINSIZE, 0)
 
3332
        szAudioFade.AddGrowableCol(1)
 
3333
        
 
3334
        szAudioFadeBox = \
 
3335
            wx.StaticBoxSizer(self.szAudioFadeBox_staticbox, wx.HORIZONTAL)
 
3336
        szAudioFadeBox.Add(szAudioFade, 1, wx.EXPAND, 0)
 
3337
        
 
3338
        szMenuLength = wx.FlexGridSizer(1, 2, 0, 0)
 
3339
        szMenuLength.Add(self.lblMenuLength, 0,
 
3340
                         wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
 
3341
        szMenuLength.Add(self.menu_length, 0,
 
3342
                         wx.ALIGN_RIGHT|wx.ADJUST_MINSIZE, 0)
 
3343
        szMenuLength.AddGrowableCol(0)
 
3344
        szMenuLength.AddGrowableCol(1)
 
3345
        
 
3346
        szLoop = wx.GridSizer(1, 2, 0, 0)
 
3347
        szLoop.Add(self.lblLoop, 0,
 
3348
                   wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
 
3349
        szLoop.Add(self.loop, 0, wx.ALIGN_RIGHT|wx.ADJUST_MINSIZE, 0)
 
3350
        
 
3351
        szSubmenus = wx.FlexGridSizer(10, 1, 5, 0)
 
3352
        szSubmenus.Add(self.static, 0, wx.ADJUST_MINSIZE, 0)
 
3353
        szSubmenus.Add(szMenuLength, 1, wx.EXPAND, 0)
 
3354
        szSubmenus.Add(szLoop, 1, wx.EXPAND, 0)
 
3355
        szSubmenus.Add(self.static_line_1, 0, wx.EXPAND, 0)
 
3356
        szSubmenus.Add(self.ani_submenus, 0,
 
3357
                       wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
 
3358
        szSubmenus.AddGrowableCol(0)
 
3359
        
 
3360
        szMenuAnim = wx.StaticBoxSizer(self.szMenuAnim_staticbox, wx.VERTICAL)
 
3361
        szMenuAnim.Add(szSubmenus, 1, wx.EXPAND, 0)
 
3362
        
 
3363
        szSubmenuColor = wx.FlexGridSizer(1, 4, 0, 7)
 
3364
        szSubmenuColor.Add(self.lblSubmenuTitleColor, 0,
 
3365
                           wx.ALIGN_CENTER_VERTICAL|wx.SHAPED, 0)
 
3366
        szSubmenuColor.Add(self.btnSubmenuTitleColor, 0, wx.ADJUST_MINSIZE, 0)
 
3367
        szSubmenuColor.Add(self.lblSubmenuStrokeColor, 0,
 
3368
                           wx.ALIGN_CENTER_VERTICAL|wx.SHAPED, 0)
 
3369
        szSubmenuColor.Add(self.btnSubmenuStrokeColor, 0, wx.ADJUST_MINSIZE, 0)
 
3370
        szSubmenuColor.AddGrowableCol(1)
 
3371
        
 
3372
        szSubmenuBox = \
 
3373
            wx.StaticBoxSizer(self.szSubmenuBox_staticbox, wx.HORIZONTAL)
 
3374
        szSubmenuBox.Add(szSubmenuColor, 1, wx.EXPAND, 0)
 
3375
        
 
3376
        szMenus = wx.FlexGridSizer(5, 1, 7, 0)
 
3377
        szMenus.Add(szMainMenuBox, 1, wx.EXPAND, 0)
 
3378
        szMenus.Add(szBackgroundBox, 1, wx.EXPAND, 0)
 
3379
        szMenus.Add(szAudioFadeBox, 1, wx.EXPAND, 0)
 
3380
        szMenus.Add(szMenuAnim, 1, wx.EXPAND, 0)
 
3381
        szMenus.Add(szSubmenuBox, 1, wx.EXPAND, 0)
 
3382
        szMenus.Fit(self)
 
3383
        szMenus.SetSizeHints(self)
 
3384
        szMenus.AddGrowableCol(0)
 
3385
        
 
3386
        self.SetAutoLayout(True)
 
3387
        self.SetSizer(szMenus)
 
3388
        
 
3389
        wx.EVT_BUTTON(self, self.btnMenuFont.GetId(), self.btnMenuFont_OnClick)
 
3390
        wx.EVT_BUTTON(self, self.btnTitleColor.GetId(),
 
3391
                      self.btnTitleColor_OnClick)
 
3392
        wx.EVT_BUTTON(self, self.btnStrokeColor.GetId(),
 
3393
                      self.btnStrokeColor_OnClick)
 
3394
        wx.EVT_CHECKBOX(self, self.text_mist.GetId(), self.text_mist_OnClick)
 
3395
        wx.EVT_CHECKBOX(self, self.static.GetId(), self.static_OnClick)
 
3396
 
 
3397
    def EnableSubmenus(self):
 
3398
        self.submenu_audio_fade.Enable()
 
3399
        self.ani_submenus.Enable()
 
3400
        self.btnSubmenuTitleColor.Enable()
 
3401
        self.btnSubmenuStrokeColor.Enable()
 
3402
 
 
3403
    def DisableSubmenus(self):
 
3404
        self.submenu_audio_fade.Disable()
 
3405
        self.ani_submenus.Disable()
 
3406
        self.btnSubmenuTitleColor.Disable()
 
3407
        self.btnSubmenuStrokeColor.Disable()
 
3408
 
 
3409
    def btnMenuFont_OnClick(self, evt):
 
3410
        dlgFont = wx.FontDialog(None, wx.FontData())
 
3411
        if (dlgFont.ShowModal() == wx.ID_OK):
 
3412
            font = dlgFont.GetFontData().GetChosenFont()
 
3413
            self.todisc_opts['menu-font'] = font.GetFaceName()
 
3414
            self.todisc_opts['menu-fontsize'] = font.GetPointSize()
 
3415
            self.btnMenuFont.SetLabel(font.GetFaceName())
 
3416
 
 
3417
    def btnTitleColor_OnClick(self, evt):
 
3418
        dlgColour = wx.ColourDialog(None, self.todisc_opts['title-colour'])
 
3419
        if (dlgColour.ShowModal() == wx.ID_OK):
 
3420
            self.todisc_opts['title-colour'] = dlgColour.GetColourData()
 
3421
 
 
3422
    def btnStrokeColor_OnClick(self, evt):
 
3423
        dlgColour = wx.ColourDialog(None)
 
3424
        if (dlgColour.ShowModal() == wx.ID_OK):
 
3425
            colour = dlgColour.GetColourData().GetColour()
 
3426
            self.todisc_opts['stroke-colour'] = colour
 
3427
 
 
3428
    def text_mist_OnClick(self, evt):
 
3429
        if (evt.IsChecked()):
 
3430
            self.btnTextMistColor.Enable()
 
3431
        else:
 
3432
            self.btnTextMistColor.Disable()
 
3433
 
 
3434
    def static_OnClick(self, evt):
 
3435
        if (evt.IsChecked()):
 
3436
            self.menu_length.Enable()
 
3437
            self.loop.Enable()
 
3438
        else:
 
3439
            self.menu_length.Disable()
 
3440
            self.loop.Disable()
 
3441
    def bgaudio_OnEdit(self, evt):
 
3442
        self.todisc_opts['bgaudio'] = self.bgaudio.GetValue()
 
3443
 
 
3444
    def bgimage_OnEdit(self, evt):
 
3445
        self.todisc_opts['bgimage'] = self.bgimage.GetValue()
 
3446
 
 
3447
    def bgvideo_OnEdit(self, evt):
 
3448
        self.todisc_opts['bgvideo'] = self.bgvideo.GetValue()
 
3449
    
 
3450
class ThumbnailTabPanel(wx.Panel):
 
3451
    def __init__(self, parent, id):
 
3452
        self.todisc_opts = {\
 
3453
            'thumb-shape': None,
 
3454
            'thumb-font': None,
 
3455
            'thumb-text-color': None, 
 
3456
            'thumb-mist-color': None, 
 
3457
            'blur': None, 
 
3458
            'opacity': None, 
 
3459
            'seek': None
 
3460
            }
 
3461
 
 
3462
        wx.Panel.__init__(self, parent, id)
 
3463
        self.feather_thumbs = wx.CheckBox(self, wx.ID_ANY, _("Feather thumbnails?"))
 
3464
        self.szBlur_staticbox = wx.StaticBox(self, wx.ID_ANY, _("Blur"))
 
3465
        self.szOpacity_staticbox = wx.StaticBox(self, wx.ID_ANY, _("Opacity"))
 
3466
        self.thumb_shape = wx.RadioBox(self, wx.ID_ANY, _("Feather Shape"), choices=[_("Normal"), _("Oval"), _("Cloud"), _("Egg")], majorDimension=1, style=wx.RA_SPECIFY_ROWS)
 
3467
        self.lblThumbTitleFont = wx.StaticText(self, wx.ID_ANY, _("Thumb Font:"))
 
3468
        self.thumb_font = wx.Button(self, wx.ID_ANY, _("Default"))
 
3469
        self.lblThumbTitleColor = wx.StaticText(self, wx.ID_ANY, _("Thumb Font Color:"))
 
3470
        self.thumb_text_color = wx.Button(self, wx.ID_ANY, _("Choose"))
 
3471
        self.lblThumbTitleMistColor = wx.StaticText(self, wx.ID_ANY, _("Thumb Mist Color:"))
 
3472
        self.thumb_mist_color = wx.Button(self, wx.ID_ANY, _("Choose"))
 
3473
        self.blur = wx.Slider(self, wx.ID_ANY, 5, 0, 10, style=wx.SL_HORIZONTAL|wx.SL_AUTOTICKS|wx.SL_LABELS)
 
3474
        self.opacity = wx.Slider(self, wx.ID_ANY, 100, 0, 100, style=wx.SL_HORIZONTAL|wx.SL_AUTOTICKS|wx.SL_LABELS)
 
3475
        self.lblSeek = wx.StaticText(self, wx.ID_ANY, _("Seconds to seek before generating thumbs:"))
 
3476
        self.seek = wx.SpinCtrl(self, wx.ID_ANY, "", min=0, max=100, style=wx.TE_RIGHT)
 
3477
        
 
3478
        self.thumb_shape.SetSelection(0)
 
3479
        self.thumb_shape.Disable()
 
3480
        
 
3481
        szThumbFont = wx.FlexGridSizer(2, 4, 5, 5)
 
3482
        szThumbFont.Add(self.lblThumbTitleFont, 0, wx.ALIGN_CENTER_VERTICAL|wx.SHAPED, 0)
 
3483
        szThumbFont.Add(self.thumb_font, 0, wx.ADJUST_MINSIZE, 0)
 
3484
        szThumbFont.Add(self.lblThumbTitleColor, 0, wx.ALIGN_CENTER_VERTICAL|wx.SHAPED, 0)
 
3485
        szThumbFont.Add(self.thumb_text_color, 0, wx.ADJUST_MINSIZE, 0)
 
3486
        szThumbFont.Add(self.lblThumbTitleMistColor, 0, wx.ALIGN_CENTER_VERTICAL|wx.SHAPED, 0)
 
3487
        szThumbFont.Add(self.thumb_mist_color, 0, wx.ADJUST_MINSIZE, 0)
 
3488
        szThumbFont.AddGrowableCol(1)
 
3489
        
 
3490
        szThumbShape = wx.FlexGridSizer(2, 1, 5, 0)
 
3491
        szThumbShape.Add(self.thumb_shape, 0, wx.EXPAND, 0)
 
3492
        szThumbShape.Add(szThumbFont, 0, wx.EXPAND|wx.ALIGN_CENTER_HORIZONTAL, 0)
 
3493
        szThumbShape.AddGrowableCol(0)
 
3494
        
 
3495
        szBlur = wx.StaticBoxSizer(self.szBlur_staticbox, wx.VERTICAL)
 
3496
        szBlur.Add(self.blur, 0, wx.EXPAND, 0)
 
3497
        
 
3498
        szOpacity = wx.StaticBoxSizer(self.szOpacity_staticbox, wx.VERTICAL)
 
3499
        szOpacity.Add(self.opacity, 0, wx.EXPAND, 0)
 
3500
        
 
3501
        szSeek = wx.FlexGridSizer(1, 2, 0, 5)
 
3502
        szSeek.Add(self.lblSeek, 0, wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
 
3503
        szSeek.Add(self.seek, 0, wx.ALIGN_RIGHT|wx.ADJUST_MINSIZE, 0)
 
3504
        szSeek.AddGrowableCol(0)
 
3505
        
 
3506
        szThumbnails = wx.FlexGridSizer(5, 1, 7, 0)
 
3507
        szThumbnails.Add(self.feather_thumbs, 0, wx.ADJUST_MINSIZE, 0)
 
3508
        szThumbnails.Add(szThumbShape, 1, wx.EXPAND, 0)
 
3509
        szThumbnails.Add(szBlur, 0, wx.EXPAND, 0)
 
3510
        szThumbnails.Add(szOpacity, 0, wx.EXPAND, 0)
 
3511
        szThumbnails.Add(szSeek, 1, wx.EXPAND, 0)
 
3512
        szThumbnails.Fit(self)
 
3513
        szThumbnails.SetSizeHints(self)
 
3514
        szThumbnails.AddGrowableCol(0)
 
3515
        
 
3516
        self.SetAutoLayout(True)
 
3517
        self.SetSizer(szThumbnails)
 
3518
        
 
3519
        wx.EVT_CHECKBOX(self, self.feather_thumbs.GetId(), self.feather_thumbs_OnClick)
 
3520
 
 
3521
    def feather_thumbs_OnClick(self, evt):
 
3522
        if (evt.IsChecked()):
 
3523
            self.thumb_shape.Enable()
 
3524
        else:
 
3525
            self.thumb_shape.Disable()
 
3526
 
 
3527
class DebugTabPanel(wx.Panel):
 
3528
    def __init__(self, parent, id):
 
3529
        wx.Panel.__init__(self, parent, id)
 
3530
        self.szDebugBox_staticbox = wx.StaticBox(self, wx.ID_ANY, _("Debug Flags"))
 
3531
        self.debug = wx.CheckBox(self, wx.ID_ANY, _("Turn on debug logging?"))
 
3532
        self.keepfiles = wx.CheckBox(self, wx.ID_ANY, _("Keep files when finished?"))
 
3533
        
 
3534
        szDebugBox = wx.StaticBoxSizer(self.szDebugBox_staticbox, wx.VERTICAL)
 
3535
        szDebugBox.Add(self.debug, 0, wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
 
3536
        szDebugBox.Add(self.keepfiles, 0, wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
 
3537
        
 
3538
        szDebug = wx.FlexGridSizer(1, 1, 7, 0)
 
3539
        szDebug.Add(szDebugBox, 0, wx.EXPAND, 0)
 
3540
        szDebug.Fit(self)
 
3541
        szDebug.SetSizeHints(self)
 
3542
        szDebug.AddGrowableRow(1)
 
3543
        szDebug.AddGrowableCol(0)
 
3544
        
 
3545
        self.SetAutoLayout(True)
 
3546
        self.SetSizer(szDebug)