version="0.2" 
# plugins/Impress/__init__.py
#
# Copyright (C)  2008-2013 Pako  (lubos.ruckl@quick.cz)
#
# This file is a plugin for EventGhost.
#
# EventGhost is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# EventGhost is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with EventGhost; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

# Changelog (in reverse chronological order):
# -------------------------------------------
# 0.2 by Pako 2013-12-15 11:30 UTC+1
#     - many changes and improvements
# 0.1 by Pako 2009-01-01 17:00
#===============================================================================

eg.RegisterPlugin(
    name = "Impress",
    author = "Pako",
    guid = "{D8767FB1-CCC0-402D-AF65-D961D2D7C20F}",    
    version = version,
    kind = "program",
    description = ur'''<rst>
Adds actions to control ...

| `Appache OpenOffice Impress`__
| or
| `LibreOffice Impress`__.

__ http://www.openoffice.org/product/impress.html
__ https://www.libreoffice.org/features/impress/
''',
    createMacrosOnAdd = True,    
    url = "http://www.eventghost.net/forum/viewtopic.php?f=9&t=1052",
    icon = (
        "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"
        "TkSuQmCC"
    )
)
#===============================================================================

import os
import wx.grid as gridlib
from os.path import join, exists
from copy import deepcopy as cpy
from time import sleep
from threading import Thread, Event, Timer
from win32com.client.dynamic import Dispatch
from win32api import GetSystemMetrics
from win32gui import SetFocus, SetForegroundWindow, SetActiveWindow, IsWindow
from eg.WinApi import SendMessageTimeout
from eg.WinApi.Dynamic import PostMessage, CreateEvent, SetEvent
from eg.WinApi.Utils import GetMonitorDimensions
from eg.Classes.MainFrame.TreeCtrl import DropTarget as EventDropTarget

SYS_VSCROLL_X    = wx.SystemSettings.GetMetric(wx.SYS_VSCROLL_X)
WM_CLOSE      = 16
WM_SYSCOMMAND = 274
SC_MINIMIZE   = 61472

findPresentation = eg.WindowMatcher(
    None,
    None,
    u'SALTMPSUBFRAME',
    None,
    None,
    None,
    False,
    0.0,
    0
)
#===============================================================================
class Text:
    err1 ='''Couldn't open file "%s" !'''
    err2 =  "Couldn't find window with presentation!"
    err3 = '''Couldn't find file "%s" !'''
    opened = "Opened"
    closed = "Closed"
    endless = "Endlessly:"
    endlessToolTip = "If True, the presentation repeats endlessly"
    pause = "Pause:"
    pauseToolTip = "Duration of black screen [seconds] after last slide"
    startLbl = 'Start slide number:'



class MyTimer():
    def __init__(self, t, plugin):
        self.timer = Timer(t, self.Run)
        self.plugin = plugin
        self.timer.start()


    def Run(self):
        try:
            self.plugin.menuDlg.Close()
            self.plugin.menuDlg = None
        except:
            pass


    def Cancel(self):
        self.timer.cancel()
#===============================================================================

class NumDialog(wx.Frame):

    def __init__(self, plugin):
        self.plugin = plugin
        wx.Frame.__init__(
            self,
            None,
            -1,
            'SlideNumber', 
            style=wx.STAY_ON_TOP | wx.SIMPLE_BORDER   
        )
        plugin.numDialog = self


    def CloseDialog(self):
        self.plugin.TriggerEvent("GoToFrame.%s" % self.plugin.text.closed)
        self.plugin.numDialog = None
        wx.CallAfter(self.Close, True)
#===============================================================================
 
class Menu(wx.Frame):

    def __init__(
        self,
        fore,
        back,
        foreSel,
        backSel,
        fontInfo,
        flag,
        plugin,
        event,
        monitor,
        data,
        evtList,
        ):
        wx.Frame.__init__(
            self,
            None,
            -1,
            'Impress_menu',
            style = wx.STAY_ON_TOP|wx.SIMPLE_BORDER
        )
        self.data = data
        self.data.sort()
        tmpLst = [i[0] for i in self.data]
        self.actFileIx = 0
        if ">>ActiveFile<<" in tmpLst:
            ix = tmpLst.index(">>ActiveFile<<")
            actFile = self.data[ix][1]
            self.data.pop(ix)
            tmpLst = [i[1] for i in self.data]
            if actFile in tmpLst:
                self.actFileIx = tmpLst.index(actFile)
        self.fore     = fore
        self.back     = back
        self.foreSel  = foreSel
        self.backSel  = backSel
        self.fontInfo = fontInfo
        self.flag     = flag
        self.plugin   = plugin
        self.monitor  = monitor
        self.evtList  = evtList
        self.plugin.TriggerEvent("OnScreenMenu.%s" % self.plugin.text.opened)
        for evt in self.evtList[0]:
            eg.Bind(evt, self.onUp)
        for evt in self.evtList[1]:
            eg.Bind(evt, self.onDown)
        for evt in self.evtList[2]:
            eg.Bind(evt, self.onRight)
        for evt in self.evtList[3]:
            eg.Bind(evt, self.onEscape)
        self.menuGridCtrl = MenuGrid(self, len(self.data), wx.DefaultSize)
        mainSizer = wx.BoxSizer(wx.VERTICAL)
        self.SetSizer(mainSizer)
        mainSizer.Add(self.menuGridCtrl, 0, wx.EXPAND)
        self.Bind(wx.EVT_CLOSE, self.onClose)
        self.Bind(gridlib.EVT_GRID_CMD_CELL_LEFT_DCLICK, self.onDoubleClick, self.menuGridCtrl)
        self.Bind(wx.EVT_CHAR_HOOK, self.onFrameCharHook)
        font = wx.FontFromNativeInfoString(fontInfo)
        self.menuGridCtrl.SetFont(font)
        self.SetFont(font)                                   
        self.SetBackgroundColour((0, 0, 0))
        self.menuGridCtrl.SetBackgroundColour(self.back)
        self.menuGridCtrl.SetForegroundColour(self.fore)
        self.menuGridCtrl.SetSelectionBackground(self.backSel)
        self.menuGridCtrl.SetSelectionForeground(self.foreSel)
        if self.flag:
            self.timer = MyTimer(t = 5.0, plugin = self.plugin)
        self.menuGridCtrl.Set(self.data)
        self.DrawMenu()         
        self.plugin.menuDlg = self
        wx.Yield()
        SetEvent(event)


    def DrawMenu(self):
        self.Show(False)
        self.menuGridCtrl.SetGridCursor(-1, 0)
        monDim = GetMonitorDimensions()
        try:
            x,y,ws,hs = monDim[self.monitor]
        except IndexError:
            x,y,ws,hs = monDim[0]
        # menu height calculation:                                
        h=self.GetCharHeight()+4
        for i in range(len(self.data)):
            self.menuGridCtrl.SetRowSize(i,h)
            self.menuGridCtrl.SetCellValue(i,0," "+self.data[i][0])
        height0 = len(self.data)*h
        height1 = h*((hs-20)/h)
        height = min(height0, height1)+6
        # menu width calculation:
        width_lst=[]
        for item in self.data:
            width_lst.append(self.GetTextExtent(item[0]+' ')[0])
        width = max(width_lst)+8
        self.menuGridCtrl.SetColSize(0,width)
        self.menuGridCtrl.ForceRefresh()
        if height1 < height0:
            width += SYS_VSCROLL_X
        if width > ws-50:
            if height + SYS_HSCROLL_Y < hs:
                height += SYS_HSCROLL_Y
            width = ws-50
        width += 6
        x_pos = x + (ws - width)/2
        y_pos = y + (hs - height)/2
        self.SetDimensions(x_pos,y_pos,width,height)
        self.menuGridCtrl.SetDimensions(2, 2, width-6, height-6, wx.SIZE_AUTO)
        self.menuGridCtrl.SelectRow(self.actFileIx)
        self.menuGridCtrl.MakeCellVisible(self.actFileIx, 0)
        self.Show(True)
        self.Raise()


    def MoveCursor(self, step):
        max=len(self.data)
        if max > 0:
            self.menuGridCtrl.MoveCursor(step)


    def onUp(self, event):
        wx.CallAfter(self.menuGridCtrl.MoveCursor, -1)
        return True #stop processing this event !!!


    def onDown(self, event):
        wx.CallAfter(self.menuGridCtrl.MoveCursor, 1)
        return True #stop processing this event !!!


    def onRight(self, event):
        wx.CallAfter(self.DefaultAction)
        return True #stop processing this event !!!


    def onEscape(self, event):
        wx.CallAfter(self.destroyMenu)
        return True #stop processing this event !!!


    def DefaultAction(self):
        item = self.data[self.menuGridCtrl.GetSelection()]
        self.destroyMenu()
        self.plugin.TriggerEvent(
            "Menu.DefaultAction",
            payload = item[1:]
        )


    def onFrameCharHook(self, event):
        keyCode = event.GetKeyCode()
        if keyCode == wx.WXK_F4:
            if event.AltDown():
                self.destroyMenu()
        elif keyCode == wx.WXK_RETURN or keyCode == wx.WXK_NUMPAD_ENTER:
            self.DefaultAction()
        elif keyCode == wx.WXK_RIGHT or keyCode == wx.WXK_NUMPAD_RIGHT:
            self.DefaultAction()
        elif keyCode == wx.WXK_ESCAPE:
            self.destroyMenu()
        elif keyCode == wx.WXK_UP or keyCode == wx.WXK_NUMPAD_UP:
            self.menuGridCtrl.MoveCursor(-1)
        elif keyCode == wx.WXK_DOWN or keyCode == wx.WXK_NUMPAD_DOWN:
            self.menuGridCtrl.MoveCursor(1)
        elif keyCode == wx.WXK_LEFT or keyCode == wx.WXK_NUMPAD_LEFT:
            self.destroyMenu()
        else:
            event.Skip()


    def onDoubleClick(self, event):
        self.DefaultAction()
        event.Skip()


    def onClose(self, event):
        self.Show(False)
        self.Destroy()
        self.plugin.menuDlg = None


    def destroyMenu(self, event = None):
        for evt in self.evtList[0]:
            eg.Unbind(evt, self.onUp)
        for evt in self.evtList[1]:
            eg.Unbind(evt, self.onDown)
        for evt in self.evtList[2]:
            eg.Unbind(evt, self.onRight)
        for evt in self.evtList[3]:
            eg.Unbind(evt, self.onEscape)
        if self.flag:
            self.timer.Cancel()
        self.plugin.TriggerEvent("OnScreenMenu.%s" % self.plugin.text.closed)
        self.Close()
#===============================================================================

class MyFileBrowseButton(eg.FileBrowseButton):
    def GetTextCtrl(self):          #  now I can make build-in textCtrl non-editable !!!
        return self.textControl     #
#===============================================================================

class MyTextDropTarget(EventDropTarget):

    def __init__(self, object):
        EventDropTarget.__init__(self, object)
        self.object = object


    def OnDragOver(self, x, y, dragResult):
        return wx.DragMove


    def OnData(self, dummyX, dummyY, dragResult):
        if self.GetData() and self.customData.GetDataSize() > 0:
            txt = self.customData.GetData()
            ix, evtList = self.object.GetEvtList()
            flag = True
            for lst in evtList:
                if txt in lst:
                    flag = False
                    break
            if flag:
                self.object.InsertImageStringItem(len(evtList[ix]), txt, 0)
                self.object.UpdateEvtList(ix, txt)
            else:
                PlaySound('SystemExclamation', SND_ASYNC)


    def OnLeave(self):
        pass
#===============================================================================

class EventListCtrl(wx.ListCtrl):

    def __init__(self, parent, id, evtList, ix, action):
        width = 205
        wx.ListCtrl.__init__(self, parent, id, style=wx.LC_REPORT | 
            wx.LC_NO_HEADER | wx.LC_SINGLE_SEL, size = (width, -1))
        self.parent = parent
        self.id = id
        self.evtList = evtList
        self.ix = ix
        self.action = action
        self.sel = -1
        self.il = wx.ImageList(16, 16)
        self.il.Add(wx.BitmapFromImage(wx.Image(join(eg.imagesDir, "event.png"), wx.BITMAP_TYPE_PNG)))
        self.SetImageList(self.il, wx.IMAGE_LIST_SMALL)
        self.InsertColumn(0, '')
        self.SetColumnWidth(0, width - 5 - SYS_VSCROLL_X)
        self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnSelect)
        self.Bind(wx.EVT_SET_FOCUS, self.OnChange) 
        self.Bind(wx.EVT_LIST_INSERT_ITEM, self.OnChange) 
        self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnChange) 
        self.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.OnRightClick)
        self.SetToolTipString(self.action.text.toolTip)


    def OnSelect(self, event):
        self.sel = event.GetIndex()
        evt = eg.ValueChangedEvent(self.id, value = self)
        wx.PostEvent(self, evt)
        event.Skip()


    def OnChange(self, event):
        evt = eg.ValueChangedEvent(self.id, value = self)
        wx.PostEvent(self, evt)
        event.Skip()


    def OnRightClick(self, event):
        if not hasattr(self, "popupID1"):
            self.popupID1 = wx.NewId()
            self.popupID2 = wx.NewId()
            self.Bind(wx.EVT_MENU, self.OnDeleteButton, id=self.popupID1)
            self.Bind(wx.EVT_MENU, self.OnDeleteAllButton, id=self.popupID2)
        # make a menu
        menu = wx.Menu()
        # add some items
        menu.Append(self.popupID1, self.action.text.popup[0])
        menu.Append(self.popupID2, self.action.text.popup[1])
        # Popup the menu.  If an item is selected then its handler
        # will be called before PopupMenu returns.
        self.PopupMenu(menu)
        menu.Destroy()


    def OnDeleteButton(self, event=None):
        self.DeleteItem(self.sel)
        self.evtList[self.ix].pop(self.sel)
        evt = eg.ValueChangedEvent(self.id, value = self)
        wx.PostEvent(self, evt)       
        if event:
            event.Skip()
        

    def OnDeleteAllButton(self, event=None):
        self.DeleteAllItems()
        evt = eg.ValueChangedEvent(self.id, value = self)
        wx.PostEvent(self, evt)
        self.evtList[self.ix] = []
        if event:
            event.Skip()


    def GetEvtList(self):
        return self.ix, self.evtList


    def UpdateEvtList(self, ix, txt):
        self.evtList[ix].append(txt)


    def SetItems(self, evtList):
        for i in range(len(evtList)):
            self.InsertImageStringItem(i, evtList[i], 0) 
#===============================================================================

class MenuEventsDialog(wx.MiniFrame):

    def __init__(self, parent, action):
        wx.MiniFrame.__init__(
            self,
            parent,
            -1,
            style=wx.CAPTION,
            name="Menu events dialog"
        )
        self.panel = parent
        self.action = action
        self.evtList = cpy(self.panel.evtList)
        self.SetBackgroundColour(wx.NullColour)
        self.ctrl = None
        self.sel = -1


    def ShowMenuEventsDialog(self, title, labels):
        self.panel.Enable(False)
        self.panel.dialog.buttonRow.cancelButton.Enable(False)
        self.panel.EnableButtons(False)
        self.SetTitle(title)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.SetMinSize((450, 308))
        topSizer=wx.GridBagSizer(2, 20)
        textLbl_0=wx.StaticText(self, -1, labels[0])        
        id = wx.NewId()
        eventsCtrl_0 = EventListCtrl(self, id, self.evtList, 0, self.action)
        eventsCtrl_0.SetItems(self.evtList[0])
        dt0 = MyTextDropTarget(eventsCtrl_0)
        eventsCtrl_0.SetDropTarget(dt0)
        textLbl_1=wx.StaticText(self, -1, labels[1])       
        id = wx.NewId()        
        eventsCtrl_1 = EventListCtrl(self, id, self.evtList, 1, self.action)
        eventsCtrl_1.SetItems(self.evtList[1])
        dt1 = MyTextDropTarget(eventsCtrl_1)
        eventsCtrl_1.SetDropTarget(dt1)
        textLbl_2=wx.StaticText(self, -1, labels[2])        
        id = wx.NewId()
        eventsCtrl_2 = EventListCtrl(self, id, self.evtList, 2, self.action)
        eventsCtrl_2.SetItems(self.evtList[2])
        dt2 = MyTextDropTarget(eventsCtrl_2)
        eventsCtrl_2.SetDropTarget(dt2)
        textLbl_3=wx.StaticText(self, -1, labels[3])        
        id = wx.NewId()        
        eventsCtrl_3 = EventListCtrl(self, id, self.evtList, 3, self.action)
        eventsCtrl_3.SetItems(self.evtList[3])
        dt3 = MyTextDropTarget(eventsCtrl_3)
        eventsCtrl_3.SetDropTarget(dt3)
        deleteSizer = wx.BoxSizer(wx.VERTICAL)
        delOneBtn = wx.Button(self, -1, self.action.text.popup[0])
        delBoxBtn = wx.Button(self, -1, self.action.text.popup[1])
        clearBtn  = wx.Button(self, -1, self.action.text.clear)
        deleteSizer.Add(delOneBtn, 1, wx.EXPAND)
        deleteSizer.Add(delBoxBtn, 1, wx.EXPAND|wx.TOP,5)
        deleteSizer.Add(clearBtn, 1, wx.EXPAND|wx.TOP,5)
        
        topSizer.Add(textLbl_0, (0,0))
        topSizer.Add(eventsCtrl_0, (1,0), flag = wx.EXPAND)
        topSizer.Add(textLbl_1, (0,1))
        topSizer.Add(eventsCtrl_1, (1,1), flag = wx.EXPAND)
        topSizer.Add(textLbl_2, (2,0),flag = wx.TOP, border = 8)
        topSizer.Add(eventsCtrl_2, (3,0), flag = wx.EXPAND)
        topSizer.Add(textLbl_3, (2,1), flag = wx.TOP, border = 8)
        topSizer.Add(eventsCtrl_3, (3,1), flag = wx.EXPAND)
        topSizer.Add(deleteSizer, (5,1), flag = wx.EXPAND)

        line = wx.StaticLine(self, -1, size=(20,-1),pos = (200,0), style=wx.LI_HORIZONTAL)
        btn1 = wx.Button(self, wx.ID_OK)
        btn1.SetLabel(self.action.text.ok)
        btn1.SetDefault()
        btn2 = wx.Button(self, wx.ID_CANCEL)
        btn2.SetLabel(self.action.text.cancel)
        btnsizer = wx.StdDialogButtonSizer()
        btnsizer.AddButton(btn1)
        btnsizer.AddButton(btn2)
        btnsizer.Realize()
        sizer.Add(topSizer,0,wx.ALL,10)
        sizer.Add(line, 0, wx.EXPAND|wx.ALIGN_CENTER|wx.TOP|wx.BOTTOM,5)
        sizer.Add(btnsizer, 0, wx.EXPAND|wx.RIGHT, 10)
        sizer.Add((1,6))
        self.SetSizer(sizer)
        sizer.Fit(self)


        def onFocus(evt):
            ctrl = evt.GetValue()
            if ctrl != self.ctrl:
                if self.ctrl:
                    self.ctrl.SetItemState(-1, wx.LIST_MASK_STATE, wx.LIST_STATE_SELECTED)
                self.ctrl = ctrl
            sel = self.ctrl.sel
            if sel != -1:
                self.sel = sel
            flag = self.ctrl.GetSelectedItemCount() > 0
            delOneBtn.Enable(flag)
            delBoxBtn.Enable(flag)
            evt.Skip()
        eventsCtrl_0.Bind(eg.EVT_VALUE_CHANGED, onFocus)        
        eventsCtrl_1.Bind(eg.EVT_VALUE_CHANGED, onFocus)        
        eventsCtrl_2.Bind(eg.EVT_VALUE_CHANGED, onFocus)        
        eventsCtrl_3.Bind(eg.EVT_VALUE_CHANGED, onFocus)     


        def onDelOneBtn(evt):
            self.ctrl.OnDeleteButton()
            delOneBtn.Enable(False)
            delBoxBtn.Enable(False)
            evt.Skip()
        delOneBtn.Bind(wx.EVT_BUTTON, onDelOneBtn)
        

        def onDelBoxBtn(evt):
            self.ctrl.OnDeleteAllButton()
            delOneBtn.Enable(False)
            delBoxBtn.Enable(False)
            evt.Skip()
        delBoxBtn.Bind(wx.EVT_BUTTON, onDelBoxBtn)


        def onClearBtn(evt):
            eventsCtrl_0.DeleteAllItems()
            eventsCtrl_1.DeleteAllItems()
            eventsCtrl_2.DeleteAllItems()
            eventsCtrl_3.DeleteAllItems()
            delOneBtn.Enable(False)
            delBoxBtn.Enable(False)
            self.evtList = [[], [], [], []]
            evt.Skip()
        clearBtn.Bind(wx.EVT_BUTTON, onClearBtn)


        def onClose(evt):
            self.panel.Enable(True)
            self.panel.dialog.buttonRow.cancelButton.Enable(True)
            self.panel.EnableButtons(True)
            self.GetParent().GetParent().Raise()
            self.Destroy()
        self.Bind(wx.EVT_CLOSE, onClose)
        

        def onCancel(evt):
            self.panel.Enable(True)
            self.panel.dialog.buttonRow.cancelButton.Enable(True)
            self.panel.EnableButtons(True)
            self.Close()
        btn2.Bind(wx.EVT_BUTTON,onCancel)
        

        def onOK(evt):
            self.panel.evtList = self.evtList
            self.Close()
        btn1.Bind(wx.EVT_BUTTON, onOK)
        
        sizer.Layout()
        self.Raise()
        self.Show()
#===============================================================================

class MenuGrid(gridlib.Grid):

    def __init__(self, parent, lngth, size):
        gridlib.Grid.__init__(self, parent, size = size)
        self.CreateGrid(lngth, 1)
        self.SetRowLabelSize(0)
        self.SetColLabelSize(0)
        self.SetScrollLineX(1)
        self.SetScrollLineY(1)
        self.EnableEditing(False)
        self.EnableDragColSize(False)
        self.EnableDragRowSize(False)
        self.EnableDragGridSize(False)
        self.EnableGridLines(False)
        self.SetColMinimalAcceptableWidth(8)
        attr = gridlib.GridCellAttr()
        attr.SetAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTRE)
        self.SetColAttr(0, attr)
        self.SetSelectionMode(gridlib.Grid.wxGridSelectRows)
        self.Bind(gridlib.EVT_GRID_CMD_SELECT_CELL, self.onGridSelectCell, self)
        self.oldSel = -1


    def SetSelection(self, row):
        self.SelectRow(row)
        self.oldSel = row


    def SetBackgroundColour(self, colour):
        self.SetDefaultCellBackgroundColour(colour)


    def SetForegroundColour(self, colour):
        self.SetDefaultCellTextColour(colour)


    def SetFont(self, font):
        self.SetDefaultCellFont(font)


    def GetSelection(self):
        return self.oldSel


    def Set(self, choices):
        oldLen = self.GetNumberRows()
        newLen = len(choices)
        h = self.GetDefaultRowSize()
        if oldLen > newLen:
            self.DeleteRows(0, oldLen - newLen, False)
        elif oldLen < newLen:
            self.AppendRows(newLen - oldLen, False)
        for i in range(len(choices)):
            self.SetCellValue(i, 0, " " + choices[i][0] + " ")
            self.SetRowSize(i, h)
        self.oldSel = 0 if newLen else -1


    def onGridSelectCell(self, event):
        row = event.GetRow()
        self.oldSel = row
        self.SelectRow(row)
        if not self.IsVisible(row, 0):
            self.MakeCellVisible(row, 0)
        event.Skip()


    def MoveCursor(self, step):
        max = self.GetNumberRows()
        sel = self.GetSelectedRows()[0]
        new = sel + step
        if new < 0:
            new += max
        elif new > max-1:
            new -= max
        self.SetGridCursor(new, 0)
        self.SelectRow(new)
#===============================================================================
      
class StartPresentation(eg.ActionClass):

    class text:
        pathLabel = 'Presentation file:'
        filemask = 'Presentations (*.pps, *.ppt, *.pptx, *.odp)|*.pps; *.ppt;\
            *.pptx; *.odp|All files (*.*)|*.*'
        toolTipFile = 'Click browse button to choose file'
        title = "Choose a file"

        
    def __call__(self, url = None, strt = 0, endless = False, pause = 0):
        if self.plugin.menuDlg is None and url is not None and exists(url):
            strt = strt if isinstance(strt, int) else self.plugin.str2int(strt)
            self.plugin.StartPresentation(url, strt, endless, pause)


    def Configure(self, url = None, start = 1, endless = False, pause = 0):
        panel = eg.ConfigPanel(self)
        sizer = wx.FlexGridSizer(2, 2, 20, 5)
        sizer.AddGrowableCol(1)
        label = wx.StaticText(
            panel,
            -1,
            self.text.pathLabel,
        )
        label2 = wx.StaticText(
            panel,
            -1,
            self.plugin.text.startLbl,
        )
        label3 = wx.StaticText(
            panel,
            -1,
            self.plugin.text.endless,
        )
        label3.SetToolTipString(self.plugin.text.endlessToolTip)
        label4 = wx.StaticText(
            panel,
            -1,
            self.plugin.text.pause,
        )
        label4.SetToolTipString(self.plugin.text.pauseToolTip)
        filepathCtrl = MyFileBrowseButton(
            panel, 
            initialValue = url if (url is not None) else '',
            startDirectory = eg.folderPath.Documents,
            fileMask = self.text.filemask,
            toolTip = self.text.toolTipFile,
            dialogTitle = self.text.title,
        )
        startCtrl = eg.SmartSpinIntCtrl(
            panel,
            -1,
            start,
            min = 1,
            max = 999
        )
        filepathCtrl.GetTextCtrl().SetEditable(False)
        endlessCtrl = wx.CheckBox(panel, -1, "")
        endlessCtrl.SetValue(endless)
        endlessCtrl.SetToolTipString(self.plugin.text.endlessToolTip)
        
        def onEndless(evt=None):
            flag = endlessCtrl.GetValue()
            label4.Enable(flag)
            pauseCtrl.Enable(flag)
            if evt:
                evt.Skip()
        endlessCtrl.Bind(wx.EVT_CHECKBOX, onEndless)

        pauseCtrl = eg.SmartSpinIntCtrl(
            panel,
            -1,
            pause,
            min = 0,
            max = 999
        )        
        pauseCtrl.SetToolTipString(self.plugin.text.pauseToolTip)
        onEndless()
        
        panel.sizer.Add(sizer, 1, wx.EXPAND|wx.ALL, 10)
        sizer.Add(label, 0, wx.TOP, 3)
        sizer.Add(filepathCtrl, 0, wx.EXPAND)
        sizer.Add(label2, 0, wx.TOP, 3)
        sizer.Add(startCtrl)
        sizer.Add(label3, 0, wx.TOP, 3)
        sizer.Add(endlessCtrl, 0, wx.EXPAND)
        sizer.Add(label4, 0, wx.TOP, 3)
        sizer.Add(pauseCtrl)
        
        while panel.Affirmed():
            panel.SetResult(
                filepathCtrl.GetValue(),
                startCtrl.GetValue(),
                endlessCtrl.GetValue(),
                pauseCtrl.GetValue(),
            )
#===============================================================================

class ShowMenu(eg.ActionClass):
    panel = None
    
    class text:
        filemask = "Presentations (*.pps, *.ppt, *.pptx, *.odp)|*.pps;*.ppt;\
            *.pptx; *.odp|All files (*.*)|*.*"
        toolTipFile = 'Click browse button to choose file'
        label = 'Menu label:'
        path = 'File:'
        menuPreview = 'On screen menu preview:'
        delete = 'Delete'
        insert = 'Insert new'
        menuFont = 'Menu font:'
        txtColour = 'Text colour'
        background = 'Background colour'
        txtColourSel = 'Selected text colour'
        backgroundSel = 'Selected background colour'
        inverted = "Use inverted colours"
        title = "Choose a file"
        OSELabel = 'Menu show on:'
        eventsBtn = "Events ..."
        dialog = "Playlist menu events assignement ..."
        btnToolTip ="""Press this button to assign events to control the menu !!!"""
        evtAssignTitle = "Menu control - events assignement"
        events = (
            "Cursor up:",
            "Cursor down:",
            "Select an item:",
            "Cancel (Escape):",
        )
        popup = (
            "Delete item",
            "Delete all items",
        )
        toolTip = "Drag-and-drop an event from the log into the box."
        cancel = 'Cancel'
        ok = 'OK'
        clear  = "Clear all"

    def Move(self, index, direction):
        tmpList = self.choices[:]
        max = len(self.choices)-1
        #Last to first position, other down
        if index == max and direction == 1:
            tmpList[1:] = self.choices[:-1]
            tmpList[0] = self.choices[max]
            index2 = 0
        #First to last position, other up
        elif index == 0 and direction == -1:
            tmpList[:-1] = self.choices[1:]
            tmpList[max] = self.choices[0]
            index2 = max
        else:
            index2 = index+direction
            tmpList[index] = self.choices[index2]
            tmpList[index2] = self.choices[index]
        self.choices = tmpList
        return index2

    def __call__(
        self,
        choices,
        fore,
        back,
        fontInfo,
        foreSel = (180, 180, 180),
        backSel = (75, 75, 75),
        inverted = True,
        monitor = 0,
        evtList = [[],[],[],[]],

    ):
        if not self.plugin.menuDlg:
            event = CreateEvent(None, 0, 0, None)  
            wx.CallAfter(
                Menu,
                fore,
                back,
                foreSel,
                backSel,
                fontInfo,
                False,
                self.plugin,
                event,
                monitor,
                choices,
                evtList
            )
            eg.actionThread.WaitOnEvent(event)


    def GetLabel(
        self,
        choices,
        fore,
        back,
        fontInfo,
        foreSel,
        backSel,
        inverted,
        monitor,
        evtList,
    ):
        res = self.text.showMenu+' '
        for n in range(0,min(3, len(choices))):
            res=res + choices[n][0]+', '
        res = res[:-2]
        if len(choices) > 3:
            res += ', ...'
        return res


    def Configure(
        self,
        choices = [],
        fore = (0, 0, 0),
        back = (255, 255, 255),
        fontInfo = None,
        foreSel = (180, 180, 180),
        backSel = (75, 75, 75),
        inverted = True,
        monitor = 0,
        evtList = [[],[],[],[]],
    ):
        self.choices = choices
        self.fore = fore
        self.back = back
        self.foreSel = foreSel
        self.backSel = backSel
        self.oldSel = -1
        global panel
        panel = eg.ConfigPanel(self)
        panel.evtList = cpy(evtList)
        w1 = panel.GetTextExtent(self.text.label)[0]
        w2 = panel.GetTextExtent(self.text.path)[0]
        w = max((w1,w2))
        mainSizer = wx.BoxSizer(wx.VERTICAL)
        topSizer = wx.BoxSizer(wx.HORIZONTAL)
        bottomSizer = wx.GridBagSizer(5, 5)
        bottomSizer.AddGrowableCol(2)
        topMiddleSizer = wx.BoxSizer(wx.VERTICAL)
        previewLbl = wx.StaticText(panel, -1, self.text.menuPreview)
        previewLblSizer = wx.BoxSizer(wx.HORIZONTAL)
        previewLblSizer.Add(previewLbl)
        mainSizer.Add(previewLblSizer)
        mainSizer.Add(topSizer, 0, wx.TOP, 5)
        mainSizer.Add(bottomSizer, 0, flag = wx.TOP|wx.EXPAND, border = 16)
        panel.sizer.Add(mainSizer)

        wl = w + 165
        listBoxCtrl = MenuGrid(
            panel,
            len(self.choices),
            size = wx.Size(wl, -1)
        )
        listBoxCtrl.SetColSize(0, wl - SYS_VSCROLL_X)

        listBoxCtrl.SetDefaultRowSize(22)        

        listBoxCtrl.SetBackgroundColour(self.back)
        listBoxCtrl.SetForegroundColour(self.fore)
        listBoxCtrl.SetSelectionBackground(self.backSel)
        listBoxCtrl.SetSelectionForeground(self.foreSel)        
        oldFont = previewLbl.GetFont()
        if fontInfo is None:
            font = listBoxCtrl.GetFont()
            font.SetPointSize(36)
            fontInfo = font.GetNativeFontInfoDesc()
        else:
            font = wx.FontFromNativeInfoString(fontInfo)        
        for n in range(10, 20):
            font.SetPointSize(n)
            previewLbl.SetFont(font)
            if previewLbl.GetTextExtent('X')[1] >= 28:
                font.SetPointSize(n - 4)
                previewLbl.SetFont(font)
                break
        listBoxCtrl.SetFont(font)
        previewLbl.SetFont(oldFont)


        label3 = wx.StaticText(
            panel,
            -1,
            self.plugin.text.endless,
        )
        label3.SetToolTipString(self.plugin.text.endlessToolTip)
        label4 = wx.StaticText(
            panel,
            -1,
            self.plugin.text.pause,
        )
        label4.SetToolTipString(self.plugin.text.pauseToolTip)

        endlessCtrl = wx.CheckBox(panel, -1, "")
        endlessCtrl.SetToolTipString(self.plugin.text.endlessToolTip)
        pauseCtrl = eg.SpinIntCtrl(
            panel,
            -1,
            0,
            min = 0,
            max = 999
        )        
        pauseCtrl.SetToolTipString(self.plugin.text.pauseToolTip)

        topSizer.Add(listBoxCtrl,0,wx.EXPAND)
        topSizer.Add((10,1))
        topSizer.Add(topMiddleSizer, 1, wx.EXPAND)
        topSizer.Add((20,1))
        labelLbl=wx.StaticText(panel, -1, self.text.label)
        labelCtrl=wx.TextCtrl(panel,-1,'',size=wx.Size(260,-1))
        labelCtrlSizer = wx.BoxSizer(wx.HORIZONTAL)
        labelCtrlSizer.Add(labelCtrl,0,wx.EXPAND)
        filepathLbl=wx.StaticText(panel, -1, self.text.path)
        filepathCtrl = MyFileBrowseButton(
            panel, 
            startDirectory=eg.folderPath.Documents,
            fileMask = self.text.filemask,
            toolTip=self.text.toolTipFile,
            dialogTitle = self.text.title,
        )
        label2 = wx.StaticText(
            panel,
            -1,
            self.plugin.text.startLbl,
        )
        startCtrl = eg.SpinIntCtrl(
            panel,
            -1,
            1,
            min = 1,
            max = 999
        )        
        filepathCtrl.GetTextCtrl().SetEditable(False)
        bottomSizer.Add(filepathLbl,(0,0),flag = wx.TOP, border = 3)
        bottomSizer.Add(filepathCtrl,(0,1),(1,2),wx.EXPAND)
        bottomSizer.Add(labelLbl,(1,0),flag=wx.TOP,border=3)
        bottomSizer.Add(labelCtrlSizer,(1,1))
        bottomSizer.Add(label2,(2,0),flag = wx.TOP, border = 3)
        bottomSizer.Add(startCtrl,(2,1))
        bottomSizer.Add(label3,(3,0))
        bottomSizer.Add(endlessCtrl,(3,1))
        bottomSizer.Add(label4,(4,0),flag = wx.TOP, border = 3)
        bottomSizer.Add(pauseCtrl,(4,1))
        #Button UP
        bmp = wx.ArtProvider.GetBitmap(wx.ART_GO_UP, wx.ART_OTHER, (16, 16))
        btnUP = wx.BitmapButton(panel, -1, bmp)
        btnUP.Enable(False)
        topMiddleSizer.Add(btnUP)
        #Button DOWN
        bmp = wx.ArtProvider.GetBitmap(wx.ART_GO_DOWN, wx.ART_OTHER, (16, 16))
        btnDOWN = wx.BitmapButton(panel, -1, bmp)
        btnDOWN.Enable(False)
        topMiddleSizer.Add(btnDOWN,0,wx.TOP,3)
        #Buttons 'Delete' and 'Insert new'
        w1 = panel.GetTextExtent(self.text.delete)[0]
        w2 = panel.GetTextExtent(self.text.insert)[0]
        if w1 > w2:
            btnDEL=wx.Button(panel,-1,self.text.delete)
            btnApp=wx.Button(panel,-1,self.text.insert,size=btnDEL.GetSize())
        else:
            btnApp=wx.Button(panel,-1,self.text.insert)
            btnDEL=wx.Button(panel,-1,self.text.delete,size=btnApp.GetSize())
        btnDEL.Enable(False)
        
        evtButton = wx.Button(panel, -1, self.text.eventsBtn)
        topMiddleSizer.Add(btnDEL,0,wx.TOP,5)
        topMiddleSizer.Add(btnApp,0,wx.TOP,5)
        topMiddleSizer.Add((-1, -1),1,wx.EXPAND)
        topMiddleSizer.Add(evtButton)
        #Font button
        fontLbl=wx.StaticText(panel, -1, self.text.menuFont)
        fontButton = eg.FontSelectButton(panel, value = fontInfo)
        #Button Text Colour
        foreLbl=wx.StaticText(panel, -1, self.text.txtColour + ':')
        foreColourButton = eg.ColourSelectButton(
            panel,
            fore,
            title = self.text.txtColour
        )
        #Button Background Colour
        backLbl=wx.StaticText(panel, -1, self.text.background+':')
        backColourButton = eg.ColourSelectButton(
            panel,
            back,
            title = self.text.background
        )
        foreSelLbl=wx.StaticText(panel, -1, self.text.txtColourSel+':')
        foreSelColourButton = eg.ColourSelectButton(panel,foreSel,title = self.text.txtColourSel)
        #Button Selected Background Colour
        backSelLbl=wx.StaticText(panel, -1, self.text.backgroundSel+':')
        backSelColourButton = eg.ColourSelectButton(panel,backSel,title = self.text.backgroundSel)
        useInvertedCtrl = wx.CheckBox(panel, -1, self.text.inverted)
        useInvertedCtrl.SetValue(inverted)
        OSElbl = wx.StaticText(panel, -1, self.text.OSELabel)
        displayChoice = eg.DisplayChoice(panel, monitor)
        topRightSizer = wx.GridBagSizer(2, 5)
        displaySizer = wx.BoxSizer(wx.HORIZONTAL)
        displaySizer.Add(OSElbl, 0,wx.TOP,3)
        displaySizer.Add((-1, -1), 1, wx.EXPAND)
        displaySizer.Add(displayChoice)
        topSizer.Add(topRightSizer)
        topRightSizer.Add(displaySizer, (0,0), (1,2),flag = wx.EXPAND)
        topRightSizer.Add(fontLbl, (1, 0), flag = wx.TOP, border = 5)
        topRightSizer.Add(fontButton, (1, 1), flag = wx.TOP, border = 2)
        topRightSizer.Add(foreLbl, (2, 0), flag = wx.TOP, border = 3)
        topRightSizer.Add(foreColourButton, (2, 1))
        topRightSizer.Add(backLbl, (3, 0), flag = wx.TOP, border = 3)
        topRightSizer.Add(backColourButton, (3, 1))
        topRightSizer.Add(foreSelLbl, (4, 0), flag = wx.TOP, border = 3)
        topRightSizer.Add(foreSelColourButton, (4, 1))
        topRightSizer.Add(backSelLbl, (5, 0), flag = wx.TOP, border = 3)
        topRightSizer.Add(backSelColourButton, (5, 1))
        topRightSizer.Add(useInvertedCtrl,(6,0),(1,2),flag = wx.TOP,border = 3)

        def OnEvtBtn(evt):
            dlg = MenuEventsDialog(
                parent = panel,
                action = self,
            )
            dlg.Centre()
            wx.CallAfter(
                dlg.ShowMenuEventsDialog,
                self.text.evtAssignTitle,
                self.text.events
            )
            evt.Skip()
        evtButton.Bind(wx.EVT_BUTTON, OnEvtBtn)      

        def OnMonitor(evt):
            listBoxCtrl.SetFocus()
            evt.Skip
        displayChoice.Bind(wx.EVT_CHOICE, OnMonitor)


        def OnInverted(evt = None):
            flag = useInvertedCtrl.IsChecked()
            foreSelLbl.Enable(not flag)
            foreSelColourButton.Enable(not flag)
            backSelLbl.Enable(not flag)
            backSelColourButton.Enable(not flag)
            self.inverted = flag
            if flag:
                self.foreSel = self.back
                self.backSel = self.fore
                backSelColourButton.SetValue(self.backSel)
                foreSelColourButton.SetValue(self.foreSel)
                listBoxCtrl.SetSelectionForeground(self.foreSel)
                listBoxCtrl.SetSelectionBackground(self.backSel)
            listBoxCtrl.SetFocus()
            if evt:
                evt.Skip
        useInvertedCtrl.Bind(wx.EVT_CHECKBOX, OnInverted)
        OnInverted()


        def onStart(evt):
            sel = listBoxCtrl.GetSelection()
            val = startCtrl.GetValue()
            self.choices[sel][2]=val
            evt.Skip()
        startCtrl.Bind(wx.EVT_TEXT, onStart)
        
        
        def onEndless(evt=None):
            if self.choices:
                sel = listBoxCtrl.GetSelection()
                val = endlessCtrl.GetValue()
                label4.Enable(val)
                pauseCtrl.Enable(val)
                if not val:
                    pauseCtrl.numCtrl.ChangeValue(0)
                    self.choices[sel][4]=0
                self.choices[sel][3]=val
            if evt:
                evt.Skip()
        endlessCtrl.Bind(wx.EVT_CHECKBOX, onEndless)

        def onPause(evt):
            sel = listBoxCtrl.GetSelection()
            val = pauseCtrl.GetValue()
            self.choices[sel][4]=val
            evt.Skip()
        pauseCtrl.Bind(wx.EVT_TEXT, onPause)
        

        def FillData(item):
            labelCtrl.ChangeValue(item[0])
            filepathCtrl.GetTextCtrl().ChangeValue(item[1])
            startCtrl.SetValue(item[2])
            endlessCtrl.SetValue(item[3])
            pauseCtrl.SetValue(item[4])
            onEndless()        

        def OnClickAfter():
            sel = listBoxCtrl.GetSelection()
            label = labelCtrl.GetValue()
            filepath = filepathCtrl.GetValue()
            if label.strip()<>"":
                if os.path.isfile(filepath):
                    if [item[0] for item in self.choices].count(label)==1:
                        if [item[1] for item in self.choices].count(filepath)==1:
                            self.oldSel=sel
                            item = self.choices[sel]
                            FillData(item)
            listBoxCtrl.SetSelection(self.oldSel)
            listBoxCtrl.SetFocus()


        def OnClick(evt):
            wx.CallAfter(OnClickAfter)
            evt.Skip()
        listBoxCtrl.Bind(gridlib.EVT_GRID_CMD_SELECT_CELL, OnClick)
        

        def OnColourBtn(evt):
            id = evt.GetId()
            value = evt.GetValue()
            if id == foreColourButton.GetId():
                listBoxCtrl.SetForegroundColour(value)
                if self.inverted:
                    self.backSel = self.fore
                    listBoxCtrl.SetSelectionBackground(value)
                    backSelColourButton.SetValue(value)
            elif id == backColourButton.GetId():
                listBoxCtrl.SetBackgroundColour(value)
                if self.inverted:
                    self.foreSel = self.back
                    listBoxCtrl.SetSelectionForeground(value)
                    foreSelColourButton.SetValue(value)
            elif id == foreSelColourButton.GetId():
                listBoxCtrl.SetSelectionForeground(value)
            elif id == backSelColourButton.GetId():
                listBoxCtrl.SetSelectionBackground(value)
            listBoxCtrl.Refresh()
            listBoxCtrl.SetFocus()
            evt.Skip()
        foreColourButton.Bind(eg.EVT_VALUE_CHANGED, OnColourBtn)
        backColourButton.Bind(eg.EVT_VALUE_CHANGED, OnColourBtn)
        foreSelColourButton.Bind(eg.EVT_VALUE_CHANGED, OnColourBtn)
        backSelColourButton.Bind(eg.EVT_VALUE_CHANGED, OnColourBtn)


        def OnFontBtn(evt):
            value = evt.GetValue()
            self.fontInfo = value
            oldFont = previewLbl.GetFont()
            font = wx.FontFromNativeInfoString(value)
            for n in range(10, 20):
                font.SetPointSize(n)
                previewLbl.SetFont(font)
                if previewLbl.GetTextExtent('X')[1] >= 28:
                    font.SetPointSize(n - 4)
                    previewLbl.SetFont(font)
                    break
            listBoxCtrl.SetFont(font)
            previewLbl.SetFont(oldFont)
            listBoxCtrl.SetFocus()
            if evt:
                evt.Skip()
        fontButton.Bind(eg.EVT_VALUE_CHANGED, OnFontBtn)


        def OnButtonUp(evt):
            newSel = self.Move(listBoxCtrl.GetSelection(), -1)
            listBoxCtrl.Set(self.choices)
            listBoxCtrl.SetSelection(newSel)
            self.oldSel = newSel
            listBoxCtrl.SetFocus()
            evt.Skip()
        btnUP.Bind(wx.EVT_BUTTON, OnButtonUp)


        def OnButtonDown(evt):
            newSel = self.Move(listBoxCtrl.GetSelection(), 1)
            listBoxCtrl.Set(self.choices)
            listBoxCtrl.SetSelection(newSel)
            self.oldSel = newSel
            listBoxCtrl.SetFocus()
            evt.Skip()
        btnDOWN.Bind(wx.EVT_BUTTON, OnButtonDown)


        def EnableCtrls(flag):
            labelCtrl.Enable(flag)
            labelLbl.Enable(flag)
            filepathCtrl.Enable(flag)
            filepathLbl.Enable(flag)
            startCtrl.Enable(flag)
            label2.Enable(flag)
            endlessCtrl.Enable(flag)
            label3.Enable(flag)
            pauseCtrl.Enable(flag)
            label4.Enable(flag)        


        def OnButtonDelete(evt):
            lngth = len(self.choices)
            if lngth == 2:
                btnUP.Enable(False)
                btnDOWN.Enable(False)
            sel = oldSel = listBoxCtrl.GetSelection()
            if lngth == 1:
                self.choices = []
                labelCtrl.ChangeValue('')
                filepathCtrl.GetTextCtrl().ChangeValue('')
                EnableCtrls(False)
                panel.EnableButtons(False)
                btnDEL.Enable(False)
                btnApp.Enable(True)
                listBoxCtrl.Set(self.choices)
                listBoxCtrl.SetFocus()
                self.oldSel = -1
                evt.Skip()
                return
            self.choices.pop(oldSel)
            if sel == lngth - 1:
                sel = 0
                self.oldSel = sel
            listBoxCtrl.Set(self.choices)
            listBoxCtrl.SetSelection(sel)
            item = self.choices[sel]
            labelCtrl.ChangeValue(item[0])
            filepathCtrl.GetTextCtrl().ChangeValue(item[1])
            startCtrl.SetValue(item[2])
            endlessCtrl.SetValue(item[3])
            pauseCtrl.SetValue(item[4])
            listBoxCtrl.SetFocus()
            onEndless()
            evt.Skip()
        btnDEL.Bind(wx.EVT_BUTTON, OnButtonDelete)

        def OnTextChange(evt):
            if self.choices <> []:
                flag = False
                sel = self.oldSel
                label = labelCtrl.GetValue()
                filepath = filepathCtrl.GetValue()
                self.choices[sel][0] = label
                self.choices[sel][1] = filepath
                listBoxCtrl.Set(self.choices)
                listBoxCtrl.SetSelection(sel)
                if label.strip()<>"":
                    if os.path.isfile(filepath):
                        if [item[0] for item in self.choices].count(label)==1:
                            if [item[1] for item in self.choices].count(filepath)==1:
                                flag = True
                panel.EnableButtons(flag)
                btnApp.Enable(flag)
            evt.Skip()
        labelCtrl.Bind(wx.EVT_TEXT, OnTextChange)
        filepathCtrl.Bind(wx.EVT_TEXT, OnTextChange)


        def OnButtonAppend(evt):
            if len(self.choices) == 1:
                btnUP.Enable(True)
                btnDOWN.Enable(True)
            EnableCtrls(True)
            sel = listBoxCtrl.GetSelection() + 1
            self.oldSel = sel
            item = ['', '', 1, False, 0]
            self.choices.insert(sel, item)
            listBoxCtrl.Set(self.choices)
            listBoxCtrl.SetSelection(sel)
            FillData(item)
            evt.Skip()
        btnApp.Bind(wx.EVT_BUTTON, OnButtonAppend)

        # re-assign the test button
        def OnButton(event):
            self.plugin.choices = self.choices

            if not self.plugin.menuDlg:
                wx.CallAfter(
                    Menu,
                    foreColourButton.GetValue(),
                    backColourButton.GetValue(),
                    foreSelColourButton.GetValue(),
                    backSelColourButton.GetValue(),
                    fontButton.GetValue(),#self.fontInfo, 
                    True,
                    self.plugin,
                    CreateEvent(None, 0, 0, None),
                    displayChoice.GetSelection(),
                    self.choices,
                    panel.evtList,
                )     
        panel.dialog.buttonRow.testButton.Bind(wx.EVT_BUTTON, OnButton)
        
        if len(self.choices) > 0:
            listBoxCtrl.Set(self.choices)
            listBoxCtrl.SetSelection(0)
            item = self.choices[0]
            labelCtrl.ChangeValue(item[0])
            filepathCtrl.GetTextCtrl().ChangeValue(item[1])
            startCtrl.SetValue(item[2])
            endlessCtrl.SetValue(item[3])
            pauseCtrl.SetValue(item[4])
            self.oldSel=0
            btnUP.Enable(True)
            btnDOWN.Enable(True)
            btnDEL.Enable(True)
        else:
            EnableCtrls(False)
            panel.EnableButtons(False)
        onEndless()
        panel.sizer.Layout()

        while panel.Affirmed():
            panel.SetResult(
            self.choices,
            foreColourButton.GetValue(),
            backColourButton.GetValue(),
            fontButton.GetValue(), 
            foreSelColourButton.GetValue(),
            backSelColourButton.GetValue(),
            useInvertedCtrl.GetValue(),
            displayChoice.GetSelection(),
            panel.evtList,
        )        
#======================================================================

#Group GoTo slide ...:
class DigitAction(eg.ActionClass):

    def __call__(self):
        cntrllr = self.plugin.controller
        if cntrllr and cntrllr.isRunning():
            self.plugin.number += self.value
            self.plugin.ShowNumLabel()
            eg.event.skipEvent = True
#======================================================================
          
class Enter(eg.ActionClass):

    def __call__(self):
        cntrllr = self.plugin.controller
        if cntrllr and cntrllr.isRunning():
            if (self.plugin.numDialog is not None) and (len(self.plugin.number)>0):
                last = cntrllr.getSlideCount()
                dest = min(last, self.plugin.str2int(self.plugin.number))
                self.plugin.numDialog.CloseDialog()
                cntrllr.gotoSlideIndex(dest - 1)
                self.plugin.number = ''
                eg.event.skipEvent = True
#======================================================================

class Backspace(eg.ActionClass):

    def __call__(self):
        if (self.plugin.numDialog is not None) and (len(self.plugin.number)>0):
                self.plugin.number = self.plugin.number[:-1]
                if len(self.plugin.number) == 0:
                    self.plugin.numDialog.CloseDialog()
                else:
                    self.plugin.ShowNumLabel()
                eg.event.skipEvent = True
#======================================================================

class Cancel(eg.ActionClass):

    def __call__(self):
        if self.plugin.numDialog is not None:
            self.plugin.number = ''
            self.plugin.numDialog.CloseDialog()
            eg.event.skipEvent = True
#======================================================================

class ComAction(eg.ActionClass):                    

    def __call__(self):
        cntrllr = self.plugin.controller
        if cntrllr and cntrllr.isRunning():
            return getattr(cntrllr, self.value)()                
#======================================================================

class blankScreen(eg.ActionClass):                    

    class text:
        color = 'Choose a blank screen color'

    def __call__(self, color = (0, 0, 0)):
        cntrllr = self.plugin.controller
        if cntrllr and cntrllr.isRunning():
            color = color[2] + 256 * color[1] + 65536 * color[0]
            if self.plugin.numDialog is None and self.plugin.menuDlg is None:
                cntrllr.resume()
                return cntrllr.blankScreen(color)


    def Configure(self, color = (0, 0, 0)):
        panel = eg.ConfigPanel()    
        colorLbl = wx.StaticText(panel, -1, self.text.color + ':')
        colorButton = eg.ColourSelectButton(panel, color, title=self.text.color)
        sizer = wx.BoxSizer(wx.HORIZONTAL)
        sizer.Add(colorLbl,0,wx.TOP,3)
        sizer.Add(colorButton,0,wx.LEFT,5)
        panel.sizer.Add(sizer,0,wx.ALL,20)
        while panel.Affirmed():
            panel.SetResult(
                colorButton.GetValue(),
            )
                
#======================================================================

class Exit(eg.ActionClass):                    

    def __call__(self):
        cntrllr = self.plugin.controller
        if cntrllr and cntrllr.isRunning():
            self.plugin.StopPresentation()

                
#===============================================================================

ACTIONS = (
    ( eg.ActionGroup, 'GotoSlide', 'GoTo slide actions', 'GoTo slide actions', (
        (DigitAction, 'Digit0', 'Digit 0', 'Type Digit 0', '0' ),
        (DigitAction, 'Digit1', 'Digit 1', 'Type Digit 1', '1' ),
        (DigitAction, 'Digit2', 'Digit 2', 'Type Digit 2', '2' ),
        (DigitAction, 'Digit3', 'Digit 3', 'Type Digit 3', '3' ),
        (DigitAction, 'Digit4', 'Digit 4', 'Type Digit 4', '4' ),
        (DigitAction, 'Digit5', 'Digit 5', 'Type Digit 5', '5' ),
        (DigitAction, 'Digit6', 'Digit 6', 'Type Digit 6', '6' ),
        (DigitAction, 'Digit7', 'Digit 7', 'Type Digit 7', '7' ),
        (DigitAction, 'Digit8', 'Digit 8', 'Type Digit 8', '8' ),
        (DigitAction, 'Digit9', 'Digit 9', 'Type Digit 9', '9' ),
        (Enter, 'Enter', 'Enter', 'Enter - Goto slide.', None),
        (Backspace, 'Backspace', 'Backspace', 'Backspace (delete last digit).', None),
        (Cancel, 'Cancel', 'Cancel', 'Cancel - Do not goto a slide.', None),
    )),
    ( eg.ActionGroup, 'Other', 'Other actions', 'Other actions', (
        (StartPresentation, 'StartPresentation', 'Start presentation', 'Start presentation.', None),
        (Exit, 'Exit', 'End presentation', 'End presentation.', None ),
        (ComAction, 'stopSound', 'Stop all sounds', 'Stop all currently played sounds.', u'stopSound' ),
        (blankScreen, 'Blank', 'Blank screen', 'Pauses the slide show and blanks the screen in the given color.', None ),
        (ComAction, 'getCurrentSlideIndex', 'Get current slide index', 'Returns the index of the current slide.', u'getCurrentSlideIndex'),
        (ShowMenu, 'ShowMenu', 'Show menu', 'Show menu.', None), 
        (ComAction, 'Pause', 'Pause presentation', 'Pause presentation.', u'pause' ),
        (ComAction, 'Resume', 'Resume presentation', 'Resume presentation.', u'resume' ),
        (ComAction, 'Next', 'Play next effect/slide', 'Play next effect (if any, else go to next slide).', u'gotoNextEffect' ),
        (ComAction,
            'Previous', 'Play previous effect/slide',
            'Play previous effect again. If no previous effect exists on this slide, show previous slide.',
            u'gotoPreviousEffect'
        ),
        (ComAction, 'NextWithout', 'Next slide without effects', 'Go to next slide without playing effects.', u'gotoNextSlide' ),
        (ComAction, 'PreviousWithout', 'Previous slide without effects', 'Go to the previous slide without playing effects.', u'gotoPreviousSlide' ),
        (ComAction, 'First', 'First slide', 'Jump to first slide in the slide show.', u'gotoFirstSlide' ),
        (ComAction, 'Last', 'Last slide', 'Jump to the last slide in the slide show.', u'gotoLastSlide' ),
        (ComAction, 'GetSlide', 'GetSlide', 'GetSlide.', u'getCurrentSlide' ),
        (ComAction, 'getNextSlideIndex', 'getNextSlideIndex', 'getNextSlideIndex.', u'getNextSlideIndex' ),
    )),
)
#====================================================================
       

class Impress(eg.PluginClass):
        
    menuDlg = None
    numDialog = None
    number = ''
    choices = []
    hwnd = 0
    text = Text

    def __init__(self):
        self.AddActionsFromList(ACTIONS)


    def __start__(self):
        eg.Bind("Impress.Menu.DefaultAction", self.OnGui)
        self.controller = None
        self.presentation = None


    def __stop__(self):
        eg.Unbind("Impress.Menu.DefaultAction", self.OnGui)
        self.StopPresentation()


    def str2int(self, s):
        s = eg.ParseString(s)
        try:
            s = int(s)
        except:
            s = 0
        return s


    def OnGui(self, event):
        self.StartPresentation(*event.payload)
        return True


    def ImprIsRunning(self, hwnd):
        self.sched = eg.scheduler.AddTask(2, self.ImprIsRunning, hwnd)
        if not IsWindow(hwnd):
            self.StopPresentation()


    def StopPresentation(self):
        if hasattr(self, 'sched') and self.sched:
            eg.scheduler.CancelTask(self.sched)
        if hasattr(self.presentation, "end"):
            self.presentation.end()
        if hasattr(self, 'hWnd') and IsWindow(self.hWnd):
            PostMessage(self.hWnd, WM_CLOSE, 0, 0)
        self.controller = None
        self.presentation = None


    def StartPresentation(self, url, start, endless, pause):
        if not exists(url):
            self.PrintError(self.text.err3 % url)
            return            
        objServiceManager = Dispatch('com.sun.star.ServiceManager')
        objServiceManager._FlagAsMethod("Bridge_GetStruct")

        def makePropertyValue(Name, Value): 
            """Create a com.sun.star.beans.PropertyValue struct and return it. 
            """ 
            oPropertyValue = objServiceManager.Bridge_GetStruct(
                "com.sun.star.beans.PropertyValue") 
            oPropertyValue.Name = Name
            oPropertyValue.Value = Value
            return oPropertyValue

        desktop = objServiceManager.CreateInstance('com.sun.star.frame.Desktop')
        try:
            doc = desktop.loadComponentfromURL(
                "private:factory/simpress",
                "_default", #"_blank",
                0,
                []    
            ) 
            win = doc.CurrentController.Frame.ContainerWindow
            hWnd = win.getWindowHandle([], 1) # 1 ~ for MS Windows env.
            SendMessageTimeout(hWnd, WM_SYSCOMMAND, SC_MINIMIZE, 0)
            doc = desktop.loadComponentfromURL("file:///"+url,'_default',0,[])
        except:
            self.PrintError(self.text.err1 % url)
            return
        presentation = doc.getPresentation()
        self.controller = presentation.getController()         
        val0 = makePropertyValue("IsFullScreen", True)
        val1 = makePropertyValue("IsAlwaysOnTop", True) 
        val2 = makePropertyValue("IsEndless", endless)   
        val3 = makePropertyValue("Pause", pause)  #Duration of black screen after last slide  [integer, seconds]  // only when presentation is Endless 
        presentation.startWithArguments([val0, val1, val2, val3])
        self.controller = presentation.getController()
        if start > 1:
            self.controller.gotoSlideIndex(start - 1)
        self.presentation = presentation
        hwnd = findPresentation()
        if len(hwnd) > 0:
            hwnd = hwnd[0]
            self.hWnd = hWnd
            self.ImprIsRunning(hwnd)      
            SetForegroundWindow(hwnd)
            SetActiveWindow(hwnd)
        else:
            self.PrintError(self.text.err2)


    def ShowNumLabel(self):
        if self.numDialog is not None:
            statText = self.numDialog.GetSizer().GetChildren()[0].GetWindow()
            statText.SetLabel(self.number)
        else:
            self.TriggerEvent("GoToFrame.%s" % self.text.opened)
            NumDialog(self)
            statText=wx.StaticText(
                self.numDialog,
                -1,
                self.number,
                style = wx.ALIGN_CENTRE | wx.ST_NO_AUTORESIZE
            )
            font = statText.GetFont()
            font.SetPointSize(100)
            font.SetWeight(wx.BOLD)
            statText.SetFont(font)
            self.numDialog.SetBackgroundColour(wx.Colour(0,255,255))
            statText.SetForegroundColour(wx.Colour(0,255,255))
            statText.SetBackgroundColour(wx.Colour(0, 0, 139))
            w,h = statText.GetTextExtent('8888')
            self.numDialog.SetSize((w+16,h+16))
            statText.SetPosition((7,7))
            statText.SetSize((w,h))
            mainSizer =wx.BoxSizer(wx.VERTICAL)
            self.numDialog.SetSizer(mainSizer)
            mainSizer.Add(statText, 0, wx.EXPAND)
            self.numDialog.Centre()
            self.numDialog.Show()        
#===============================================================================

