Laurent@951: #!/usr/bin/env python Laurent@951: # -*- coding: utf-8 -*- Laurent@951: andrej@1571: # This file is part of Beremiz, a Integrated Development Environment for andrej@1571: # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. andrej@1571: # andrej@1571: # Copyright (C) 2013: Edouard TISSERANT and Laurent BESSARD andrej@1571: # andrej@1571: # See COPYING file for copyrights details. andrej@1571: # andrej@1571: # This program is free software; you can redistribute it and/or andrej@1571: # modify it under the terms of the GNU General Public License andrej@1571: # as published by the Free Software Foundation; either version 2 andrej@1571: # of the License, or (at your option) any later version. andrej@1571: # andrej@1571: # This program is distributed in the hope that it will be useful, andrej@1571: # but WITHOUT ANY WARRANTY; without even the implied warranty of andrej@1571: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the andrej@1571: # GNU General Public License for more details. andrej@1571: # andrej@1571: # You should have received a copy of the GNU General Public License andrej@1571: # along with this program; if not, write to the Free Software andrej@1571: # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Laurent@951: andrej@1881: andrej@1881: from __future__ import absolute_import Laurent@951: import os Laurent@951: Laurent@951: import wx Laurent@951: Laurent@951: from util.BitmapLibrary import GetBitmap Laurent@951: Laurent@951: DRIVE, FOLDER, FILE = range(3) Laurent@951: andrej@1736: Laurent@951: def sort_folder(x, y): Laurent@951: if x[1] == y[1]: Laurent@951: return cmp(x[0], y[0]) Laurent@951: elif x[1] != FILE: Laurent@951: return -1 Laurent@951: else: Laurent@951: return 1 Laurent@951: andrej@1736: Laurent@951: def splitpath(path): Laurent@951: head, tail = os.path.split(path) Laurent@951: if head == "": Laurent@951: return [tail] Laurent@951: elif tail == "": Laurent@951: return splitpath(head) Laurent@951: return splitpath(head) + [tail] Laurent@951: andrej@1736: Laurent@951: class FolderTree(wx.Panel): andrej@1730: Laurent@951: def __init__(self, parent, folder, filter=None, editable=True): Laurent@951: wx.Panel.__init__(self, parent, style=wx.TAB_TRAVERSAL) andrej@1730: Laurent@951: main_sizer = wx.BoxSizer(wx.VERTICAL) andrej@1730: andrej@1730: self.Tree = wx.TreeCtrl(self, andrej@1768: style=(wx.TR_HAS_BUTTONS | andrej@1768: wx.TR_SINGLE | andrej@1768: wx.SUNKEN_BORDER | andrej@1768: wx.TR_HIDE_ROOT | andrej@1768: wx.TR_LINES_AT_ROOT | andrej@1768: wx.TR_EDIT_LABELS)) Laurent@951: if wx.Platform == '__WXMSW__': Laurent@951: self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnTreeItemExpanded, self.Tree) Laurent@951: self.Tree.Bind(wx.EVT_LEFT_DOWN, self.OnTreeLeftDown) Laurent@951: else: Laurent@951: self.Bind(wx.EVT_TREE_ITEM_EXPANDED, self.OnTreeItemExpanded, self.Tree) Laurent@951: self.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnTreeItemCollapsed, self.Tree) Laurent@951: self.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, self.OnTreeBeginLabelEdit, self.Tree) Laurent@951: self.Bind(wx.EVT_TREE_END_LABEL_EDIT, self.OnTreeEndLabelEdit, self.Tree) Laurent@951: main_sizer.AddWindow(self.Tree, 1, flag=wx.GROW) andrej@1730: Laurent@951: if filter is not None: Laurent@951: self.Filter = wx.ComboBox(self, style=wx.CB_READONLY) Laurent@951: self.Bind(wx.EVT_COMBOBOX, self.OnFilterChanged, self.Filter) Laurent@951: main_sizer.AddWindow(self.Filter, flag=wx.GROW) Laurent@951: else: Laurent@951: self.Filter = None andrej@1730: Laurent@951: self.SetSizer(main_sizer) andrej@1730: Laurent@951: self.Folder = folder Laurent@951: self.Editable = editable andrej@1730: Laurent@951: self.TreeImageList = wx.ImageList(16, 16) Laurent@951: self.TreeImageDict = {} Laurent@951: for item_type, bitmap in [(DRIVE, "tree_drive"), Laurent@951: (FOLDER, "tree_folder"), Laurent@951: (FILE, "tree_file")]: Laurent@951: self.TreeImageDict[item_type] = self.TreeImageList.Add(GetBitmap(bitmap)) Laurent@951: self.Tree.SetImageList(self.TreeImageList) andrej@1730: Laurent@951: self.Filters = {} Laurent@951: if self.Filter is not None: Laurent@951: filter_parts = filter.split("|") Laurent@951: for idx in xrange(0, len(filter_parts), 2): Laurent@951: if filter_parts[idx + 1] == "*.*": Laurent@951: self.Filters[filter_parts[idx]] = "" Laurent@951: else: Laurent@951: self.Filters[filter_parts[idx]] = filter_parts[idx + 1].replace("*", "") Laurent@951: self.Filter.Append(filter_parts[idx]) Laurent@951: if idx == 0: Laurent@951: self.Filter.SetStringSelection(filter_parts[idx]) andrej@1730: Laurent@951: self.CurrentFilter = self.Filters[self.Filter.GetStringSelection()] Laurent@951: else: Laurent@951: self.CurrentFilter = "" andrej@1730: Laurent@951: def _GetFolderChildren(self, folderpath, recursive=True): Laurent@951: items = [] Laurent@951: if wx.Platform == '__WXMSW__' and folderpath == "/": Laurent@951: for c in xrange(ord('a'), ord('z')): Laurent@951: drive = os.path.join("%s:\\" % chr(c)) Laurent@951: if os.path.exists(drive): Laurent@951: items.append((drive, DRIVE, self._GetFolderChildren(drive, False))) Laurent@951: else: Laurent@951: try: Laurent@951: files = os.listdir(folderpath) andrej@1780: except Exception: Laurent@951: return [] Laurent@951: for filename in files: Laurent@951: if not filename.startswith("."): Laurent@951: filepath = os.path.join(folderpath, filename) Laurent@951: if os.path.isdir(filepath): Laurent@951: if recursive: Laurent@951: children = len(self._GetFolderChildren(filepath, False)) Laurent@951: else: Laurent@951: children = 0 Laurent@951: items.append((filename, FOLDER, children)) andrej@1730: elif (self.CurrentFilter == "" or Laurent@951: os.path.splitext(filename)[1] == self.CurrentFilter): Laurent@951: items.append((filename, FILE, None)) Laurent@951: if recursive: Laurent@951: items.sort(sort_folder) Laurent@951: return items andrej@1730: Laurent@951: def SetFilter(self, filter): Laurent@951: self.CurrentFilter = filter andrej@1730: Laurent@951: def GetTreeCtrl(self): Laurent@951: return self.Tree andrej@1730: Laurent@951: def RefreshTree(self): Laurent@951: root = self.Tree.GetRootItem() Laurent@951: if not root.IsOk(): Laurent@951: root = self.Tree.AddRoot("") Laurent@951: self.GenerateTreeBranch(root, self.Folder) andrej@1730: Laurent@951: def GenerateTreeBranch(self, root, folderpath): Laurent@951: item, item_cookie = self.Tree.GetFirstChild(root) Laurent@951: for idx, (filename, item_type, children) in enumerate(self._GetFolderChildren(folderpath)): Laurent@951: if not item.IsOk(): Laurent@951: item = self.Tree.AppendItem(root, filename, self.TreeImageDict[item_type]) Laurent@951: if wx.Platform != '__WXMSW__': Laurent@951: item, item_cookie = self.Tree.GetNextChild(root, item_cookie) Laurent@951: elif self.Tree.GetItemText(item) != filename: Laurent@951: item = self.Tree.InsertItemBefore(root, idx, filename, self.TreeImageDict[item_type]) Laurent@951: filepath = os.path.join(folderpath, filename) Laurent@951: if item_type != FILE: Laurent@951: if self.Tree.IsExpanded(item): Laurent@951: self.GenerateTreeBranch(item, filepath) Laurent@951: elif children > 0: Laurent@951: self.Tree.SetItemHasChildren(item) Laurent@951: item, item_cookie = self.Tree.GetNextChild(root, item_cookie) Laurent@951: to_delete = [] Laurent@951: while item.IsOk(): Laurent@951: to_delete.append(item) Laurent@951: item, item_cookie = self.Tree.GetNextChild(root, item_cookie) Laurent@951: for item in to_delete: Laurent@951: self.Tree.Delete(item) Laurent@951: Laurent@951: def ExpandItem(self, item): Laurent@951: self.GenerateTreeBranch(item, self.GetPath(item)) Laurent@951: self.Tree.Expand(item) andrej@1730: Laurent@951: def OnTreeItemActivated(self, event): Laurent@951: self.ExpandItem(event.GetItem()) Laurent@951: event.Skip() andrej@1730: Laurent@951: def OnTreeLeftDown(self, event): Laurent@951: item, flags = self.Tree.HitTest(event.GetPosition()) Laurent@951: if flags & wx.TREE_HITTEST_ONITEMBUTTON and not self.Tree.IsExpanded(item): Laurent@951: self.ExpandItem(item) Laurent@951: else: Laurent@951: event.Skip() andrej@1730: Laurent@951: def OnTreeItemExpanded(self, event): Laurent@951: item = event.GetItem() Laurent@951: self.GenerateTreeBranch(item, self.GetPath(item)) Laurent@951: event.Skip() Laurent@951: Laurent@951: def OnTreeItemCollapsed(self, event): Laurent@951: item = event.GetItem() Laurent@951: self.Tree.DeleteChildren(item) Laurent@951: self.Tree.SetItemHasChildren(item) Laurent@951: event.Skip() Laurent@951: Laurent@951: def OnTreeBeginLabelEdit(self, event): Laurent@951: item = event.GetItem() Laurent@951: if self.Editable and not self.Tree.ItemHasChildren(item): Laurent@951: event.Skip() Laurent@951: else: Laurent@951: event.Veto() andrej@1730: Laurent@951: def OnTreeEndLabelEdit(self, event): Laurent@1219: new_name = event.GetLabel() Laurent@1219: if new_name != "": Laurent@1219: old_filepath = self.GetPath(event.GetItem()) Laurent@1219: new_filepath = os.path.join(os.path.split(old_filepath)[0], new_name) Laurent@1219: if new_filepath != old_filepath: Laurent@1219: if not os.path.exists(new_filepath): Laurent@1219: os.rename(old_filepath, new_filepath) Laurent@1219: event.Skip() Laurent@1219: else: andrej@1758: message = wx.MessageDialog(self, andrej@1768: _("File '%s' already exists!") % new_name, andrej@1768: _("Error"), wx.OK | wx.ICON_ERROR) Laurent@1219: message.ShowModal() Laurent@1219: message.Destroy() Laurent@1219: event.Veto() Laurent@1219: else: Laurent@1219: event.Skip() andrej@1730: Laurent@951: def OnFilterChanged(self, event): Laurent@951: self.CurrentFilter = self.Filters[self.Filter.GetStringSelection()] Laurent@951: self.RefreshTree() Laurent@951: event.Skip() andrej@1730: Laurent@951: def _SelectItem(self, root, parts): Laurent@951: if len(parts) == 0: Laurent@951: self.Tree.SelectItem(root) Laurent@951: else: Laurent@951: item, item_cookie = self.Tree.GetFirstChild(root) Laurent@951: while item.IsOk(): Laurent@951: if self.Tree.GetItemText(item) == parts[0]: andrej@1766: if self.Tree.ItemHasChildren(item) and \ andrej@1766: not self.Tree.IsExpanded(item): Laurent@951: self.Tree.Expand(item) Laurent@951: wx.CallAfter(self._SelectItem, item, parts[1:]) Laurent@951: else: Laurent@951: self._SelectItem(item, parts[1:]) Laurent@951: return Laurent@951: item, item_cookie = self.Tree.GetNextChild(root, item_cookie) andrej@1730: Laurent@951: def SetPath(self, path): Laurent@951: if path.startswith(self.Folder): Laurent@951: root = self.Tree.GetRootItem() Laurent@951: if root.IsOk(): Laurent@951: relative_path = path.replace(os.path.join(self.Folder, ""), "") Laurent@951: self._SelectItem(root, splitpath(relative_path)) andrej@1730: Laurent@951: def GetPath(self, item=None): Laurent@951: if item is None: Laurent@951: item = self.Tree.GetSelection() Laurent@951: if item.IsOk(): Laurent@951: filepath = self.Tree.GetItemText(item) Laurent@951: parent = self.Tree.GetItemParent(item) Laurent@951: while parent.IsOk() and parent != self.Tree.GetRootItem(): Laurent@951: filepath = os.path.join(self.Tree.GetItemText(parent), filepath) Laurent@951: parent = self.Tree.GetItemParent(parent) Laurent@951: return os.path.join(self.Folder, filepath) Laurent@951: return self.Folder