changeset 23:de305d93a503

Primitivus: new FileDialog Advanced file choosing dialog with: - path understanding when removing part with C-w - completion - listing of GTK & KDE's bookmarks - selection of file by typing the first letters of the name
author Goffi <goffi@goffi.org>
date Mon, 16 Aug 2010 21:11:00 +0800
parents dfabea6f73b5
children 67a19cfeab8f
files frontends/primitivus/files_management.py
diffstat 1 files changed, 152 insertions(+), 19 deletions(-) [+]
line wrap: on
line diff
--- a/frontends/primitivus/files_management.py	Mon Aug 16 21:06:03 2010 +0800
+++ b/frontends/primitivus/files_management.py	Mon Aug 16 21:11:00 2010 +0800
@@ -25,11 +25,17 @@
 import os, os.path
 from xml.dom import minidom
 from logging import debug, info, error
+from time import time
 
 class PathEdit(custom_widgets.AdvancedEdit):
+    """AdvancedEdit with manage file paths"""
     
     def keypress(self, size, key):
-        if key == 'ctrl w':
+        if key == '~' and self.edit_pos==0:
+            expanded = os.path.expanduser('~')
+            self.set_edit_text(os.path.normpath(expanded+'/'+self.edit_text))
+            self.set_edit_pos(len(expanded)+1)
+        elif key == 'ctrl w':
             if self.edit_pos<2:
                 return
             before = self.edit_text[:self.edit_pos]
@@ -40,11 +46,96 @@
         else:
             return super(PathEdit, self).keypress(size, key) 
 
+class FilesViewer(urwid.WidgetWrap):
+    """List specialised for files"""
+
+    def __init__(self, onPreviousDir, onDirClick, onFileClick):
+        self.path=''
+        self.key_cache = ''
+        self.key_time = time()
+        self.onPreviousDir = onPreviousDir
+        self.onDirClick = onDirClick
+        self.onFileClick = onFileClick
+        self.files_list = urwid.SimpleListWalker([])
+        self.show_hidden = True 
+        listbox = urwid.ListBox(self.files_list)
+        urwid.WidgetWrap.__init__(self, listbox)
+
+    def keypress(self, size, key):
+        if key=='meta h':
+            #(un)hide hidden files
+            self.show_hidden = not self.show_hidden
+            self.showDirectory(self.path)
+        if key=='meta d':
+            #jump to directories
+            if self.files_list:
+                self._w.set_focus(0)
+        elif key=='meta f':
+            for idx in range(len(self.files_list)):
+                if isinstance(self.files_list[idx].base_widget,urwid.Divider):
+                    if idx<len(self.files_list)-1:
+                        self._w.set_focus(idx+1)
+                    break
+        elif len(key) == 1:
+            if time() - self.key_time > 2:
+                self.key_cache=key
+            else:
+                self.key_cache+=key
+            self.key_time = time()
+            for idx in range(len(self.files_list)):
+                if isinstance(self.files_list[idx],custom_widgets.ClickableText) and self.files_list[idx].get_text().lower().startswith(self.key_cache.lower()):
+                    self._w.set_focus(idx)
+                    break
+        else:
+            return self._w.keypress(size, key)
+
+    def showDirectory(self, path):
+        self.path = path
+        del self.files_list[:]
+        directories = []
+        files = []
+        try:
+            for filename in os.listdir(path):
+                fullpath = os.path.join(path,filename)
+                if os.path.isdir(fullpath):
+                    directories.append(filename)
+                else:
+                    files.append(filename)
+        except OSError:
+           self.files_list.append(urwid.Text(("warning",_("Impossible to list directory")),'center')) 
+        directories.sort()
+        files.sort()
+        if os.path.abspath(path)!='/' and os.path.abspath(path) != '//':
+            previous_wid = custom_widgets.ClickableText('..',default_attr='directory')
+            urwid.connect_signal(previous_wid,'click',self.onPreviousDir)
+            self.files_list.append(previous_wid)
+        for directory in directories:
+            if directory.startswith('.') and not self.show_hidden:
+                continue
+            dir_wid = custom_widgets.ClickableText(directory,default_attr='directory')
+            urwid.connect_signal(dir_wid,'click',self.onDirClick)
+            self.files_list.append(dir_wid)
+        self.files_list.append(urwid.AttrMap(urwid.Divider('-'),'separator'))
+        for filename in files:
+            if filename.startswith('.') and not self.show_hidden:
+                continue
+            file_wid = custom_widgets.ClickableText(filename)
+            urwid.connect_signal(file_wid,'click',self.onFileClick)
+            self.files_list.append(file_wid)
+
+
+
 class FileDialog(urwid.WidgetWrap):
 
-    def __init__(self,title=_("Please select a file")):
+    def __init__(self, ok_cb, cancel_cb, title=_("Please select a file"), style=[]):
+        """Create file dialog
+        @param title: title of the window/popup
+        @param style: NOT USED YET #FIXME
+        """
+        self.ok_cb = ok_cb
         self.__home_path = os.path.expanduser('~')
-        self.path_wid = PathEdit(_('Path: '), os.getcwdu())
+        self.path_wid = PathEdit(_('Path: '))
+        self.path_wid.setCompletionMethod(self._directory_completion)
         urwid.connect_signal(self.path_wid, 'change', self.onPathChange)
         header = urwid.Pile([self.path_wid, urwid.Divider(u'─')])
         bookm_list = urwid.SimpleListWalker([])
@@ -57,30 +148,60 @@
             urwid.connect_signal(book_wid, 'click', self.onBookmarkSelected)
             bookm_list.append(book_wid)
         bookm_wid = urwid.Frame(urwid.ListBox(bookm_list), urwid.AttrMap(urwid.Text(_('Bookmarks'),'center'),'title'))
-        self.files_list = urwid.SimpleListWalker([urwid.Text('toto.mkv')])
-        files_wid = urwid.ListBox(self.files_list)
+        self.files_wid = FilesViewer(self.onPreviousDir, self.onDirClick, self.onFileClick)
         center_row = urwid.Columns([('weight',2,bookm_wid),
-                     ('weight',8,custom_widgets.VerticalSeparator(files_wid))])
-        main_frame = custom_widgets.FocusFrame(center_row, header)
+                     ('weight',8,custom_widgets.VerticalSeparator(self.files_wid))])
+
+        buttons = []
+        buttons.append(custom_widgets.CustomButton(_('Cancel'),cancel_cb))
+        max_len = max([button.getSize() for button in buttons])
+        buttons_wid = urwid.GridFlow(buttons,max_len,1,0,'center')
+        main_frame = custom_widgets.FocusFrame(center_row, header, buttons_wid)
         decorated = custom_widgets.LabelLine(main_frame, custom_widgets.SurroundedText(title))
         urwid.WidgetWrap.__init__(self, decorated)
+        self.path_wid.set_edit_text(os.getcwdu())
+
+    def _directory_completion(self, path, completion_data):
+        path=os.path.abspath(path)
+        if not os.path.isdir(path):
+            head,dir_start = os.path.split(path)
+        else:
+            head=path
+            dir_start=''
+        try:
+            filenames = os.listdir(head)
+            filenames.sort()
+            try:
+                start_idx=filenames.index(completion_data['last_dir'])+1
+                if start_idx == len(filenames):
+                    start_idx = 0
+            except (KeyError,ValueError):
+                start_idx = 0
+            for idx in range(start_idx,len(filenames)) + range(0,start_idx):
+                full_path = os.path.join(head,filenames[idx])
+                if filenames[idx].lower().startswith(dir_start.lower()) and os.path.isdir(full_path):
+                    completion_data['last_dir'] = filenames[idx]
+                    return full_path 
+        except OSError:
+            pass
+        return path
 
     def getBookmarks(self):
-        gnome_bookm = os.path.expanduser("~/.gtk-bookmarks")
-        kde_bookm = os.path.expanduser("~/.kde/share/apps/kfileplaces/bookmarks.xm")
+        gtk_bookm = os.path.expanduser("~/.gtk-bookmarks")
+        kde_bookm = os.path.expanduser("~/.kde/share/apps/kfileplaces/bookmarks.xml")
         bookmarks = set()
         try:
-            with open(gnome_bookm) as gnome_fd:
-                for bm in gnome_fd.readlines():
+            with open(gtk_bookm) as gtk_fd:
+                for bm in gtk_fd.readlines():
                     if bm.startswith("file:///"):
                         bookmarks.add(bm[7:].replace('\n',''))
         except IOError:
-            info(_('No Gnome bookmarks file found'))
+            info(_('No GTK bookmarks file found'))
             pass
         
         try:
             dom = minidom.parse(kde_bookm)
-            for elem in getElementsByTagName('bookmark'):
+            for elem in dom.getElementsByTagName('bookmark'):
                 bm = elem.getAttribute("href")
                 if bm.startswith("file:///"):
                     bookmarks.add(bm[7:])
@@ -93,9 +214,21 @@
     def onBookmarkSelected(self, button):
         self.path_wid.set_edit_text(os.path.expanduser(button.get_text()))
 
-    def onPathChange(self, edit, text):
-        if os.path.isdir(text):
-            del self.files_list[:]
-            files = os.listdir(text)
-            files.sort()
-            self.files_list.extend([custom_widgets.ClickableText(filename) for filename in files])
+    def onPathChange(self, edit, path):
+        if os.path.isdir(path):
+            self.files_wid.showDirectory(path)
+
+    def onPreviousDir(self, wid):
+        path = os.path.abspath(self.path_wid.get_edit_text())
+        if not os.path.isdir(path):
+            path = dirname(path)
+        self.path_wid.set_edit_text(os.path.split(path)[0])
+
+    def onDirClick(self, wid):
+        path = os.path.abspath(self.path_wid.get_edit_text())
+        if not os.path.isdir(path):
+            path = dirname(path)
+        self.path_wid.set_edit_text(os.path.join(path,wid.get_text()))
+    
+    def onFileClick(self, wid):
+        self.ok_cb(os.path.abspath(os.path.join(self.files_wid.path,wid.get_text())))