
'''
This software is licensed under the GPL (GNU General Public License) version 2
as it appears here: http://www.gnu.org/copyleft/gpl.html
It is also included with this archive as `gpl.txt <gpl.txt>`_.
'''


from collections import deque
import itertools
import os
import string
import threading
import time
import wx
import wx.stc
import wx.lib.mixins.listctrl as listmix
import __main__

import filtertable
import pubsub

attr1 = wx.ListItemAttr()
attr1.SetBackgroundColour(wx.Colour(0xee, 0xff, 0xee))

attr2 = wx.ListItemAttr()
attr2.SetBackgroundColour(wx.Colour(0xee, 0xee, 0xff))

OB1 = 0
class FileDropTarget(wx.FileDropTarget):
    def __init__(self, parent, root):
        wx.FileDropTarget.__init__(self)
        self.parent = parent
        self.root = root
    def OnDropFiles(self, x, y, filenames):
        if len(filenames) != 1:
            #append documents
            for filename in filenames:
                dn, fn = os.path.split(filename)
                filename = self.root.getAbsolute(fn, dn)
                unt = (filename[:10] == '<' and filename[-1:] == '>')
                if unt or self.root.isAbsOpen(filename):
                    #relocate to the end
                    i = self.root.getPositionAbsolute(filename, unt)
                    selected = self.root.control.GetSelection()
                    if i != -1:
                        p = self.root.control.GetPage(i)
                        t = self.root.control.GetPageText(i)
                        self.root.control.RemovePage(i)
                        self.root.control.AddPage(p, t, selected==i)
                else:
                    dn, fn = os.path.split(self.root.getAlmostAbsolute(fn, dn))
                    self.root.newTab(dn, fn)
            return

        selindex = self.root.control.GetSelection()

        i, _ = self.parent.HitTest((x,y))
        ## print "dropped at position", i, filenames[0]

        filename = filenames[0]
        unt = (filename[:1] == '<' and filename[-1:] == '>')
        if not unt:
            dn, fn = os.path.split(filename)
            filename = self.root.getAlmostAbsolute(fn, dn)
            dn, fn = os.path.split(filename)
        new = 0
        if not unt and not self.root.isOpen(fn, dn):
            new = 1
            cp = self.root.control.GetPageCount()
            self.root.newTab(dn, fn, switch=1)
            return
        elif unt:
            for cp, j in enumerate(self.root.control):
                if j.getshort() == filename:
                    break
        else:
            for cp, j in enumerate(self.root.control):
                if j.getlong() == filename:
                    break

        ## print "originated at position", cp

        if OB1:
            i -= 1
        if i == -1-OB1:
            i = self.root.control.GetPageCount()
        if i != cp:
            self.root.control.MoveTabPage(cp, i, selindex)

WHICHNB = 2

if WHICHNB == 0:
    BaseNotebook = wx.Notebook
    _style = wx.NB_TOP
    pch = wx.EVT_NOTEBOOK_PAGE_CHANGED
elif WHICHNB == 1:
    import wx.lib.flatnotebook as fnb
    BaseNotebook = fnb.FlatNotebook
    _style = fnb.FNB_NODRAG|fnb.FNB_DROPDOWN_TABS_LIST|fnb.FNB_NO_NAV_BUTTONS|fnb.FNB_NO_X_BUTTON|fnb.FNB_FF2
    pch = fnb.EVT_FLATNOTEBOOK_PAGE_CHANGED
elif WHICHNB == 2:
    from wx import aui
    BaseNotebook = aui.AuiNotebook
    _style = aui.AUI_NB_SCROLL_BUTTONS|aui.AUI_NB_TOP|aui.AUI_NB_CLOSE_ON_ACTIVE_TAB|aui.AUI_NB_TAB_MOVE
    pch = aui.EVT_AUINOTEBOOK_PAGE_CHANGED

class MyNB(BaseNotebook):
    def __init__(self, root, id, parent):
        BaseNotebook.__init__(self, parent, id, style=_style)
        self.root = root
        if WHICHNB != 2:
            if __main__.USE_DOC_ICONS:
                self.AssignImageList(__main__.IMGLIST2)
        else:
            self.imagelist = __main__.IMGLIST3

        #for some reason, the notebook needs the next line...the text control
        #doesn't.
        self.Bind(wx.EVT_KEY_DOWN, self.root.OnKeyPressed)
        self.Bind(pch, self.OnPageChanged)
        self.Bind(wx.EVT_SET_FOCUS, self.GotFocus)

        self.SetDropTarget(__main__.FileDropTarget(self.root))
        self.calling = 0
        self.other_focus = 0
        self.fr = None
        self.cs = 0
        self.Bind(aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.OnClose)
        self.Bind(aui.EVT_AUINOTEBOOK_BEGIN_DRAG, self.OnBeginDrag)
        self.Bind(aui.EVT_AUINOTEBOOK_DRAG_DONE, self.OnDragDone)
        pubsub.subscribe('document.selected', self.updateChecks)

    def OnClose(self, evt):
        evt.Veto()
        self.root.OnClose(None)

    def __iter__(self):
        count = self.GetPageCount()
        cur = 0
        while cur < count:
            r = self.GetPage(cur).GetWindow1()
            yield r
            cur += 1

    @property
    def items(self):
        return [os.path.join(i.dirname, i.filename) for i in self if i.dirname]

    def __contains__(self, name):
        dn, fn = os.path.split(name)
        return self.root.isOpen(fn, dn)

    def GotFocus(self, evt):
        evt.Skip()
        if self.GetPageCount():
            self.GetPage(self.GetSelection()).GetWindow1().SetFocus()

    def GNBI(self, i):
        if WHICHNB == 2:
            return self.imagelist[i]
        return i

    def GetCurrentPage(self):
        x = self.GetSelection()
        if x >= 0:
            return self.GetPage(x)
        return None

    def OnPageChanged(self, event):
        if event:
            event.Skip()

        if self.calling:
            return

        try:
            self.calling = 1

            old = event.GetOldSelection()
            new = event.GetSelection()
            if old > -1:
                owin = self.GetPage(old).GetWindow1()
                ## owin.docstate.Hide()
            if new > -1:
                self.root.dragger._SelectItem(new)
                win = self.GetPage(new).GetWindow1()
                #fix for dealing with current paths.

                if win.dirname:
                    try:
                        __main__.current_path = win.dirname
                    except:
                        traceback.print_exc()
                        pass
                pubsub.publish('document.selected', stc_id=id(win))
                #width = self.GetClientSize()[0]
                #split = win.parent
                #if win.GetWrapMode() == wx.STC_WRAP_NONE:
                #    self.parent.SetStatusText("", 1)
                #else:
                #    self.parent.SetStatusText("WRAP",1)
                ## self.root.OnDocumentChange(win, None)
                ## win.docstate.Show()

                _, flags = __main__.CARET_OPTION_TO_ID[__main__.caret_option]
                win.SetXCaretPolicy(flags, __main__.caret_slop*__main__.caret_multiplier)
                win.SetYCaretPolicy(flags, __main__.caret_slop)
                win.SetCaretWidth(__main__.CARET_WIDTH)
                if not win.loaded:
                    win.loadfile()

            self.root.timer.Start(10, wx.TIMER_ONE_SHOT)
            if self.other_focus:
                wx.CallAfter(win.SetFocus)
                self.other_focus = 0
            else:
                self.root.dragger.justdragged = time.time()
            self._seen()
            ## print "pagechanged", new, old
        finally:
            self.calling = 0
#
    def updateChecks(self, message):
        if isinstance(message, dict):
            for win in self:
                if id(win) == message.stc_id:
                    break
            else:
                # we can't find the proper window... skip the updating process
               return
        else:
            win = message
        #Clear for non-documents
        for i in __main__.ASSOC:
            self.root.menubar.Check(i[1], 0)
        for i in __main__.TB_RMAPPING.itervalues():
            self.root.menubar.Check(i[1], 0)
        for i in __main__.CARET_OPTION_TO_ID.itervalues():
            self.root.menubar.Check(i[0], 0)
        for i in __main__.TITLE_OPTION_TO_ID.itervalues():
            self.root.menubar.Check(i[0], 0)
        for i in __main__.DOCUMENT_LIST_OPTION_TO_ID.itervalues():
            self.root.menubar.Check(i, 0)
        if hasattr(self.root.docpanel, 'recentlyclosed'):
            for i in __main__.DOCUMENT_LIST_OPTION_TO_ID2.itervalues():
                self.root.menubar.Check(i, 0)
        for i in __main__.MACRO_CLICK_OPTIONS.iterkeys():
            self.root.menubar.Check(i, 0)
        for i in __main__.SYNTAX_CHECK_DELAY_ID_TO_NUM:
            self.root.menubar.Check(i, 0)
        for i in __main__.REFRESH_DELAY_ID_TO_NUM:
            self.root.menubar.Check(i, 0)


        #Check for non-documents
        self.root.menubar.Check(__main__.lexers3[__main__.DEFAULTLEXER], 1)
        self.root.menubar.Check(__main__.TB_RMAPPING[__main__.TOOLBAR][1], 1)
        self.root.menubar.Check(__main__.CARET_OPTION_TO_ID[__main__.caret_option][0], 1)
        self.root.menubar.Check(__main__.TITLE_OPTION_TO_ID[__main__.title_option][0], 1)
        self.root.menubar.Check(__main__.DOCUMENT_LIST_OPTION_TO_ID[__main__.document_options], 1)
        if hasattr(self.root.docpanel, 'recentlyclosed'):
            self.root.menubar.Check(__main__.DOCUMENT_LIST_OPTION_TO_ID2[__main__.document_options2], 1)
        self.root.menubar.Check(__main__.MACRO_CLICK_TO_ID[__main__.macro_doubleclick], 1)
        self.root.menubar.Check(__main__.SYNTAX_CHECK_DELAY_NUM_TO_ID[__main__.HOW_OFTEN1], 1)
        self.root.menubar.Check(__main__.REFRESH_DELAY_NUM_TO_ID[__main__.HOW_OFTEN2], 1)

        if not win:
            return

        win.SetCaretLineBack(__main__.COLOUR)

        if __main__.UNICODE:
            self.root.SetStatusText(win.enc, 2)

            for i in __main__.ENCODINGS.itervalues():
                self.root.menubar.Check(i, 0)
            self.root.menubar.Check(__main__.ENCODINGS[win.enc], 1)

        #clear out all of the marks
        for i in __main__.lexers2.itervalues():
            self.root.menubar.Check(i, 0)
        for i in __main__.LE_RMAPPING.itervalues():
            self.root.menubar.Check(i[1], 0)
        for i in __main__.LL_RMAPPING.itervalues():
            self.root.menubar.Check(i[1], 0)

        #set all of the marks
        self.root.menubar.Check(__main__.lexers2[win.lexer], 1)
        self.root.menubar.Check(__main__.LE_RMAPPING[win.GetEOLMode()][1], 1)
        self.root.menubar.Check(__main__.LL_RMAPPING[win.GetEdgeMode()][1], 1)

        for m,cid in ((0, __main__.NUM), (1, __main__.MARGIN)):
            self.root.menubar.Check(cid, bool(win.GetMarginWidth(m)))
        self.root.menubar.Check(__main__.INDENTGUIDE, win.GetIndentationGuides())
        self.root.menubar.Check(__main__.USETABS, win.GetUseTabs())
        self.root.menubar.Check(__main__.AUTO, win.showautocomp)
        self.root.menubar.Check(__main__.FETCH_M, win.fetch_methods)
        self.root.menubar.Enable(__main__.FETCH_M, win.showautocomp)
        self.root.menubar.Check(__main__.AC_KEYWORDS, win.use_keywords)
        self.root.menubar.Enable(__main__.AC_KEYWORDS, win.showautocomp)
        self.root.menubar.Enable(__main__.AC_LENGTH, win.showautocomp)
        self.root.menubar.Check(__main__.ANNOTATIONS_VISIBLE, bool(win.AnnotationGetVisible()))
        self.root.menubar.Check(__main__.WRAPL, win.GetWrapMode() != wx.stc.STC_WRAP_NONE)
        self.root.menubar.Check(__main__.SLOPPY, win.sloppy)
        self.root.menubar.Check(__main__.SMARTPASTE, win.smartpaste)
        self.root.menubar.Check(__main__.S_WHITE, win.GetViewWhiteSpace())
        self.root.menubar.Check(__main__.S_LEND, win.GetViewEOL())
        ## self.root.menubar.Check(SORTBY, win.tree.tree.SORTTREE)
        self.root.menubar.Check(__main__.SAVE_CURSOR, win.save_cursor)
        self.root.menubar.Check(__main__.HIGHLIGHT_LINE, win.GetCaretLineVisible())
        self.root.menubar.SetHelpString(__main__.IDR, "Indent region %i spaces"%win.GetIndent())
        self.root.menubar.SetHelpString(__main__.DDR, "Dedent region %i spaces"%win.GetIndent())
        self.root.macropage.update_button(win)

#----------------- This deals with the tab swapping support. -----------------
    def RemovePage(self, index):
        BaseNotebook.RemovePage(self, index)
        self.root.dragger._RemoveItem(index)
        self._seen()

    def DeletePage(self, index):
        self.root.dragger._RemoveItem(index)
        page = self.GetPage(index)
        stc = page.GetWindow1()
        stc.docstate.Hide()
        stc.docstate.Destroy()
        BaseNotebook.DeletePage(self, index)
        self._seen()

    def AddPage(self, page, text, switch=1):
        which = __main__.GDI(text)
        BaseNotebook.AddPage(self, page, text, switch, self.GNBI(which))
        self.root.dragger._AddItem(text)
        if switch or self.GetPageCount() == 1:
            self.root.OnDocumentChange(page.GetWindow1())
            ## self.root.dragger._SelectItem(max(self.GetPageCount()-1, 0))
            self._seen()
        ## if switch:
            ## wx.CallAfter(self.SetSelection, self.GetPageCount()-1)

    def InsertPage(self, posn, page, text, switch=1):
        which = __main__.GDI(text)
        BaseNotebook.InsertPage(self, posn, page, text, switch, self.GNBI(which))
        self.root.dragger._InsertItem(posn, text)
        if self.GetSelection() == posn or switch:
            self.root.OnDocumentChange(page.GetWindow1())
        self._seen()
        ## if switch:
            ## wx.CallAfter(self.SetSelection, posn)

    def MoveTabPage(self, cp, i, selindex=None):
        #remove from original location, insert at destination
        ## print "getting information for page", cp
        if selindex is None:
            selindex = cp
        p = self.GetPage(cp)
        t = self.GetPageText(cp)
        try:
            self.Freeze()
            self.root.dragger.Freeze()
            self.other_focus = 1
            self.root.dragger.justdragged = time.time()
            self.root.starting = 1
            ## print "removing page", cp
            self.RemovePage(cp)
            if i >= self.GetPageCount():
                self.AddPage(p, t)
                ## print "appending page", i, self.GetPageCount()
            else:
                ## print "inserting page at", i
                self.InsertPage(i, p, t)
            if cp == selindex:
                ## print "selecting page"
                page = min(i, self.GetPageCount()-1)
                _callme = lambda page: (self.SetSelection(page),
                                   self.GetPage(page).GetWindow1().SetFocus(),
                                   self.root.dragger._SelectItem(page)
                                   )

                wx.CallAfter(_callme, page)
        finally:
            self.root.starting = 0
            self.root.dragger.Thaw()
            self.Thaw()

    def OnBeginDrag(self, evt):
        self.fr = evt.OldSelection
        evt.Skip()

    def OnDragDone(self, evt):
        i = evt.Selection
        cp = self.fr
        self.fr = None
        if cp is None:
            return
        self.MoveTabPage(cp, i)
        evt.Skip()

    def SetPageText(self, posn, text):
        self.root.dragger._RenameItem(posn, text)
        BaseNotebook.SetPageText(self, posn, text)
        self._seen()

    def SetPageImage(self, which, img):
        if WHICHNB == 2:
            self.SetPageBitmap(which, self.GNBI(img))
        else:
            BaseNotebook.SetPageImage(self, which, img)

    def _seen(self):
        if not self.cs:
            self.cs = 1
            wx.CallAfter(self._seen_)

    def _seen_(self):
        self.cs = 0
        sel = self.GetSelection()
        if sel == -1:
            return
        shown = None
        for j,i in enumerate(self):
            if i.docstate.IsShown() and j != sel:
                i.docstate.Hide()
            elif j == sel and not i.docstate.IsShown():
                i.docstate.Show()
            if i.docstate.IsShown():
                shown = i
        if shown:
            self.root.OnDocumentChange(shown)
        self.root.dragger._Refresh()
        self.root.dragger._SelectItem(sel)

class setupmix:
    def setupcolumns(self, do=None):
        for i in xrange(self.GetColumnCount()-1, -1, -1):
            self.DeleteColumn(i)

        if do is None:
            if isinstance(self, MyLC):
                do = __main__.document_options
            else:
                do = __main__.document_options2
        if do&4:
            self.InsertColumn(0, "", width=20)
            self.InsertColumn(self.GetColumnCount(), "Path", format=wx.LIST_FORMAT_RIGHT)
        if do&1:
            self.InsertColumn(self.GetColumnCount(), "Filename")
        if (do&2) or do == 0:
            self.InsertColumn(self.GetColumnCount(), "Whole Path")
        if do == 0:
            self.InsertColumn(self.GetColumnCount(), "Filename")

        #0 -> path, filename
        #1 -> filename
        #2 -> path
        #3 -> filename, path
        self.resizeColumn(32)

        self.Bind(wx.EVT_SIZE, self._thisResize)

    def _thisResize(self, e):
        if e.GetSize()[1] < 32:
            return
        e.Skip()


class MyLC(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, setupmix):
    def __init__(self, parent, root):
        wx.ListCtrl.__init__(self, parent, style=wx.LC_REPORT|wx.LC_VIRTUAL|wx.LC_SINGLE_SEL)#|wx.BORDER_NONE
        listmix.ListCtrlAutoWidthMixin.__init__(self)
        ## self.setResizeColumn(1)
        self.root = root

        self.setupcolumns()

        self.SetDropTarget(FileDropTarget(self, root))

        if __main__.USE_DOC_ICONS:
            self.AssignImageList(__main__.IMGLIST1, wx.IMAGE_LIST_SMALL)

        self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.OnBeginDrag)
        self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnSelectionChanged)
        self.data = []
        self.justdragged = 0

    def OnGetItemText(self, index, col):
        if index >= self.root.control.GetPageCount():
            return ''
        do = __main__.document_options
        if do == 0:
            col = not col
        elif do == 2:
            col = 1

        item = self.root.control.GetPage(index).GetWindow1()
        if col == 0:
            return item.getshort()
        return item.getlong()

    def OnGetItemImage(self, item):
        if item >= self.root.control.GetPageCount():
            return -1
        return __main__.GDI(self.root.control.GetPage(item).GetWindow1().getshort())

    def OnSelectionChanged(self, event):
        index=event.GetIndex()
        if not self.justdragged or time.time()-self.justdragged > .25:
            l = lambda:(self.root.control.SetSelection(index),
                        self.root.control.GetPage(index).GetWindow1().SetFocus())
            wx.CallAfter(l)

    def OnBeginDrag(self, event):
        posn = event.GetIndex()
        if posn == -1:
            event.Skip()
            return

        a = self.root.control.GetPage(posn).GetWindow1()
        data = a.getlong()
        if not data:
            data = a.getshort()

        ## data = data.encode('ascii')
        d = wx.FileDataObject()
        d.AddFile(data)
        ## print d.GetFilenames()
        a = wx.DropSource(self)
        a.SetData(d)
        if a.DoDragDrop(wx.Drag_AllowMove|wx.Drag_CopyOnly):
            #we use a time so that users can (almost immediately) click on
            #another document to switch.
            self.justdragged = time.time()
            self.root.control.other_focus = 1

    def _special_method(self, *args):
        self.SetItemCount(self.root.control.GetPageCount())
        self.Refresh()

    _RemoveItem = _AddItem = _InsertItem = \
    _RenameItem = _Refresh = _special_method

    def _SelectItem(self, index):
        x = self.GetFirstSelected()
        while x != -1:
            self.Select(x, 0)
            x = self.GetNextSelected(x)
        wx.CallAfter(self.Select, index)

class RecentClosed(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, setupmix):
    def __init__(self, parent, root):
        wx.ListCtrl.__init__(self, parent, style=wx.LC_REPORT|wx.LC_VIRTUAL)#|wx.BORDER_NONE
        listmix.ListCtrlAutoWidthMixin.__init__(self)

        self.setupcolumns()
        self.root = root

        self.items = []

        self.SetDropTarget(__main__.FileDropTarget(root))
        self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnActivate)
        wx.CallAfter(self.refresh)

    def GetSelectedList(self):
        lst = []
        x = self.GetFirstSelected()
        while x != -1:
            lst.append(x)
            x = self.GetNextSelected(x)
        lst = [-i-1 for i in lst]
        return lst

    def OnActivate(self, evt):
        lst = self.GetSelectedList()
        for i in lst:
            self.Select(-(i+1), 0)
        if len(lst):
            self.Select(-(lst[0]+1), 1)

        self.root.OnDrop([self.items[i] for i in lst])

    def OnGetItemText(self, index, col):
        if index >= len(self.items):
            return ''

        do = __main__.document_options2
        if do == 0:
            col = not col
        elif do == 2:
            col = 1

        index = -index-1

        if col == 0:
            return os.path.split(self.items[index])[1]
        return self.items[index]

    def refresh(self):
        self.items = self.root.lastused.keys()
        self.SetItemCount(len(self.items))
        self.Refresh()

class FakeRecentClosed(object):
    def __init__(self, parent):
        self.parent = parent
        self.items = __main__.root.control.items + __main__.root.lastused.keys()[::-1]
    def refresh(self):
        self.items = __main__.root.control.items + __main__.root.lastused.keys()[::-1]
        self.parent.table.refresh(self.parent.table.last_text)

die = None
last_checked = set([None])
last_extns = set([None])
def get_filenames(paths, extns, call_on_complete, refresh, token, startthread=1):
    global die, last_checked, last_extns
    if startthread:
        t = threading.Thread(target=get_filenames, args=(paths, extns, call_on_complete, refresh, token, 0))
        t.setDaemon(1)
        t.start()
        return

    # let's first dedupe and remove paths that have another path as their
    # prefix
    ptw = paths[:]
    i = 0
    while i < len(ptw):
        p = ptw[i]
        j = i+1
        while j < len(ptw):
            if p.startswith(ptw[j]):
                del ptw[j]
            elif ptw[j].startswith(p):
                ptw[i] = ptw[j]
                del ptw[j]
                break
            else:
                j += 1
        else:
            i += 1

    if set(last_checked) == set(ptw) and set(last_extns) == set(extns) and not refresh:
        return
    last_checked = set(ptw)
    last_extns = extns = set(extns)

    known_files = set()
    last = 0
    def key(i):
        p, c = os.path.split(i.lower())
        return c,p
    for path in ptw:
        for parent, dirnames, files in os.walk(path):
            if die != token:
                return
            # we're foing to ignore paths and files with dotted prefixes
            dirnames[:] = [i for i in dirnames if i[:1] != '.']
            known_files.update(os.path.join(parent,fil) for fil in files if fil[:1] != '.' and (('.' not in fil) or (fil.split('.')[-1].lower() in extns)))
            if refresh and len(known_files) - last >= 1000:
                wx.CallAfter(call_on_complete, sorted(known_files, key=key), refresh)
                last = len(known_files)
        if die != token:
            return
    # need to add recent documents to this list :)
    known_files = sorted(known_files, key=key)
    wx.CallAfter(call_on_complete, known_files, refresh, 1)

def gen_path_diff(p1, p2):
    # this function assumes that p1 and p2 are the same path except for some
    # single middle section
    # think paths of the form:
    # /src/svn/python/trunk/Lib/...
    # /src/svn/python/branches/release26-maint/Lib/...
    # the first one will return 'trunk' the second 'branches/release26-maint'
    ## p1 = os.path.split(p1)[0]
    ## p2 = os.path.split(p2)[0]
    lead = 0
    for i, (pp1, pp2) in enumerate(itertools.izip(p1, p2)):
        if pp1 == pp2:
            lead = i+1
        else:
            break
    trail = 0
    for i, (pp1, pp2) in enumerate(itertools.izip(p1[::-1], p2[::-1])):
        if pp1 == pp2:
            trail = i+1
        else:
            break
    sep = os.path.sep
    if lead + trail > len(p1):
        return ''
    leadf = p1.rfind(os.path.sep, 0, lead)
    trailf = p1.find(os.path.sep, len(p1)-trail)
    lead = (leadf != -1 and [leadf+1] or [lead])[0]
    trail = (trailf != -1 and [len(p1)-trailf] or [trail])[0]
    join = []
    if lead:
        join.append('..')
    join.append(p1[lead:len(p1)-trail])
    if trail:
        join.append('..')
    return os.path.sep.join(join)

class _vitems:
    def __init__(self, *args):
        self.parts = args
    def __len__(self):
        return sum(len(i) for i in self.parts)
    def __getitem__(self, key):
        if key > len(self):
            raise IndexError
        for part in self.parts:
            if key < len(part):
                return part[key]
            key -= len(part)

def vitems(part1, part2):
    part1 = list(part1)
    if not part1:
        return part2
    return _vitems(part1, part2)

COMPLICATED_AND_SLOW = 0

class WatchProject(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, setupmix):
    def __init__(self, parent, root):
        wx.ListCtrl.__init__(self, parent, style=wx.LC_REPORT|wx.LC_VIRTUAL)
        listmix.ListCtrlAutoWidthMixin.__init__(self)
        self.AssignImageList(__main__.IMGLIST4, wx.IMAGE_LIST_SMALL)

        self.setupcolumns(5)
        self.root = root
        self.parent = parent

        self.items = []
        self.disp_items = []
        self.paths_to_watch = __main__.paths_to_watch
        self.extensions_to_watch = __main__.extensions_to_watch
        self.last_text = ''

        self.SetDropTarget(__main__.FileDropTarget(root))
        self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnActivate)
        wx.CallAfter(self.update_data, True)

    def add_path(self, path):
        self.paths_to_watch.append(path.rstrip(os.path.sep) + os.path.sep)
        self.update_data(True)

    def remove_paths(self, paths):
        paths = set(paths)
        self.paths_to_watch[:] = [p for p in self.paths_to_watch if p not in paths]
        self.update_data(True)

    def update_data(self, refresh=False):
        global die
        die = time.time()
        get_filenames(self.paths_to_watch, self.extensions_to_watch, self.got_new_data, refresh, die)

    def got_new_data(self, data, refresh=False, complete=False):
        self.items = data
        if refresh:
            self.refresh(self.last_text)
        if complete:
            __main__.root.SetStatusText("Finished adding %i files to Quick Browse."%len(data))
        else:
            print "Added %i files to Quick Browse"%len(data)

    def refresh(self, text):
        lcs = filtertable._lcsseq
        recent = self.parent.recentlyclosed.items
        self.last_text = text
        if not text:
            self.disp_items = vitems(recent, self.items)
        elif 0:
            items = itertools.chain(recent, self.items)
            text = os.path.sep.join(text.replace('\\', '/').replace('/', ' ').strip().split()).lower()
            self.disp_items = [k for k in (j for j in items if j) if lcs(k.lower(), text)]
        else:
            items = itertools.chain(recent, self.items)
            text = os.path.sep.join(text.replace('\\', '/').replace('/', ' ').strip().split()).lower()
            def key(i, x=text[::-1], score=filtertable._sseq_score):
                # we're going to prefer filename matches over path matches
                return score(i[::-1].lower(), x), i
            self.disp_items = sorted(set(k for k in (j for j in items if j) if lcs(k.lower(), text)), key=key)

        self.SetItemCount(len(self.disp_items))
        self.Refresh()

    def OnGetItemText(self, index, col):
        if col == 0:
            return ''
        col -= 1
        di = self.disp_items
        if index >= len(di):
            return ''

        cd, cf = os.path.split(di[index])
        if col == 1:
            return cf

        disp = ''
        if index > 0:
            # check previous path
            od, of = os.path.split(di[index-1])
            if cf == of:
                disp = gen_path_diff(cd, od)
        if (not disp) and (index+1) < len(di):
            # check next path
            od, of = os.path.split(di[index+1])
            if cf == of:
                disp = gen_path_diff(cd, od)
        disp = disp or cd
        return disp

    def GetSelectedList(self):
        lst = []
        x = self.GetFirstSelected()
        while x != -1:
            lst.append(x)
            x = self.GetNextSelected(x)
        return lst

    def OnActivate(self, evt):
        lst = self.GetSelectedList()
        di = self.disp_items
        self.root.OnDrop([di[i] for i in lst])

    def OnGetItemAttr(self, item):
        it = self.disp_items[item]
        if it in self.root.control:
            return attr2
        elif it in self.root.lastused:
            return attr1
        return None

    def OnGetItemImage(self, item):
        return __main__.GDI(os.path.split(self.disp_items[item])[1])

def Button(*args, **kwargs):
    help = kwargs.pop('help', None)
    b = wx.Button(*args, **kwargs)
    if help:
        b.SetHelpText(help)
    return b

class DocumentPanel(wx.Panel):
    def __init__(self, parent, root):
        wx.Panel.__init__(self, parent, -1)

        self.root = root
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.documentlist = MyLC(self, root)
        sizer.Add(self.documentlist, 1, wx.EXPAND|wx.ALL, 2)

        self.filter = wx.TextCtrl(self, -1, "", style=wx.WANTS_CHARS)
        self.Bind(wx.EVT_TEXT, self.OnText)
        sizer.Add(self.filter, 0, wx.EXPAND|wx.ALL, 2)

        sz3 = wx.BoxSizer(wx.HORIZONTAL)

        b = Button(self, -1, "+", help="Add paths to watch", style=wx.BU_EXACTFIT)
        sz3.Add(b, 0, wx.ALL, 2)
        self.Bind(wx.EVT_BUTTON, self.OnPlus, b)

        b = Button(self, -1, "-", help="Remove paths to watch", style=wx.BU_EXACTFIT)
        sz3.Add(b, 0, wx.ALL, 2)
        self.Bind(wx.EVT_BUTTON, self.OnMinus, b)

        b = Button(self, -1, "R", help="Refresh file list", style=wx.BU_EXACTFIT)
        sz3.Add(b, 0, wx.ALL, 2)
        self.Bind(wx.EVT_BUTTON, self.OnRefresh, b)

        sz3.Add(wx.StaticText(self, -1, ""), 1, wx.EXPAND|wx.ALL, 2)

        b = Button(self, -1, "X", help="Remove file from history", style=wx.BU_EXACTFIT)
        sz3.Add(b, 0, wx.ALL, 2)
        self.Bind(wx.EVT_BUTTON, self.OnRemoveHistory, b)

        sz3.Add(wx.StaticText(self, -1, ""), 1, wx.EXPAND|wx.ALL, 2)

        b = Button(self, -1, "E+", help="Add extensions to watch", style=wx.BU_EXACTFIT)
        sz3.Add(b, 0, wx.ALL, 2)
        self.Bind(wx.EVT_BUTTON, self.OnAddExt, b)

        b = Button(self, -1, "E-", help="Remove extensions to watch", style=wx.BU_EXACTFIT)
        sz3.Add(b, 0, wx.ALL, 2)
        self.Bind(wx.EVT_BUTTON, self.OnRemExt, b)

        sizer.Add(sz3, 0, wx.EXPAND|wx.ALL, 2)


        self.table = WatchProject(self, root)
        sizer.Add(self.table, 1, wx.EXPAND|wx.ALL, 2)

        self.recentlyclosed = FakeRecentClosed(self)
        if __main__.show_recent:

            s2 = wx.BoxSizer(wx.HORIZONTAL)

            s2.Add(wx.StaticText(self, -1, "Recently Open:"), 0,
                    wx.EXPAND|wx.ALIGN_CENTER_VERTICAL|wx.TOP|wx.BOTTOM, 6)

            btn = wx.Button(self, -1, "Remove")
            btn.SetToolTipString("Remove the selected recent document from the list")
            self.Bind(wx.EVT_BUTTON, self.OnButton, btn)

            s2.Add(wx.StaticText(self, -1, " "), 1, wx.EXPAND)

            s2.Add(btn)

            sizer.Add(s2, 0, wx.ALL|wx.EXPAND, 4)

            self.recentlyclosed = RecentClosed(self, root)
            sizer.Add(self.recentlyclosed, 1, wx.EXPAND|wx.ALL, 2)

        self.SetSizer(sizer)

    def OnText(self, evt):
        self.table.refresh(self.filter.GetValue())

    def OnPlus(self, evt):
        dlg = wx.DirDialog(self, "Choose a directory to add to the quick browse listing:", style=wx.DD_DEFAULT_STYLE, pos=(0,0))
        if dlg.ShowModal() == wx.ID_OK:
            self.table.add_path(dlg.GetPath())
        dlg.Destroy()

    def OnMinus(self, evt):
        dlg = wx.MultiChoiceDialog(self, "Select paths you would like to remove from the\nlist of paths you are currently monitoring.",
            "Remove Paths...", self.table.paths_to_watch, pos=(0,0))

        if (dlg.ShowModal() == wx.ID_OK):
            selections = dlg.GetSelections()
            strings = [self.table.paths_to_watch[x] for x in selections]
            self.table.remove_paths(strings)
        dlg.Destroy()

    def OnRefresh(self, evt):
        self.table.update_data(True)

    def OnAddExt(self, evt):
        dlg = wx.TextEntryDialog(
                self, 'Please add extensions you would like to watch',
                'Add extensions to watch', '', pos=(0,0))
        if dlg.ShowModal() == wx.ID_OK:
            self.table.extensions_to_watch[:] = list(
                set(dlg.GetValue().replace(';', ' ').replace('.', ' ').replace(',', ' ').split()) |
                set(self.table.extensions_to_watch))
            self.table.update_data(True)
        dlg.Destroy()

    def OnRemExt(self, evt):
        extensions = sorted(self.table.extensions_to_watch)
        dlg = wx.MultiChoiceDialog(self, "Select extensions you would like to remove from the\nlist of extensions you are currently monitoring.",
            "Remove Extensions...", extensions, pos=(0,0))
        if dlg.ShowModal() == wx.ID_OK:
            selections = dlg.GetSelections()
            self.table.extensions_to_watch[:] = [i for i in extensions if i not in set(extensions[x] for x in selections)]
            self.table.update_data(True)
        dlg.Destroy()

    def OnRemoveHistory(self, evt):
        for i in self.table.GetSelectedList():
            try:
                del self.root.lastused[self.table.disp_items[i]]
            except (KeyError, IndexError):
                pass
        self._refresh()

    def OnButton(self, evt):
        rc = self.recentlyclosed
        lst = rc.GetSelectedList()
        for i in lst:
            rc.Select(-(i+1), 0)
        if len(lst):
            rc.Select(-(lst[0]+1), 1)

        for i in lst:
            try:
                del self.root.lastused[rc.items[i]]
            except (KeyError, IndexError):
                pass
        self._refresh()

    def _refresh(self):
        if hasattr(self, 'recentlyclosed'):
            self.recentlyclosed.refresh()
        self.documentlist._Refresh()

    def _setcolumn(self):
        if hasattr(self, 'recentlyclosed'):
            self.recentlyclosed.setupcolumns()
        self.documentlist.setupcolumns()

def _MyLC(parent, root):
    dp = DocumentPanel(parent, root)
    dragger = dp.documentlist
    return dp, dragger

#
