/////////////////////////////////////////////////////////////////////////////
// Name:        proppanel_packaging.cpp
// Purpose:     wxPackagePropertiesPanel
//              (stuff for handling the "packaging" page)
// Author:      Francesco Montorsi
// Modified by:
// Created:     28/06/2006 19:22:47
// RCS-ID:      $Id: proppanel_packaging.cpp,v 1.18 2007/02/01 22:59:41 frm Exp $
// Copyright:   (c) 2006 Julian Smart, Francesco Montorsi
// Licence:     wxWidgets license
/////////////////////////////////////////////////////////////////////////////


// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
#pragma hdrstop
#endif

#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif

#include <wx/filepicker.h>
#include <wx/dir.h>

#include "guipkg/proppanel.h"
#include "guipkg/excludedlg.h"

#include "wxp/package.h"
#include "wxp/wxp.h"
#include "wxp/packagewxp.h"

#include "wx/miscutils.h"
#include "wx/explorertreectrl.h"


// ----------------------------------------------------------------------------
// wxPackagePropertiesPanel - packaging stuff - I/O
// ----------------------------------------------------------------------------

void wxPackagePropertiesPanel::InitPackagingPage()
{
    UpdateRelativeRoot();
    UpdateUncompressedSize();
}

void wxPackagePropertiesPanel::GetPackagingPackageInfo(wxPackageInfo &ret) const
{
    // PACKAGING info
    ret.SetCompressionMode(m_pZip->GetValue() ? wxPCM_ZIP : wxPCM_TAR_GZ);
    if (m_bRelativeRootValid)
    {
        // the relative root
        ret.SetRoot(m_pRelativeRoot->GetValue());
    }

    // the exclude list
    for (size_t i=0; i<wxPEM_MAX; i++)
    {
        wxPackageExcludeMode curr = (wxPackageExcludeMode)i;
        ret.SetExcluded(curr, m_pExcluded->Get(curr));
    }

    ret.SetFileName(m_pFileName->GetValue());
    ret.SetUploadDestination(m_pUpload->GetValue());
}

void wxPackagePropertiesPanel::DoSetPackagingPackageInfo(const wxPackageInfo &p,
                                                         const wxString &filename)
{
    // compression system
    switch (p.GetCompressionMode())
    {
    case wxPCM_ZIP:
    case wxPCM_TAR_GZ:
        m_pZip->SetValue(p.GetCompressionMode() == wxPCM_ZIP);
        m_pTar->SetValue(p.GetCompressionMode() == wxPCM_TAR_GZ);
        break;

    default:        // invalid compression mode. Use zip by default
        m_pZip->SetValue(true);
    }

    // output file name
    m_pFileName->SetValue(p.GetOutputFileName());

    // destination (if present)
    m_pUpload->SetValue(p.GetUploadDestination());


    // clear all our controls which need a valid filename/relative root:

    m_pProjectDir->SetPath(wxEmptyString);
    m_pWXPDir->SetPath(wxEmptyString);

    m_pRelativeRoot->SetValue(wxEmptyString);
    m_pIncludedTree->SetPath(wxEmptyString);
    m_pExcluded->Clear();

    // set the WXP path
    wxFileName fn(filename);
    m_pWXPDir->SetPath(fn.GetPath());

    // set the project dir too
    m_strRoot = p.GetProjectFolder(filename);   // UpdateRelativeRoot() uses m_strRoot!
    m_pProjectDir->SetPath(m_strRoot);

    // the relative root should now be automatically set to
    // the correct value which is already in the wxPackageInfo:
    UpdateRelativeRoot();
    if (!m_bRelativeRootValid)
        return;

    wxASSERT(m_pRelativeRoot->GetValue() == p.GetRelativeRoot());

    // we can set the include folder now
    SetIncludePath(m_strRoot);

    // add excluded things to our list
    m_pExcluded->Clear();
    for (size_t i=0; i<wxPEM_MAX; i++)
    {
        wxPackageExcludeMode curr = (wxPackageExcludeMode)i;
        m_pExcluded->Add(curr, p.GetExcluded(curr));
    }

    // and last, synchronize the tree with the list
    // (since the list contains the updated data while the tree
    //  has all items unchecked)
    m_pExcluded->SynchTree(m_pIncludedTree);

    UpdateUncompressedSize();
}

bool wxPackagePropertiesPanel::IsPackagingPageOk() const
{
    bool isok = true;

    // check relative root
    if (!m_bRelativeRootValid)
    {
        wxLogWarning(wxT("The root relative to the package's WXP is not correctly set"));
        isok = false;
    }

    // check output file name
    if (m_pFileName->GetValue().IsEmpty())
    {
        wxLogError(wxT("The output file name is not optional"));
        isok = false;
    }

    // check included stuff
    wxArrayString includedfiles = m_pIncludedTree->GetFilesChecked(),
                  includeddirs = m_pIncludedTree->GetDirsChecked();

    // does it makes sense to show the dialog?
    if (includedfiles.GetCount() == 0 &&
        includeddirs.GetCount() == 0)
    {
        wxLogError(wxT("No files are included in the package"));
        isok = false;
    }

    // check uncompressed size
#define MEGABYTE        (1024*1024)
    if (m_szUncompressed > 50*MEGABYTE)
    {
        wxLogWarning(wxT("The package uncompressed size is very big (%s)"),
                     wxFileName::GetHumanReadableSize(m_szUncompressed).c_str());
        isok = false;
    }

    return isok;
}


// ----------------------------------------------------------------------------
// wxPackagePropertiesPanel - packaging stuff
// ----------------------------------------------------------------------------

wxString wxPackagePropertiesPanel::GetProjectParent() const
{
    // get the parent of the project root
    wxFileName dir(m_pProjectDir->GetDirName());
    wxASSERT(dir.GetDirCount() > 0);
    dir.RemoveLastDir();

    // that's our prefix
    return dir.GetPath(wxPATH_GET_VOLUME);
}

void wxPackagePropertiesPanel::SetIncludePath(const wxString &path)
{
    // update the tree control
    m_pIncludedTree->SetPath(path);

    // this is an handy thing that the user will surely like:
    m_pIncludedTree->Expand(m_pIncludedTree->GetRootItem());
}

void wxPackagePropertiesPanel::GetNewExcludedStuff()
{
    wxArrayString includedfiles = m_pIncludedTree->GetFilesChecked(),
                  includeddirs = m_pIncludedTree->GetDirsChecked();

    // does it makes sense to show the dialog?
    if (includedfiles.GetCount() == 0 &&
        includeddirs.GetCount() == 0)
    {
        wxString str;
        str.Printf(wxT("All files of the '%s' directory are already excluded.\n")
                   wxT("There are no files or directories to exclude."),
                   m_strRoot.c_str());

        wxMessageBox(str, wxT("Warning"), wxOK, this);
        return;
    }

    wxPackageExcludeDlg dlg(this);
    wxString prefix = GetProjectParent();

    // add the list of all checked files & folders
    dlg.SetFileList(prefix, includedfiles);
    dlg.SetDirList(prefix, includeddirs);

    if (dlg.ShowModal() == wxID_OK)
    {
        wxArrayString arr;

        if (dlg.GetMode() == wxPEM_FILE)
            arr = dlg.GetSelectedFiles();
        else if (dlg.GetMode() == wxPEM_DIRECTORY)
            arr = dlg.GetSelectedDirs();
        else if (dlg.GetMode() == wxPEM_PATTERN)
        {
            // if we don't add to the list the exclusion pattern _before_
            // doing the SynchFromTree() call below, then the exclusion
            // list would be filled with all the files matched by the pattern!
            m_pExcluded->Add(wxPEM_PATTERN, dlg.GetPattern());

            arr = dlg.GetMatchingFiles();
        }

        for (size_t i=0; i < arr.GetCount(); i++)
        {
            // arr[i] is using wxPATH_NATIVE
            wxTreeItemId item = m_pIncludedTree->FindItem(arr[i], wxPATH_NATIVE);
            wxASSERT(item.IsOk());
            
            m_pIncludedTree->UnCheck(item);
        }

        m_pExcluded->SynchFromTree(m_pIncludedTree);
    }
}

void wxPackagePropertiesPanel::UpdateRelativeRoot()
{
    wxFileName absroot = wxFileName::DirName(m_strRoot);
    wxFileName wxpdir = wxFileName::DirName(m_pWXPDir->GetPath());

    bool valid = absroot.IsOk() && 
                 wxpdir.IsOk() &&
                 !absroot.GetPath().IsEmpty() &&
                 !wxpdir.GetPath().IsEmpty();

    // we cannot accept volumes as absolute root of a project
    // since we need to be able to reference the parent directory
    // of absroot!
    valid &= absroot.GetDirCount() > 0;

    bool exist = absroot.DirExists() && wxpdir.DirExists();
    bool parent = wxIsSubfolderOf(absroot, wxpdir);

    if (valid && exist && parent)
    {
        m_bRelativeRootValid = true;

        // we can set the relative root
        m_pRootCheckLabel->SetLabel(wxT("Valid!"));
        m_pRootBitmap->SetBitmap(GetBitmapResource(wxT("valid")));

        // if project dir is c:\myproject and the WXP directory is
        // c:\myproject\build we want to have as relative root path
        // "../.." and not just ".."
        wxFileName rootParent(absroot);
        rootParent.RemoveLastDir();

        bool success = rootParent.MakeRelativeTo(wxpdir.GetPath());
        wxASSERT(success);

        // wxPATH_UNIX is because we always want to have forward slashes!
        wxString str = rootParent.GetPath(0, wxPATH_UNIX);
#ifdef __WXDEBUG__
        // only the '.' and the '/' characters should be part of the relative root!
        for (size_t i=0; i<str.Len(); i++)
            wxASSERT(str[i] == wxT('.') || str[i] == wxT('/'));
#endif

        m_pRelativeRoot->SetValue(str.IsEmpty() ? wxT(".") : str);
    }
    else
    {
        m_bRelativeRootValid = false;
        m_pRootCheckLabel->SetLabel(wxT("Invalid"));
        m_pRootBitmap->SetBitmap(GetBitmapResource(wxT("invalid")));

        if (!valid)
        {
            m_pRelativeRoot->SetValue(wxT("Both paths must be non-empty"));
        }
        else if (!exist)
        {
            m_pRelativeRoot->SetValue(wxT("Both paths must exist"));
        }
        else if (!parent)
        {
            m_pRelativeRoot->SetValue(
                wxT("The WXP dir must be a subfolder of the project root"));
        }
    }
}

void wxPackagePropertiesPanel::UpdateUncompressedSize()
{
    wxArrayString arr;

    // helper class
    class wxPackageDirTraverser : public wxDirTraverser
    {
    public:
        wxPackageDirTraverser(const wxString &prefix,
                              const wxArrayString &filesToExclude,
                              const wxArrayString &dirsToExclude,
                              const wxArrayString &patternsToExclude)
        { 
            m_prefix = prefix;

            // we need to compare dirs/files/patterns using native path separator!
            ConvertToNativeSeparator(filesToExclude, m_arrFilesExcluded);
            ConvertToNativeSeparator(dirsToExclude, m_arrDirsExcluded);
            ConvertToNativeSeparator(patternsToExclude, m_arrPatternsExcluded);
        }

        void ConvertToNativeSeparator(const wxArrayString &source, wxArrayString &arr)
        {
            const wxString sep = wxString(wxFileName::GetPathSeparator(), 1);
            for (size_t i=0; i<source.GetCount(); i++)
            {
                wxString str = source[i];
                str.Replace(wxT("/"), sep);
                arr.Add(str);
            }
        }

        virtual wxDirTraverseResult OnFile(const wxString& filename)
        {
            wxASSERT(filename.StartsWith(m_prefix));
            wxString name = filename.Mid(m_prefix.Len());

            // remove the separator, if present
            if (name[0] == wxFileName::GetPathSeparator())
                name = name.Mid(1);

            wxULongLong sz = wxFileName::GetSize(filename);
            m_szTotal += sz;

            if (m_arrFilesExcluded.Index(name) != wxNOT_FOUND)
                return wxDIR_CONTINUE;      // skip this file

            for (size_t i=0; i<m_arrPatternsExcluded.GetCount(); i++)
                if (wxMatchWild(m_arrPatternsExcluded[i], name, false /* dot_special */))
                    return wxDIR_CONTINUE;      // skip this file

            // add the size of this file
            m_sz += sz;

            return wxDIR_CONTINUE;
        }

        virtual wxDirTraverseResult OnDir(const wxString& dirname)
        {
            wxASSERT(dirname.StartsWith(m_prefix));
            wxString name = dirname.Mid(m_prefix.Len());

            // remove the separator, if present
            if (name[0] == wxFileName::GetPathSeparator())
                name = name.Mid(1);

            if (m_arrDirsExcluded.Index(name) != wxNOT_FOUND)
                return wxDIR_IGNORE;      // skip this directory

            // recurse into this directory
            return wxDIR_CONTINUE;
        }

    public:
        wxString m_prefix;
        wxArrayString m_arrFilesExcluded, 
                      m_arrDirsExcluded, 
                      m_arrPatternsExcluded;
        wxULongLong m_szTotal, m_sz;
    };


    wxPackageDirTraverser 
        traverser(m_strRoot,
                  m_pExcluded->Get(wxPEM_FILE),
                  m_pExcluded->Get(wxPEM_DIRECTORY),
                  m_pExcluded->Get(wxPEM_PATTERN));

    if (m_strRoot.IsEmpty())
    {
        m_pUncompressedSize->SetLabel(wxT("NA / NA"));
        return;
    }

    wxDir dir(m_strRoot);
    if (!dir.IsOpened())
    {
        m_pUncompressedSize->SetLabel(wxT("NA / NA"));
        return;
    }

    // search included files
    dir.Traverse(traverser);

    /*
    if (arr.GetCount() > 0)
    {
        wxString list;
        for (size_t i=0; i<arr.GetCount(); i++)
            list += arr[i] + wxT('\n');

        wxLogWarning(wxT("Could not determine the size of the following files:\n%s"),
                     list.c_str());
    }
    */

    // total / only-included
    m_szUncompressed = traverser.m_sz;
    m_pUncompressedSize->SetLabel(
        wxFileName::GetHumanReadableSize(traverser.m_szTotal) +
        wxT(" / ") +
        wxFileName::GetHumanReadableSize(m_szUncompressed));
}



// ----------------------------------------------------------------------------
// wxPackagePropertiesPanel - event handlers
// ----------------------------------------------------------------------------

void wxPackagePropertiesPanel::OnPackagingButton(wxCommandEvent &ev)
{
    switch (ev.GetId())
    {
    case ID_PROP_REFRESH_INCLUDED:
        {
            int reply = wxMessageBox(
                _("Refreshing the tree, all the current state of checked/unchecked files and folders will be lost. Are you sure you want to proceed?"),
                wxT("Question"), wxYES_NO, this);
            if (reply == wxNO)
                return;

            // refresh the contents and expand the root
            m_pIncludedTree->RefreshContents();
            m_pIncludedTree->Expand(m_pIncludedTree->GetRootItem());

            // all items of the tree are now unchecked... reset them to
            // checked and make empty the exclude list
            wxEventBlocker blocker(this);
            m_pIncludedTree->CheckAll();

            m_pExcluded->Clear();
            
            // check status has changed...
            OnUserChange();
            UpdateUncompressedSize();
        }
        break;

    case ID_PROP_EXPAND_INCLUDED:
        m_pIncludedTree->ExpandAll();
        break;

    case ID_PROP_COLLAPSE_INCLUDED:
        m_pIncludedTree->CollapseAll();
        break;
        
    case ID_PROP_NEW_EXCLUDED:
        // add a new excluded file/dir/pattern
        // (this works in a different way than other lists handled below)
        GetNewExcludedStuff();
        OnUserChange();
        UpdateUncompressedSize();
        break;

    case ID_PROP_DELETE_EXCLUDED:
        m_pExcluded->DeleteSelected();
        m_pExcluded->SynchTree(m_pIncludedTree);
        OnUserChange();
        UpdateUncompressedSize();
        break;
    }
}

void wxPackagePropertiesPanel::OnIncludedTreeCheck(wxExplorerTreeEvent &ev)
{
    m_pExcluded->SynchFromTree(m_pIncludedTree);

    OnUserChange();
    UpdateUncompressedSize();
}

void wxPackagePropertiesPanel::OnProjectRootChange(wxFileDirPickerEvent &ev)
{
    // see wxPackagePropertiesPanel header for more info about the m_strRoot variable:
    m_strRoot = ev.GetPath();

    // if the user has not selected a WXP directory yet,
    // set it now to be the same of the project root...
    if (m_pWXPDir->GetPath().IsEmpty())
        m_pWXPDir->SetPath(m_strRoot);

    // update the relative root
    UpdateRelativeRoot();

    // the SetPath() and CheckAll() operations may require some time
    wxBusyCursor wait;

    // update the tree control
    SetIncludePath(m_strRoot);

    {
        // performance optimization: avoid that OnIncludedTreeCheck()
        // gets called hundreds of times because of the initial CheckAll()
        wxEventBlocker blocker(this);
        m_pIncludedTree->CheckAll();
    }

    // no files excluded now
    m_pExcluded->Clear();

    OnUserChange();
    UpdateUncompressedSize();
}

void wxPackagePropertiesPanel::OnWXPDirChange(wxFileDirPickerEvent &ev)
{
    UpdateRelativeRoot();
    OnUserChange();
}

void wxPackagePropertiesPanel::OnPackagingPageUpdateUI(wxUpdateUIEvent &WXUNUSED(ev))
{
    FindWindowById(ID_PROP_DELETE_EXCLUDED)->Enable(!m_pExcluded->IsEmpty());

    // all buttons make no sense with an empty tree:
    bool nonempty = !m_pIncludedTree->IsEmpty();
    FindWindowById(ID_PROP_NEW_EXCLUDED)->Enable(nonempty);
    FindWindowById(ID_PROP_REFRESH_INCLUDED)->Enable(nonempty);
    FindWindowById(ID_PROP_EXPAND_INCLUDED)->Enable(nonempty);
    FindWindowById(ID_PROP_COLLAPSE_INCLUDED)->Enable(nonempty);
}




// ----------------------------------------------------------------------------
// wxPackageExcludeListBox
// ----------------------------------------------------------------------------

IMPLEMENT_DYNAMIC_CLASS( wxPackageExcludeListBox, wxListBox )

bool wxPackageExcludeListBox::Create( wxWindow* parent, wxWindowID id, 
                                      const wxPoint& pos, const wxSize& size, 
                                      long style )
{
    if (!wxListBox::Create(parent, id, pos, size, style))
        return false;

    return true;
}

void wxPackageExcludeListBox::Add(wxPackageExcludeMode type, 
                                  const wxString &str)
{
    wxString toadd =
        wxT("[") + wxPackageExcludeMode2String(type) + wxT("] ") + str;

    // we always want to have unix paths in this control
    toadd.Replace(wxString(wxFileName::GetPathSeparator(), 1), wxT("/"));

    wxASSERT(FindString(toadd) == wxNOT_FOUND);
    Append(toadd);
}

void wxPackageExcludeListBox::Delete(wxPackageExcludeMode type, 
                                     const wxString &str)
{
    wxString toremove =
        wxT("[") + wxPackageExcludeMode2String(type) + wxT("] ") + str;

    int n = FindString(toremove);
    wxASSERT(n != wxNOT_FOUND);
    wxListBox::Delete(n);
}

wxArrayString wxPackageExcludeListBox::Get(wxPackageExcludeMode type) const
{
    wxArrayString ret;
    wxString str;

    for (size_t i=0; i < GetCount(); i++)
    {
        str = GetString(i);
        if (GetType(str) == type)
            ret.Add(GetContent(str));
    }

    return ret;
}

wxString wxPackageExcludeListBox::GetSelection(wxPackageExcludeMode *type) const
{
    wxString sel = GetStringSelection();

    if (type)
        *type = GetType(sel);

    return GetContent(sel);
}

void wxPackageExcludeListBox::DeleteSelected()
{
    int n = wxListBox::GetSelection();
    wxASSERT(n != wxNOT_FOUND);
    wxListBox::Delete(n);
}

/* static */
wxPackageExcludeMode wxPackageExcludeListBox::GetType(const wxString &str)
{
    // look at the part in square brackets
    wxString prefix = str.AfterFirst(wxT('[')).BeforeFirst(wxT(']'));
    return wxString2PackageExcludeMode(prefix);
}

/* static */
wxString wxPackageExcludeListBox::GetContent(const wxString &str)
{
    // look at the part in square brackets
    return wxString(str).AfterFirst(wxT(']')).Remove(0, 1);
}

void wxPackageExcludeListBox::SynchFromTree(const wxExplorerTreeCtrl *tree)
{
    wxEventBlocker block(this);     // performance optimizations: avoid
                                    // redraws in the middle of the list update

    // before clearing this listbox, save the patterns
    wxArrayString patterns = Get(wxPEM_PATTERN);
    Clear();

    // readd the patterns
    Add(wxPEM_PATTERN, patterns);

    // now add completely unchecked dirs which do not match our patterns
    wxArrayString dirs = tree->GetDirsRecursivelyUnChecked();
    for (size_t i=0; i<dirs.GetCount(); i++)
    {
        // NO NEED to remove the prefix since GetDirsRecursivelyUnChecked()
        // does not return prefixed directories

        bool nomatches = true;
        for (size_t j=0; j<patterns.GetCount(); j++)
        {
            if (wxMatchWild(patterns[j], dirs[i], false /* special_dot */))
            {
                nomatches = false;
                break;
            }
        }

        if (nomatches)
            Add(wxPEM_DIRECTORY, dirs[i]);
    }

    // now add all unchecked files which do not match our patterns
    // and are not contained in the already added directories
    wxArrayString files = tree->GetFilesChecked(false /* get unchecked files */);
    for (size_t i=0; i<files.GetCount(); i++)
    {
        // remove the prefix
        wxString f = files[i];
        f.Remove(0, tree->GetPathPrefix().Len()+1);

        bool nomatches = true;
        for (size_t j=0; j<patterns.GetCount(); j++)
        {
            if (wxMatchWild(patterns[j], f, false /* special_dot */))
            {
                nomatches = false;
                break;
            }
        }

        for (size_t j=0; j<dirs.GetCount(); j++)
        {
            if (f.StartsWith(dirs[j]))
            {
                nomatches = false;
                break;
            }
        }

        if (nomatches)
            Add(wxPEM_FILE, f);
    }
}

void wxPackageExcludeListBox::SynchTree(wxExplorerTreeCtrl *tree) const
{
    wxEventBlocker blocker(tree);   // for better performances (even if 
                                    // it doesn't make a big difference)

    // build the arrays once and pass them recursively
    wxArrayString files = Get(wxPEM_FILE),
                  dirs = Get(wxPEM_DIRECTORY),
                  patterns = Get(wxPEM_PATTERN);

    wxTreeItemId root = tree->GetRootItem();
    bool allchecked = DoSynchTree(root, tree, tree->GetItemText(root) + wxT("/"), 
                                  files, dirs, patterns);

    // see HACK comment below
    tree->SetItemBold(root, allchecked);
}

bool wxPackageExcludeListBox::DoSynchTree(const wxTreeItemId &item,
                                          wxExplorerTreeCtrl *tree,
                                          const wxString &prefix,
                                          const wxArrayString &files,
                                          const wxArrayString &dirs,
                                          const wxArrayString &patterns) const
{
    bool allchecked = true;
    wxTreeItemIdValue cookie;
    for (wxTreeItemId i = tree->GetFirstChild(item, cookie);
         i.IsOk();
         i = tree->GetNextChild(item, cookie))
    {
        // don't use tree->GetItemFullPath() for better performances
        wxString name = prefix + tree->GetItemText(i);

        if (tree->IsFolder(i))
        {
            // is this a folder to uncheck?
            if (dirs.Index(name) != wxNOT_FOUND)
            {
                tree->UnCheck(i);
                allchecked = false;
            }
            else
            {
                bool childchecked = 
                    DoSynchTree(i, tree, name + wxT("/"), files, dirs, patterns);

                // HACK: we should do a tree->UnCheck(i) if allchecked==false
                //       but this would result yet in another recursive walk of
                //       the tree (in wxExplorerTreeCtrl) which would slow down
                //       everything without altering anything (we did already
                //       modify all item's status in our recursive DoSynchTree
                //       call done above); so we just alter the bold status of the
                //       folder ourselves here:
                tree->SetItemBold(i, childchecked);

                allchecked &= childchecked;
            }
        }
        else
        {
            // is this a file to uncheck?
            if (files.Index(name) != wxNOT_FOUND)
            {
                tree->UnCheck(i);
                allchecked = false;
            }
            else
            {
                bool matches = false;
                for (size_t j=0; j<patterns.GetCount(); j++)
                {
                    if (wxMatchWild(patterns[j], name, false /* special_dot */))
                    {
                        matches = true;
                        break;
                    }
                }

                if (matches)
                {
                    tree->UnCheck(i);
                    allchecked = false;
                }
                else
                    tree->Check(i);
            }
        }
    }

    return allchecked;
}

