1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 import os
18 import traceback
19
20 import libxyz.ui
21 import libxyz.core
22 import libxyz.const
23 import libxyz.exceptions
24
25 from libxyz.core.utils import ustring, bstring, is_func
26 from libxyz.ui import lowui
27 from libxyz.ui import align
28 from libxyz.ui import Shortcut
29 from libxyz.ui.utils import refresh
30 from libxyz.ui.utils import truncate
31 from libxyz.vfs.types import VFSTypeFile
32 from libxyz.vfs.local import LocalVFSObject
33
34 -class Panel(lowui.WidgetWrap):
35 """
36 Panel is used to display filesystem hierarchy
37 """
38
39 resolution = (u"panel", u"widget")
40 context = u":sys:panel"
41
42 EVENT_SHUTDOWN = u"event:shutdown"
43
45 self.xyz = xyz
46 self.conf = self.xyz.conf[u"plugins"][u":sys:panel"]
47
48 self._keys = libxyz.ui.Keys()
49
50 _size = self.xyz.screen.get_cols_rows()
51 _blocksize = libxyz.ui.Size(rows=_size[1] - 1, cols=_size[0] / 2 - 2)
52 self._enc = xyzenc
53 self._stop = False
54 self._resize = False
55
56 self._set_plugins()
57 self._cmd = libxyz.ui.Cmd(xyz)
58 _cwd = os.getcwd()
59
60 self.filters = self._build_filters()
61 self.xyz.hm.register("event:conf_update", self._update_conf_hook)
62
63 self.block1 = Block(xyz, _blocksize, _cwd, self._enc, active=True)
64 self.block2 = Block(xyz, _blocksize, _cwd, self._enc)
65 self._compose()
66
67 super(Panel, self).__init__(self._widget)
68
69
70
72 """
73 Hook for update conf event
74 """
75
76
77 if sect != "plugins" or var != ":sys:panel":
78 return
79
80 if "filters" in val or "filters_enabled" in val:
81 self.filters = self._build_filters()
82
83
84
86 """
87 Compose widgets
88 """
89
90 columns = lowui.Columns([self.block1, self.block2], 0)
91 self._widget = lowui.Pile([columns, self._cmd])
92
93
94
96 """
97 Compile filters
98 """
99
100 filters = []
101
102
103 if not self.conf[u"filters_enabled"]:
104 return filters
105
106 for f in self.conf[u"filters"]:
107 try:
108 rule = libxyz.core.FSRule(ustring(f))
109 except libxyz.exceptions.ParseError, e:
110 xyzlog.error(_(u"Error compiling filter: %s" %
111 ustring(str(e))))
112 continue
113 else:
114 filters.append(rule)
115
116 return filters
117
118
119
120 @property
122 if self.block1.active:
123 return self.block1
124 else:
125 return self.block2
126
127
128
129 @property
131 if self.block1.active:
132 return self.block2
133 else:
134 return self.block1
135
136
137
139 """
140 Start working loop
141 """
142
143 _dim = self.xyz.screen.get_cols_rows()
144
145 while True:
146 if self._stop:
147 break
148
149 canv = self.xyz.top.render(_dim, True)
150 self.xyz.screen.draw_screen(_dim, canv)
151
152 _input = self.xyz.input.get()
153
154 if _input:
155 try:
156 self._cmd.keypress(_dim, _input)
157 except Exception, e:
158 xyzlog.error(_(u"Error executing bind (%s): %s") %
159 (Shortcut(_input), ustring(str(e))))
160 xyzlog.debug(ustring(traceback.format_exc(),
161 self._enc))
162
163 if self.xyz.input.resized:
164 self._resize = True
165
166 if self._resize:
167 self._resize = False
168 _dim = self.xyz.screen.get_cols_rows()
169 _bsize = libxyz.ui.Size(rows=_dim[1] - 1,
170 cols=_dim[0] / 2 - 2)
171
172 self.block1.size = _bsize
173 self.block2.size = _bsize
174 self._cmd._invalidate()
175 self.block1._invalidate()
176 self.block2._invalidate()
177
178
179
250
251
252
264
265
266
268 """
269 Reparint screen
270 """
271
272 self._resize = True
273 self.xyz.screen.clear()
274
275
276
277 - def entry_next(self):
278 """
279 Next entry
280 """
281
282 return self.active.next()
283
284
285
286 - def entry_prev(self):
287 """
288 Previous entry
289 """
290
291 return self.active.prev()
292
293
294
295 - def entry_top(self):
296 """
297 Top entry
298 """
299
300 return self.active.top()
301
302
303
304 - def entry_bottom(self):
305 """
306 Bottom entry
307 """
308
309 return self.active.bottom()
310
311
312
324
325
326
333
334
335
342
343
344
346 """
347 Get selected VFSObject instance
348 """
349
350 if active:
351 obj = self.active
352 else:
353 obj = self.inactive
354
355 return obj.get_selected()
356
357
358
360 """
361 Return list of tagged VFSObject instances
362 """
363
364 if active:
365 obj = self.active
366 else:
367 obj = self.inactive
368
369 return obj.get_tagged()
370
371
372
374 """
375 Return list of not tagged VFSObject instances
376 """
377
378 if active:
379 obj = self.active
380 else:
381 obj = self.inactive
382
383 return obj.get_untagged()
384
385
386
388 """
389 Return VFSObject instance of current VFSObject
390 """
391
392 if active:
393 obj = self.active
394 else:
395 obj = self.inactive
396
397 return obj.get_current()
398
399
400
402 """
403 Return list of tagged VFSObject instances or list of single selected
404 object if none tagged
405 """
406
407 return self.get_tagged() or [self.get_selected()]
408
409
410
422
423
424
426 """
427 Tag every single object in current dir
428 """
429
430 if active:
431 obj = self.active
432 else:
433 obj = self.inactive
434
435 return obj.tag_all()
436
437
438
440 """
441 Untag every single object in current dir
442 """
443
444 if active:
445 obj = self.active
446 else:
447 obj = self.inactive
448
449 return obj.untag_all()
450
451
452
454 """
455 Invert currently tagged files
456 """
457
458 if active:
459 obj = self.active
460 else:
461 obj = self.inactive
462
463 return obj.tag_invert()
464
465
466
468 """
469 Tag files by combined rule
470 """
471
472 if active:
473 obj = self.active
474 else:
475 obj = self.inactive
476
477 return obj.tag_rule()
478
479
480
482 """
483 Untag files by combined rules
484 """
485
486 if active:
487 obj = self.active
488 else:
489 obj = self.inactive
490
491 return obj.untag_rule()
492
493
494
496 """
497 Swap panel blocks
498 """
499
500 self.block1, self.block2 = self.block2, self.block1
501 self._compose()
502
503 if hasattr(self, "set_w"):
504 self.set_w(self._widget)
505 else:
506 self._w = self._widget
507
508
509
510 - def reload(self, active=True):
511 """
512 Reload contents
513 """
514
515 if active:
516 obj = self.active
517 else:
518 obj = self.inactive
519
520 return obj.reload()
521
522
523
531
532
533
534 - def action(self, active=True):
535 """
536 Perfrom action on selected object
537 """
538
539 if active:
540 obj = self.active
541 else:
542 obj = self.inactive
543
544 return obj.action()
545
546
547
548 - def chdir(self, path, active=True):
559
560
561
568
569
570
577
578
579
581 """
582 Enable cyclic search-when-you-type mode
583 """
584
585 self.active.search_cycle()
586
587
588
600
601
602
603 - def select(self, name, active=True):
604 """
605 Select VFS object by given name in current directory
606 """
607
608 if active:
609 obj = self.active
610 else:
611 obj = self.inactive
612
613 return obj.select(name)
614
615
616
617 - def cwd(self, active=True):
618 """
619 Get current working directory
620 """
621
622 if active:
623 obj = self.active
624 else:
625 obj = self.inactive
626
627 return obj.cwd
628
629
630
632 """
633 Return vfs driver used by object. None stands for LocalVFS
634 """
635
636 if active:
637 obj = self.active
638 else:
639 obj = self.inactive
640
641 return obj.vfs_driver
642
643
644
646 """
647 Filter objects
648 """
649
650 if not self.conf["filters_enabled"]:
651 return objects
652
653 policy = self.conf["filters_policy"]
654
655 def policyf(res):
656 if policy == True:
657 result = not res
658 else:
659 result = res
660
661 return result
662
663 for f in self.filters:
664 objects = [x for x in objects if policyf(f.match(x))]
665
666 return objects
667
668
669
670 - def sort(self, objects):
671 """
672 Sort objects
673 """
674
675 policy = self.conf["sorting_policy"]
676
677 if policy is None:
678 return objects
679
680 if policy not in self.conf["sorting"]:
681 xyzlog.warning(_(u"Unable to find `%s` sorting policy" %
682 ustring(policy)))
683 return objects
684
685 policy_data = self.conf["sorting"][policy]
686
687 if is_func(policy_data):
688 objects.sort(cmp=policy_data)
689 elif isinstance(policy_data, list):
690 for f in policy_data:
691 objects.sort(cmp=f)
692
693 return objects
694
695
696
697 -class Block(lowui.FlowWidget):
698 """
699 Single panel block
700 """
701
702 - def __init__(self, xyz, size, path, enc, active=False):
703 """
704 @param xyz: XYZData instance
705 @param size: Block widget size
706 @type size: L{libxyz.ui.Size}
707 @param enc: Local encoding
708 @param active: Boolean flag, True if block is active
709
710 Required resources: cwdtitle, cwdtitleinact, panel, cursor, info
711 border, tagged
712 """
713
714 self.xyz = xyz
715 self.size = size
716 self.attr = lambda x: self.xyz.skin.attr(Panel.resolution, x)
717
718 self.term_width = lambda x: lowui.util.calc_width(x, 0, len(x))
719
720 self.active = active
721 self.selected = 0
722 self.cwd = path
723
724 self._display = []
725 self._vindex = 0
726 self._from = 0
727 self._to = 0
728 self._force_reload = False
729 self.entries = []
730 self._dir = None
731 self._len = 0
732 self._palettes = []
733 self._vfsobj = None
734 self._title = u""
735 self._tagged = []
736
737 self._cursor_attr = None
738 self._custom_info = None
739 self._keys = libxyz.ui.Keys()
740 self._cmd = self.xyz.pm.load(":sys:cmd")
741 self._filter = self.xyz.pm.from_load(":sys:panel", "filter")
742 self._sort = self.xyz.pm.from_load(":sys:panel", "sort")
743
744 self._pending = libxyz.core.Queue(20)
745 self._re_raw = r".*"
746 self._rule_raw = ""
747 self._enc = enc
748 self.vfs_driver = None
749
750 self.chdir(path)
751
752 self._winfo = lowui.Text(u"")
753 self._sep = libxyz.ui.Separator()
754
755 _info = self._make_info()
756 _title_attr = self._get_title_attr()
757
758 self.frame = lowui.Frame(lowui.Filler(lowui.Text("")), footer=_info)
759 self.border = libxyz.ui.Border(self.frame, self._title,
760 _title_attr, self.attr(u"border"))
761 self.block = lowui.AttrWrap(self.border, self.attr(u"panel"))
762
763 super(Block, self).__init__()
764
765
766
767 - def rows(self, (maxcol,), focus=False):
771
772
773
776
777
778
780 _parent, _dir, _dirs, _files = vfsobj.walk()
781
782 self._dir = _dir
783
784 _entries = []
785 _entries.extend(_dirs)
786 _entries.extend(_files)
787 _entries = self._filter(_entries)
788 _entries = self._sort(_entries)
789 _entries.insert(0, _parent)
790
791 self._title = truncate(_dir.full_path, self.size.cols - 4,
792 self._enc, True)
793
794 if hasattr(self, "border"):
795 self.border.set_title(self._title)
796
797 self._tagged = []
798
799 self.entries = _entries
800 self._len = len(self.entries)
801 self._palettes = self._process_skin_rulesets()
802 self._vfsobj = vfsobj
803 self.vfs_driver = vfsobj.driver
804
805 self._force_reload = True
806
807
808
811
812
813
814 - def render(self, (maxcol,), focus=False):
815 """
816 Render block
817 """
818
819 w = self.display_widget((maxcol,), focus)
820 maxrow = w.rows((maxcol,), focus)
821
822
823 maxcol_orig, maxcol = maxcol, maxcol - 2
824 maxrow_orig, maxrow = maxrow, maxrow - 4
825
826
827 while True:
828 try:
829 _act = self._pending.pop()
830 except IndexError:
831 break
832 else:
833 _act(maxcol, maxrow)
834
835 if self._custom_info is not None:
836 self._set_custom_info(self._custom_info, maxcol)
837 else:
838 self._set_info(self.entries[self.selected], maxcol)
839
840 _tlen = len(self._tagged)
841
842 if _tlen > 0:
843 _text = _(u"%s bytes (%d)") % (
844 self._make_number_readable(
845 reduce(lambda x, y: x + y,
846 [self.entries[x].size for x in self._tagged
847 if isinstance(self.entries[x].ftype, VFSTypeFile)
848 ], 0)), _tlen)
849
850 self._sep.set_text(bstring(_text, self._enc),
851 self.attr(u"tagged"))
852 else:
853 self._sep.clear_text()
854
855 self._display = self._get_visible(maxrow, maxcol, self._force_reload)
856 self._force_reload = False
857
858 _len = len(self._display)
859
860 canvases = []
861
862 for i in xrange(0, _len):
863 _text = self._display[i]
864 _own_attr = None
865 _abs_i = self._from + i
866
867 if self._cursor_attr is not None and i == self._vindex:
868 _own_attr = self._cursor_attr
869 elif self.active and i == self._vindex:
870 _own_attr = self.attr(u"cursor")
871 elif _abs_i in self._tagged:
872 _own_attr = self.attr(u"tagged")
873 elif _abs_i in self._palettes:
874 _own_attr = self._palettes[_abs_i]
875
876 if _own_attr is not None:
877 x = lowui.AttrWrap(lowui.Text(bstring(_text, self._enc)),
878 _own_attr).render((maxcol,))
879 canvases.append((x, i, False))
880 else:
881 canvases.append((lowui.Text(_text).render((maxcol,)),
882 i, False))
883
884 if _len < maxrow:
885 _pad = lowui.AttrWrap(lowui.Text(" "), self.attr(u"panel"))
886 canvases.append((_pad.render((maxcol,), focus), 0, False))
887
888 _info = self._make_info()
889 self.frame.set_footer(_info)
890
891 combined = lowui.CanvasCombine(canvases)
892 border = self.block.render((maxcol_orig, maxrow_orig), focus)
893
894 if _len > maxrow:
895 combined.trim_end(_len - maxrow)
896
897 return lowui.CanvasOverlay(combined, border, 1, 1)
898
899
900
906
907
908
910 _res = []
911
912 i = 0
913 _sep = False
914
915 for x in reversed(unicode(num)):
916 if _sep:
917 _res.append(u"_")
918 _sep = False
919
920 _res.append(x)
921
922 if i > 0 and (i + 1) % 3 == 0:
923 _sep = True
924
925 i += 1
926
927 _res.reverse()
928
929 return u"".join(_res)
930
931
932
934 """
935 Get currently visible piece of entries
936 """
937
938 _len = self._len
939 _from, _to, self._vindex = self._update_vindex(rows)
940
941 if reload or ((_from, _to) != (self._from, self._to)):
942 self._from, self._to = _from, _to
943 self._display = []
944
945 for _obj in self.entries[self._from:self._to]:
946 _text = "%s%s "% (_obj.vtype, _obj.name)
947 _text = truncate(_text, cols, self._enc)
948 self._display.append(_text)
949
950 return self._display
951
952
953
955 """
956 Process defined fs.* rulesets
957 """
958
959 _result = {}
960
961 try:
962 _rules = self.xyz.skin[u"fs.rules"]
963 except KeyError:
964 return _result
965
966 for i in xrange(self._len):
967 for _exp, _attr in _rules.iteritems():
968 if _exp.match(self.entries[i]):
969 _result[i] = _attr.name
970 break
971
972 return _result
973
974
975
977 """
978 Return title attr
979 """
980
981 if self.active:
982 return self.attr(u"cwdtitle")
983 else:
984 return self.attr(u"cwdtitleinact")
985
986
987
989 """
990 Set info text
991 """
992
993 _part2 = vfsobj.info
994 _part1 = truncate(vfsobj.visual, cols - len(_part2) - 2, self._enc)
995
996 _text = u"%s%s%s" % (_part1,
997 u" " * (cols - (self.term_width(_part1) +
998 self.term_width(_part2)) -
999 1), _part2)
1000
1001 self._winfo.set_text(bstring(_text, self._enc))
1002
1003
1004
1006 """
1007 Set custom info text
1008 """
1009
1010 _text = truncate(custom_text, cols, self._enc, True)
1011 self._winfo.set_text(bstring(_text, self._enc))
1012
1013
1014
1016 """
1017 Calculate vindex according to selected position
1018 """
1019
1020 pos = self.selected
1021
1022 _from = pos / rows * rows
1023 _to = _from + rows
1024 _vindex = pos - (rows * (pos / rows))
1025
1026 return (_from, _to, _vindex)
1027
1028
1029
1030 @refresh
1038
1039
1040
1041 @refresh
1050
1051
1052
1053 @refresh
1055 """
1056 Next entry
1057 """
1058
1059 if self.selected < self._len - 1:
1060 self.selected += 1
1061
1062
1063
1064 @refresh
1066 """
1067 Previous entry
1068 """
1069
1070 if self.selected > 0:
1071 self.selected -= 1
1072
1073
1074
1075 @refresh
1077 """
1078 Top entry
1079 """
1080
1081 self.selected = 0
1082
1083
1084
1085 @refresh
1087 """
1088 Bottom entry
1089 """
1090
1091 self.selected = self._len - 1
1092
1093
1094
1095 @refresh
1097 """
1098 One block down
1099 """
1100
1101 def _do_next_block(cols, rows):
1102 if self.selected + rows >= self._len:
1103 return self.bottom()
1104 else:
1105 self.selected += rows
1106
1107
1108
1109
1110
1111
1112 self._pending.push(_do_next_block)
1113
1114
1115
1116 @refresh
1118 """
1119 One block up
1120 """
1121
1122 def _do_prev_block(cols, rows):
1123 if self.selected - rows < 0:
1124 return self.top()
1125 else:
1126 self.selected -= rows
1127
1128
1129
1130 self._pending.push(_do_prev_block)
1131
1132
1133
1135 """
1136 Get selected VFSObject instance
1137 """
1138
1139 return self.entries[self.selected]
1140
1141
1142
1144 """
1145 Get current VFSObject instance
1146 """
1147
1148 return self._vfsobj
1149
1150
1151
1153 """
1154 Return list of tagged VFSObject instances
1155 """
1156
1157 return [self.entries[x] for x in self._tagged]
1158
1159
1160
1162 """
1163 Return list of not tagged VFSObject instances
1164 """
1165
1166 return [self.entries[x] for x in xrange(self._len)
1167 if x not in self._tagged]
1168
1169
1170
1172 """
1173 Toggle tagged selected file
1174 """
1175
1176 if self.selected in self._tagged:
1177 self._tagged.remove(self.selected)
1178 else:
1179 self._tagged.append(self.selected)
1180
1181 self.next()
1182
1183
1184
1185 @refresh
1187 """
1188 Tag files by combined rule
1189 """
1190
1191 self._tag_rule(tag=True)
1192
1193
1194
1195 @refresh
1197 """
1198 Untag files by combined rule
1199 """
1200
1201 self._tag_rule(tag=False)
1202
1203
1204
1206 """
1207 Tag engine
1208 """
1209
1210 if tag:
1211 _title = _(u"Tag group")
1212 else:
1213 _title = _(u"Untag group")
1214
1215 _input = libxyz.ui.InputBox(self.xyz, self.xyz.top,
1216 _("Type FS Rule"),
1217 title=_title, text=self._rule_raw)
1218
1219 _raw = _input.show()
1220
1221 if _raw is None:
1222 return
1223 else:
1224 self._rule_raw = _raw
1225
1226 try:
1227 _rule = libxyz.core.FSRule(ustring(_raw, self._enc))
1228 except libxyz.exceptions.ParseError, e:
1229 xyzlog.error(ustring(str(e)))
1230 return
1231
1232 try:
1233 if tag:
1234 self._tagged = [i for i in xrange(self._len) if
1235 _rule.match(self.entries[i])]
1236 else:
1237 self._tagged = [i for i in self._tagged if not
1238 _rule.match(self.entries[i])]
1239 except libxyz.exceptions.FSRuleError, e:
1240 self._tagged = []
1241
1242 xyzlog.error(ustring(str(e)))
1243 return
1244
1245
1246
1247 @refresh
1249 """
1250 Invert currently tagged files
1251 """
1252
1253 self._tagged = [i for i in xrange(self._len)
1254 if i not in self._tagged]
1255
1256
1257
1258 @refresh
1260 """
1261 Tag every single object in current dir
1262 """
1263
1264 self._tagged = [i for i in xrange(self._len) if
1265 self.entries[i].name != ".."]
1266
1267
1268
1269 @refresh
1271 """
1272 Untag every single object in current dir
1273 """
1274
1275 self._tagged = []
1276
1277
1278
1279 @refresh
1281 """
1282 Reload contents
1283 """
1284
1285 _selected = self.entries[self.selected]
1286
1287 self._setup(self._vfsobj)
1288
1289 if self.selected >= self._len:
1290 self.selected = self._len - 1
1291
1292
1293 if self.entries[self.selected].name != _selected.name:
1294 self.select(_selected.name)
1295
1296
1297
1298 @refresh
1300 """
1301 Select VFS object by given name in current directory
1302 """
1303
1304 for i in xrange(self._len):
1305 if self.entries[i].name == name:
1306 self.selected = i
1307 break
1308
1309
1310
1312 """
1313 Perform action on selected file
1314 """
1315
1316 _selected = self.entries[self.selected]
1317
1318 _action = self.xyz.am.match(_selected)
1319
1320 if _action is not None:
1321 try:
1322 _action(_selected)
1323 except Exception, e:
1324 xyzlog.error(_(u"Action error: %s") % (ustring(str(e))))
1325
1326
1327
1328 @refresh
1329 - def chdir(self, path, reload=True, active=True):
1330 """
1331 Change directory
1332 If reload is not True only execute os.chdir, without reloading
1333 directory contents
1334 If active is False do not call os.chdir
1335 """
1336
1337 if reload:
1338 _path = os.path.normpath(path)
1339 _parent = None
1340 _old_vfs = None
1341
1342 if self.entries:
1343 _parent = os.path.normpath(self.entries[0].full_path)
1344 _old = self._dir.name
1345 _old_vfs = self._vfsobj
1346
1347 try:
1348 _vfsobj = self.xyz.vfs.dispatch(path, self._enc)
1349 except libxyz.exceptions.VFSError, e:
1350 xyzlog.error(_(u"Unable to chdir to %s: %s") %
1351 (ustring(path), ustring(e)))
1352 return
1353
1354 try:
1355 self._setup(_vfsobj)
1356 except libxyz.exceptions.XYZRuntimeError, e:
1357 xyzlog.info(_(u"Unable to chdir to %s: %s") %
1358 (ustring(path), ustring(e)))
1359 return
1360
1361 self.selected = 0
1362
1363
1364 if _parent == _path:
1365 for x in xrange(self._len):
1366 if self.entries[x].name == _old:
1367 self.selected = x
1368 break
1369
1370
1371 if _old_vfs:
1372 del(_old_vfs)
1373
1374 self.cwd = path
1375
1376
1377 if isinstance(self._vfsobj, LocalVFSObject) and active:
1378 os.chdir(path)
1379
1380
1381
1382 @refresh
1384 """
1385 Search forward for matching object while user types
1386 """
1387
1388 return self._search_engine(lambda x: (xrange(x, self._len)))
1389
1390
1391
1392 @refresh
1394 """
1395 Search backward for matching object while user types
1396 """
1397
1398 return self._search_engine(lambda x: (xrange(x, 0, -1)))
1399
1400
1401
1403 """
1404 Search from current position downwards and then from top to
1405 currently selected
1406 """
1407
1408 return self._search_engine(lambda x: range(x, self._len) +
1409 range(0, x))
1410
1411
1412
1413 @refresh
1415 """
1416 Show only tagged entries
1417 """
1418
1419 if not self._tagged:
1420 return
1421
1422 self.entries = [self.entries[x] for x in self._tagged]
1423 self._len = len(self.entries)
1424 self.selected = 0
1425 self._tagged = []
1426 self._palettes = self._process_skin_rulesets()
1427
1428 _tagged = _(u"TAGGED")
1429
1430 if not self._title.endswith(_tagged):
1431 self._title = truncate(u"%s:%s" % (self._title, _tagged),
1432 self.size.cols - 4, self._enc, True)
1433
1434 if hasattr(self, "border"):
1435 self.border.set_title(self._title)
1436
1437 self._force_reload = True
1438
1439
1440
1442 """
1443 Search for matching filenames while user types
1444 @param order: A function that returns generator for search order
1445 @param pattern: A search type pattern
1446 """
1447
1448 self._cursor_attr = self.attr(u"search")
1449
1450 if pattern is None:
1451
1452 pattern = lambda pat, obj: ustring(pat) in ustring(obj)
1453
1454
1455
1456 _dim = self.xyz.screen.get_cols_rows()
1457 _collected = []
1458
1459 _current_pos = self.selected
1460 _current_pos_orig = self.selected
1461 _skip = False
1462
1463
1464 while True:
1465 self._custom_info = u"".join(_collected)
1466
1467 self._invalidate()
1468 self.xyz.screen.draw_screen(_dim, self.xyz.top.render(_dim, True))
1469
1470 try:
1471 _raw = self.xyz.input.get()
1472
1473 if self.xyz.input.WIN_RESIZE in _raw:
1474 _dim = self.xyz.screen.get_cols_rows()
1475 continue
1476
1477 if self._keys.ESCAPE in _raw or self._keys.ENTER in _raw:
1478 self._invalidate()
1479 break
1480 elif self._keys.BACKSPACE in _raw:
1481 _current_pos = _current_pos_orig
1482 if _collected:
1483 _collected.pop()
1484
1485 elif self._keys.DOWN in _raw:
1486 _skip = True
1487
1488 _tmp = _collected[:]
1489 _tmp.extend([ustring(x, self._enc) for x in _raw
1490 if len(x) == 1])
1491 _pattern = u"".join(_tmp)
1492 except Exception:
1493 break
1494
1495
1496 for i in order(_current_pos):
1497 if pattern(_pattern, self.entries[i].name):
1498 if _skip:
1499 _skip = False
1500 _current_pos = i + 1
1501 continue
1502
1503 self.selected = i
1504 _collected = _tmp
1505 break
1506
1507 self._cursor_attr = None
1508 self._custom_info = None
1509