exit from pyview by ESC
[lfm.git] / lfm / messages.py
blob90c550cd1cf6a0d0928cc2393fa10ec0cfd5b4dc
1 # -*- coding: utf-8 -*-
3 """messages.py
5 This module contains some windows for lfm use.
6 """
9 import sys
10 import os.path
11 import curses
12 import curses.panel
13 import files
16 ######################################################################
17 ##### module variables
18 app = None
19 historic = []
20 HISTORIC_MAXLEN = 100
23 ######################################################################
24 class CommonWindow:
25 """A superclass for 'error' and 'win' windows"""
27 def __init__(self, title, text, br_att, br_bg, bd_att, bd_bg, waitkey = 1):
28 self.waitkey = waitkey
29 text = text.replace('\t', ' ' * 4)
30 lines = text.split('\n')
31 length = max(map(len, lines))
32 if self.waitkey:
33 w = min(max(max(length+6, 27+4), len(title)+6), app.maxw-2)
34 if length < 27 + 4 and len(title) <= length:
35 w -= 1
36 else:
37 w = min(max(length+6, len(title)+6), app.maxw-2)
38 if len(title) < length:
39 w -= 1
40 h = len(lines) + 4
41 for l in lines:
42 h += int((len(l)+1) / (app.maxw-2))
43 if h > app.maxh - 4:
44 h = app.maxh - 4
45 text = ''.join([l+'\n' for l in lines[-5:]])
46 try:
47 win = curses.newwin(h, w,
48 int((app.maxh-h)/2), int((app.maxw-w)/2))
49 self.pwin = curses.panel.new_panel(win)
50 self.pwin.top()
51 except curses.error:
52 print 'Can\'t create window'
53 sys.exit(-1)
54 win.bkgd(br_bg, br_att)
55 win.erase()
56 win.box(0, 0)
57 if len(title) > app.maxw-14:
58 title = title[:app.maxw-10] + '...' + '\''
59 win.addstr(0, int((w-len(title)-2)/2), ' %s ' % title, curses.A_BOLD)
60 if h == 5:
61 win.addstr(2, 2, text, bd_att)
62 else:
63 win.addstr(2, 1, text, bd_att)
64 if self.waitkey:
65 win.addstr(h-1, int((w-27)/2), ' Press any key to continue ', br_att)
66 win.refresh()
67 win.keypad(1)
69 def run(self):
70 if self.waitkey:
71 while not self.pwin.window().getch():
72 pass
73 self.pwin.hide()
76 class FixSizeCommonWindow:
77 """A superclass for messages, with fixed size"""
79 def __init__(self, title, text, downtext,
80 br_att, br_bg, bd_att, bd_bg, waitkey = 1):
81 self.waitkey = waitkey
82 text = text.replace('\t', ' ' * 4)
83 w = app.maxw - 20
84 if len(title) > w - 4:
85 title = title[:w-4]
86 if len(text) > w - 4:
87 text = text[:w-5]
88 if len(downtext) > w - 4:
89 downtext = downtext[:w-4]
90 h = 5
91 try:
92 win = curses.newwin(h, w,
93 int((app.maxh-h)/2), int((app.maxw-w)/2))
94 self.pwin = curses.panel.new_panel(win)
95 self.pwin.top()
96 except curses.error:
97 print 'Can\'t create window'
98 sys.exit(-1)
99 win.bkgd(br_bg, br_att)
100 win.erase()
101 win.box(0, 0)
102 if len(title) > app.maxw - 14:
103 title = title[:app.maxw-10] + '...' + '\''
104 win.addstr(0, int((w-len(title)-2)/2), ' %s ' % title, curses.A_BOLD)
105 if h == 5:
106 win.addstr(2, 2, text, bd_att)
107 else:
108 win.addstr(2, 1, text, bd_att)
109 if self.waitkey:
110 win.addstr(h-1, int((w-27)/2),
111 ' Press any key to continue ', br_att)
112 elif downtext:
113 win.addstr(h-1, int((w-len(downtext)-2)/2),
114 ' %s ' % downtext, br_att)
115 win.refresh()
116 win.keypad(1)
118 def run(self):
119 if self.waitkey:
120 while not self.pwin.window().getch():
121 pass
122 self.pwin.hide()
125 class FixSizeProgressBarWindow:
126 """Like FixSizeCommonWindow but with a ProgressBar"""
128 def __init__(self, title, text, downtext, percent,
129 bd_att, bd_bg, pb_att, pb_bg, waitkey = 1):
130 title = title[:app.maxw-14]
131 text = text[:app.maxw-14]
132 self.waitkey = waitkey
133 text = text.replace('\t', ' ' * 4)
134 w = app.maxw - 20
135 if len(title) > w - 4:
136 title = title[:w-4]
137 if len(text) > w - 4:
138 text = text[:w-5]
139 if len(downtext) > w - 4:
140 downtext = downtext[:w-4]
141 h = 7
142 self.h, self.w = h, w
143 self.bd_att, self.pb_att = bd_att, pb_att
144 try:
145 win = curses.newwin(h, w,
146 int((app.maxh-h)/2), int((app.maxw-w)/2))
147 self.progressbar = curses.newpad(1, w-11+1)
148 self.pwin = curses.panel.new_panel(win)
149 self.pwin.top()
150 except curses.error:
151 print 'Can\'t create window'
152 sys.exit(-1)
153 win.bkgd(bd_bg, bd_att)
154 self.progressbar.bkgd(curses.ACS_CKBOARD, pb_bg)
155 win.erase()
156 win.keypad(1)
158 def show(self, title, text, downtext, percent):
159 self.pwin.window().erase()
160 self.pwin.window().box(0, 0)
161 if len(title) > app.maxw - 14:
162 title = title[:app.maxw-10] + '...' + '\''
163 self.pwin.window().addstr(0, int((self.w-len(title)-2)/2),
164 ' %s ' % title, curses.A_BOLD)
165 self.pwin.window().addstr(2, 2, text)
166 w1 = percent * (self.w-11) / 100
167 self.progressbar.erase()
168 self.progressbar.addstr(0, 0, ' ' * w1, self.pb_att | curses.A_BOLD)
169 self.pwin.window().addstr(self.h-3, self.w-8, '[%3d%%]' % percent)
170 if self.waitkey:
171 self.pwin.window().addstr(self.h-1, int((self.w-27)/2),
172 ' Press any key to continue ')
173 elif downtext:
174 self.pwin.window().addstr(self.h-1, int((self.w-len(downtext)-2)/2),
175 ' %s ' % downtext)
176 self.pwin.window().refresh()
177 y0 = int((app.maxh-self.h)/2) + 4
178 x0 = int((app.maxw-self.w)/2) + 2
179 self.progressbar.refresh(0, 0, y0, x0, y0+1, x0+self.w-12)
180 self.pwin.window().refresh()
183 ######################################################################
184 def error(title, msg = '', file = ''):
185 """show an error window"""
187 if file == '':
188 buf = msg
189 else:
190 buf = '%s: %s' % (file, msg)
191 CommonWindow(title, buf,
192 curses.color_pair(8),
193 curses.color_pair(8),
194 curses.color_pair(7) | curses.A_BOLD,
195 curses.color_pair(7)).run()
198 ######################################################################
199 def win(title, text):
200 """show a message window and wait for a key"""
202 CommonWindow(title, text,
203 curses.color_pair(1), curses.color_pair(1),
204 curses.color_pair(4), curses.color_pair(4)).run()
207 def win_nokey(title, text, downtext = ''):
208 """show a message window, does not wait for a key"""
210 FixSizeCommonWindow(title, text, downtext,
211 curses.color_pair(1), curses.color_pair(1),
212 curses.color_pair(1), curses.color_pair(1),
213 waitkey = 0).run()
216 ######################################################################
217 def notyet(title):
218 """show a not-yet-implemented message"""
220 CommonWindow(title,
221 'Sorry, but this function\n is not implemented yet!',
222 curses.color_pair(1) | curses.A_BOLD, curses.color_pair(1),
223 curses.color_pair(4), curses.color_pair(4)).run()
226 ######################################################################
227 def get_a_key(title, question):
228 """show a window returning key pressed"""
230 question = question.replace('\t', ' ' * 4)
231 lines = question.split('\n')
232 length = max(map(len, lines))
233 h = min(len(lines)+4, app.maxh-2)
234 w = min(length+4, app.maxw-2)
235 try:
236 win = curses.newwin(h, w, int((app.maxh-h)/2), int((app.maxw-w)/2))
237 pwin = curses.panel.new_panel(win)
238 pwin.top()
239 except curses.error:
240 print 'Can\'t create window'
241 sys.exit(-1)
242 win.bkgd(curses.color_pair(1))
244 win.erase()
245 win.box(0, 0)
246 win.addstr(0, int((w-len(title)-2)/2), ' %s' % title,
247 curses.color_pair(1) | curses.A_BOLD)
248 for row, l in enumerate(lines):
249 win.addstr(row+2, 2, l)
250 win.refresh()
251 win.keypad(1)
252 while True:
253 ch = win.getch()
254 if ch in (0x03, 0x1B): # Ctrl-C, ESC
255 pwin.hide()
256 return -1
257 elif 0x01 <= ch <= 0xFF:
258 pwin.hide()
259 return ch
260 else:
261 curses.beep()
264 ######################################################################
265 def confirm(title, question, default = 0):
266 """show a yes/no window, returning 1/0"""
268 h = 5
269 w = min(max(34, len(question)+5), app.maxw-2)
270 try:
271 win = curses.newwin(h, w, int((app.maxh-h)/2), int((app.maxw-w)/2))
272 pwin = curses.panel.new_panel(win)
273 pwin.top()
274 except curses.error:
275 print 'Can\'t create window'
276 sys.exit(-1)
277 win.bkgd(curses.color_pair(1))
279 win.erase()
280 win.box(0, 0)
281 win.addstr(0, int((w-len(title)-2)/2), ' %s' % title.capitalize(),
282 curses.color_pair(1) | curses.A_BOLD)
283 win.addstr(1, 2 , '%s?' % question)
284 win.refresh()
286 row = int((app.maxh-h)/2) + 3
287 col = int((app.maxw-w)/2)
288 col1 = col + int(w/5) + 1
289 col2 = col + int(w*4/5) - 6
290 win.keypad(1)
291 answer = default
292 while True:
293 if answer == 1:
294 attr_yes = curses.color_pair(9) | curses.A_BOLD
295 attr_no = curses.color_pair(1) | curses.A_BOLD
296 else:
297 attr_yes = curses.color_pair(1) | curses.A_BOLD
298 attr_no = curses.color_pair(9) | curses.A_BOLD
299 btn = curses.newpad(1, 8)
300 btn.addstr(0, 0, '[ Yes ]', attr_yes)
301 btn.refresh(0, 0, row, col1, row + 1, col1 + 6)
302 btn = curses.newpad(1, 7)
303 btn.addstr(0, 0, '[ No ]', attr_no)
304 btn.refresh(0, 0, row, col2, row + 1, col2 + 5)
306 ch = win.getch()
307 if ch in (curses.KEY_UP, curses.KEY_DOWN, curses.KEY_LEFT,
308 curses.KEY_RIGHT, 9):
309 answer = not answer
310 elif ch in (ord('Y'), ord('y')):
311 answer = 1
312 break
313 elif ch in (ord('N'), ord('n')):
314 answer = 0
315 break
316 elif ch in (0x03, 0x1B): # Ctrl-C, ESC
317 answer = -1
318 break
319 elif ch in (10, 13): # enter
320 break
321 else:
322 curses.beep()
324 pwin.hide()
325 return answer
328 ######################################################################
329 def confirm_all(title, question, default = 0):
330 """show a yes/all/no/stop window, returning 1/2/0/-1"""
332 h = 5
333 w = min(max(45, len(question)+5), app.maxw-2)
334 try:
335 win = curses.newwin(h, w, int((app.maxh-h)/2), int((app.maxw-w)/2))
336 pwin = curses.panel.new_panel(win)
337 pwin.top()
338 except curses.error:
339 print 'Can\'t create window'
340 sys.exit(-1)
341 win.bkgd(curses.color_pair(1))
343 win.erase()
344 win.box(0, 0)
345 win.addstr(0, int((w-len(title)-2)/2), ' %s ' % title,
346 curses.color_pair(1) | curses.A_BOLD)
347 win.addstr(1, 2 , '%s?' % question)
348 win.refresh()
350 row = int((app.maxh-h) / 2) + 3
351 col = int((app.maxw-w) / 2)
352 x = (w-28) / 5
353 col1 = col + x + 1
354 col2 = col1 + 7 + x
355 col3 = col2 + 7 + x
356 col4 = col3 + 6 + x
358 win.keypad(1)
359 answer = default
360 while True:
361 if answer == 1:
362 attr_yes = curses.color_pair(9) | curses.A_BOLD
363 attr_all = curses.color_pair(1) | curses.A_BOLD
364 attr_no = curses.color_pair(1) | curses.A_BOLD
365 attr_skipall = curses.color_pair(1) | curses.A_BOLD
366 elif answer == 2:
367 attr_yes = curses.color_pair(1) | curses.A_BOLD
368 attr_all = curses.color_pair(9) | curses.A_BOLD
369 attr_no = curses.color_pair(1) | curses.A_BOLD
370 attr_skipall = curses.color_pair(1) | curses.A_BOLD
371 elif answer == 0:
372 attr_yes = curses.color_pair(1) | curses.A_BOLD
373 attr_all = curses.color_pair(1) | curses.A_BOLD
374 attr_no = curses.color_pair(9) | curses.A_BOLD
375 attr_skipall = curses.color_pair(1) | curses.A_BOLD
376 elif answer == -1:
377 attr_yes = curses.color_pair(1) | curses.A_BOLD
378 attr_all = curses.color_pair(1) | curses.A_BOLD
379 attr_no = curses.color_pair(1) | curses.A_BOLD
380 attr_skipall = curses.color_pair(9) | curses.A_BOLD
381 else:
382 raise ValueError
383 btn = curses.newpad(1, 8)
384 btn.addstr(0, 0, '[ Yes ]', attr_yes)
385 btn.refresh(0, 0, row, col1, row + 1, col1 + 6)
386 btn = curses.newpad(1, 8)
387 btn.addstr(0, 0, '[ All ]', attr_all)
388 btn.refresh(0, 0, row, col2, row + 1, col2 + 6)
389 btn = curses.newpad(1, 7)
390 btn.addstr(0, 0, '[ No ]', attr_no)
391 btn.refresh(0, 0, row, col3, row + 1, col3 + 5)
392 btn = curses.newpad(1, 15)
393 btn.addstr(0, 0, '[ Stop ]', attr_skipall)
394 btn.refresh(0, 0, row, col4, row + 1, col4 + 7)
396 ch = win.getch()
397 if ch in (curses.KEY_UP, curses.KEY_DOWN, curses.KEY_LEFT,
398 curses.KEY_RIGHT, 9):
399 if answer == 1:
400 answer = 2
401 elif answer == 2:
402 answer = 0
403 elif answer == 0:
404 answer = -1
405 elif answer == -1:
406 answer = 1
407 else:
408 raise ValueError
409 elif ch in (ord('Y'), ord('y')):
410 answer = 1
411 break
412 elif ch in (ord('A'), ord('a')):
413 answer = 2
414 break
415 elif ch in (ord('N'), ord('n')):
416 answer = 0
417 break
418 elif ch in (ord('S'), ord('s'), 0x03, 0x1B): # Ctrl-C, ESC
419 answer = -1
420 break
421 elif ch in (10, 13): # enter
422 break
423 else:
424 curses.beep()
426 pwin.hide()
427 return answer
430 ######################################################################
431 def confirm_all_none(title, question, default = 0):
432 """show a yes/all/no/none/stop window, returning 1/2/0/-2/-1"""
434 h = 5
435 w = min(max(50, len(question)+5), app.maxw-2)
436 try:
437 win = curses.newwin(h, w, int((app.maxh-h)/2), int((app.maxw-w)/2))
438 pwin = curses.panel.new_panel(win)
439 pwin.top()
440 except curses.error:
441 print 'Can\'t create window'
442 sys.exit(-1)
443 win.bkgd(curses.color_pair(1))
445 win.erase()
446 win.box(0, 0)
447 win.addstr(0, int((w-len(title)-2)/2), ' %s ' % title,
448 curses.color_pair(1) | curses.A_BOLD)
449 win.addstr(1, 2 , '%s?' % question)
450 win.refresh()
452 row = int((app.maxh-h) / 2) + 3
453 col = int((app.maxw-w) / 2)
454 x = (w-36) / 6
455 col1 = col + x + 1
456 col2 = col1 + 7 + x
457 col3 = col2 + 7 + x
458 col4 = col3 + 6 + x
459 col5 = col4 + 8 + x
461 win.keypad(1)
462 answer = default
463 while True:
464 if answer == 1:
465 attr_yes = curses.color_pair(9) | curses.A_BOLD
466 attr_all = curses.color_pair(1) | curses.A_BOLD
467 attr_no = curses.color_pair(1) | curses.A_BOLD
468 attr_none = curses.color_pair(1) | curses.A_BOLD
469 attr_skipall = curses.color_pair(1) | curses.A_BOLD
470 elif answer == 2:
471 attr_yes = curses.color_pair(1) | curses.A_BOLD
472 attr_all = curses.color_pair(9) | curses.A_BOLD
473 attr_no = curses.color_pair(1) | curses.A_BOLD
474 attr_none = curses.color_pair(1) | curses.A_BOLD
475 attr_skipall = curses.color_pair(1) | curses.A_BOLD
476 elif answer == 0:
477 attr_yes = curses.color_pair(1) | curses.A_BOLD
478 attr_all = curses.color_pair(1) | curses.A_BOLD
479 attr_no = curses.color_pair(9) | curses.A_BOLD
480 attr_none = curses.color_pair(1) | curses.A_BOLD
481 attr_skipall = curses.color_pair(1) | curses.A_BOLD
482 elif answer == -2:
483 attr_yes = curses.color_pair(1) | curses.A_BOLD
484 attr_all = curses.color_pair(1) | curses.A_BOLD
485 attr_no = curses.color_pair(1) | curses.A_BOLD
486 attr_none = curses.color_pair(9) | curses.A_BOLD
487 attr_skipall = curses.color_pair(1) | curses.A_BOLD
488 elif answer == -1:
489 attr_yes = curses.color_pair(1) | curses.A_BOLD
490 attr_all = curses.color_pair(1) | curses.A_BOLD
491 attr_no = curses.color_pair(1) | curses.A_BOLD
492 attr_none = curses.color_pair(1) | curses.A_BOLD
493 attr_skipall = curses.color_pair(9) | curses.A_BOLD
494 else:
495 raise ValueError
496 btn = curses.newpad(1, 8)
497 btn.addstr(0, 0, '[ Yes ]', attr_yes)
498 btn.refresh(0, 0, row, col1, row + 1, col1 + 6)
499 btn = curses.newpad(1, 8)
500 btn.addstr(0, 0, '[ All ]', attr_all)
501 btn.refresh(0, 0, row, col2, row + 1, col2 + 6)
502 btn = curses.newpad(1, 7)
503 btn.addstr(0, 0, '[ No ]', attr_no)
504 btn.refresh(0, 0, row, col3, row + 1, col3 + 5)
505 btn = curses.newpad(1, 9)
506 btn.addstr(0, 0, '[ NOne ]', attr_none)
507 btn.refresh(0, 0, row, col4, row + 1, col4 + 7)
508 btn = curses.newpad(1, 9)
509 btn.addstr(0, 0, '[ Stop ]', attr_skipall)
510 btn.refresh(0, 0, row, col5, row + 1, col5 + 7)
512 ch = win.getch()
513 if ch in (curses.KEY_UP, curses.KEY_DOWN, curses.KEY_LEFT,
514 curses.KEY_RIGHT, 9):
515 if answer == 1:
516 answer = 2
517 elif answer == 2:
518 answer = 0
519 elif answer == 0:
520 answer = -2
521 elif answer == -2:
522 answer = -1
523 elif answer == -1:
524 answer = 1
525 else:
526 raise ValueError
527 elif ch in (ord('Y'), ord('y')):
528 answer = 1
529 break
530 elif ch in (ord('A'), ord('a')):
531 answer = 2
532 break
533 elif ch in (ord('N'), ord('n')):
534 answer = 0
535 break
536 elif ch in (ord('O'), ord('o')):
537 answer = -2
538 break
539 elif ch in (ord('S'), ord('s'), 0x03, 0x1B): # Ctrl-C, ESC
540 answer = -1
541 break
542 elif ch in (10, 13): # enter
543 break
544 else:
545 curses.beep()
547 pwin.hide()
548 return answer
551 ######################################################################
552 class Yes_No_Buttons:
553 """Yes/No buttons"""
555 def __init__(self, w, h, d):
556 self.row = int((app.maxh-h) / 2) + 4 + d
557 col = int((app.maxw-w) / 2)
558 self.col1 = col + int(w/5) + 1
559 self.col2 = col + int(w*4/5) - 6
560 self.active = 0
563 def show(self):
564 if self.active == 0:
565 attr1 = curses.color_pair(1) | curses.A_BOLD
566 attr2 = curses.color_pair(1) | curses.A_BOLD
567 elif self.active == 1:
568 attr1 = curses.color_pair(9) | curses.A_BOLD
569 attr2 = curses.color_pair(1) | curses.A_BOLD
570 else:
571 attr1 = curses.color_pair(1) | curses.A_BOLD
572 attr2 = curses.color_pair(9) | curses.A_BOLD
573 btn = curses.newpad(1, 8)
574 btn.addstr(0, 0, '[<Yes>]', attr1)
575 btn.refresh(0, 0, self.row, self.col1, self.row + 1, self.col1 + 6)
576 btn = curses.newpad(1, 7)
577 btn.addstr(0, 0, '[ No ]', attr2)
578 btn.refresh(0, 0, self.row, self.col2, self.row + 1, self.col2 + 5)
581 def manage_keys(self):
582 tmp = curses.newpad(1, 1)
583 while True:
584 ch = tmp.getch()
585 if ch in (0x03, 0x1B): # Ctrl-C, ESC
586 return -1
587 elif ch == ord('\t'):
588 return ch
589 elif ch in (10, 13): # enter
590 if self.active == 1:
591 return 10
592 else:
593 return -1
594 else:
595 curses.beep()
598 ######################################################################
599 class EntryLine:
600 """An entry line to enter a dir. or file, a pattern, etc"""
602 def __init__(self, w, h, x, y, path, with_historic, with_complete,
603 panelpath):
604 self.enc = app.prefs.settings['encoding']
605 try:
606 self.entry = curses.newwin(1, w-4+1, x, y)
607 except curses.error:
608 print 'Can\'t create window'
609 sys.exit(-1)
610 self.entry.attrset(curses.color_pair(11) | curses.A_BOLD)
611 self.entry.keypad(1)
613 self.buf = ""
615 self.entry_width = w - 4
616 self.text = path
617 self.panelpath = panelpath
618 self.pos = len(self.utext())
619 self.ins = True
621 self.with_complete = with_complete
622 self.with_historic = with_historic
623 if self.with_historic:
624 self.historic = historic[:]
625 self.historic_i = len(self.historic)
627 def utext(self):
628 """ decoded self.text with respection to encoding """
629 return self.text.decode(self.enc)
631 def bpos(self):
632 """ byte pos in self.text for decoded self.text """
633 return len(self.utext()[:self.pos].encode(self.enc))
635 def show(self):
636 text = self.text
637 pos = self.pos
638 ew = self.entry_width
639 ltext = len(self.utext())
640 if pos < ew:
641 relpos = pos
642 if ltext < ew:
643 textstr = text + ' ' * (ew - ltext)
644 else:
645 textstr = text.decode(self.enc)[:ew].encode(self.enc)
646 else:
647 if pos > ltext - (ew-1):
648 relpos = ew - 1 - (ltext - pos)
649 textstr = text.decode(self.enc)[ltext-ew+1:].encode(self.enc) + ' '
650 else:
651 relpos = pos - int(pos/ew)*ew
652 textstr = text.decode(self.enc)[int(pos/ew)*ew:int(pos/ew)*ew+ew].encode(self.enc)
653 self.entry.bkgd(curses.color_pair(1))
654 self.entry.erase()
655 self.entry.addstr(textstr.decode(self.enc)[:ew].encode(self.enc), curses.color_pair(11) | curses.A_BOLD)
656 self.entry.move(0, relpos)
657 self.entry.refresh()
660 def manage_keys(self):
661 while True:
662 self.show()
663 ch = self.entry.getch()
664 # print 'key: \'%s\' <=> %c <=> 0x%X <=> %d' % \
665 # (curses.keyname(ch), ch & 255, ch, ch)
666 if ch in (0x03, 0x1B): # Ctrl-C, ESC
667 return -1
668 elif ch == curses.KEY_UP:
669 if self.with_historic:
670 if self.historic_i > 0:
671 if self.historic_i == len(self.historic):
672 if self.text == None:
673 self.text = ''
674 self.historic.append(self.text)
675 self.historic_i -= 1
676 self.text = self.historic[self.historic_i]
677 self.pos = len(self.text)
678 else:
679 continue
680 elif ch == curses.KEY_DOWN:
681 if self.with_historic:
682 if self.historic_i < len(self.historic) - 1:
683 self.historic_i += 1
684 self.text = self.historic[self.historic_i]
685 self.pos = len(self.text)
686 else:
687 continue
688 elif ch == 0x14: # Ctrl-T
689 return ord('\t')
690 elif ch in (10, 13): # enter
691 return 10
692 elif ch == ord('\t'): # tab
693 if self.with_complete:
694 entries = files.complete(self.text, self.panelpath)
695 if not entries:
696 curses.beep()
697 continue
698 elif len(entries) == 1:
699 selected = entries.pop()
700 else:
701 y, x = self.entry.getbegyx()
702 selected = SelectItem(entries, y + 1, x - 2).run()
703 app.display()
704 cursor_show2()
705 if selected != -1:
706 self.text = files.join(self.text, selected)
707 self.pos = len(self.text)
708 return 0x14
709 else:
710 continue
711 # chars and edit keys
712 elif ch == 0x17: # Ctrl-W
713 if self.text == None or self.text == '':
714 continue
715 text = self.text
716 if text == os.sep:
717 text = ''
718 else:
719 if text[len(text)-1] == os.sep:
720 text = os.path.dirname(text)
721 text = os.path.dirname(text)
722 if text != '' and text != os.sep:
723 text += os.sep
724 self.text = text
725 self.pos = len(self.text)
726 elif ch == 0x04: # Ctrl-D
727 if self.text == None or self.text == '':
728 continue
729 text = ''
730 self.text = text
731 self.pos = len(self.text)
732 elif ch == curses.KEY_IC: # insert
733 self.ins = not self.ins
734 elif ch in (curses.KEY_HOME, 0x01): # home
735 self.pos = 0
736 elif ch in (curses.KEY_END, 0x05): # end
737 self.pos = len(self.utext())
738 elif ch == curses.KEY_LEFT and self.pos > 0:
739 self.pos -= 1
740 elif ch == curses.KEY_RIGHT and self.pos < len(self.utext()):
741 self.pos += 1
742 elif ch in (8, 127, curses.KEY_BACKSPACE) and len(self.utext()) > 0 and \
743 self.pos > 0: # backspace
744 utext = self.utext()
745 utext = utext[:self.pos-1] + utext[self.pos:]
746 self.text = utext.encode(self.enc)
747 self.pos -= 1
748 elif ch == curses.KEY_DC and self.pos < len(self.text): # del
749 utext = self.utext()
750 utext = utext[:self.pos] + utext[self.pos+1:]
751 self.text = utext.encode(self.enc)
752 elif len(self.text) < 255 and 32 <= ch <= 255:
753 self.buf += chr(ch)
754 try:
755 uchar = self.buf.decode(self.enc)
756 except UnicodeDecodeError:
757 return
758 self.buf = ""
759 bpos = self.bpos()
761 if self.ins:
762 self.text = self.text[:bpos] + uchar.encode(self.enc) + self.text[bpos:]
763 else:
764 self.text = self.text[:bpos] + uchar.encode(self.enc) + self.text[bpos+1:]
766 self.pos += 1
767 else:
768 curses.beep()
771 ######################################################################
772 class Entry:
773 """An entry window to enter a dir. or file, a pattern, ..."""
775 def __init__(self, title, help, path = '', with_historic = 1,
776 with_complete = 1, panelpath = ''):
777 h = 6
778 w = min(max(34, len(help)+5), app.maxw-2)
779 try:
780 win = curses.newwin(h, w, int((app.maxh-h)/2), int((app.maxw-w)/2))
781 self.entry = EntryLine(w, h,
782 int((app.maxh-h)/2)+2, int((app.maxw-w+4)/2),
783 path, with_historic, with_complete,
784 panelpath)
785 self.btns = Yes_No_Buttons(w, h, 0)
786 self.pwin = curses.panel.new_panel(win)
787 self.pwin.top()
788 except curses.error:
789 print 'Can\'t create window'
790 sys.exit(-1)
791 win.bkgd(curses.color_pair(1))
792 win.erase()
793 win.box(0, 0)
794 win.addstr(1, 2 , '%s:' % help)
795 win.addstr(0, int((w-len(title)-2)/2), ' %s ' % title,
796 curses.color_pair(1) | curses.A_BOLD)
797 win.refresh()
799 self.with_historic = with_historic
800 self.active_widget = self.entry
803 def run(self):
804 self.entry.entry.refresh() # needed to avoid a problem with blank paths
805 self.entry.show()
806 self.btns.show()
807 cursor_show2()
809 quit = False
810 while not quit:
811 self.btns.show()
812 ans = self.active_widget.manage_keys()
813 if ans == -1: # Ctrl-C
814 quit = True
815 answer = None
816 elif ans == ord('\t'): # tab
817 if self.active_widget == self.entry:
818 self.active_widget = self.btns
819 self.btns.active = 1
820 cursor_hide()
821 answer = self.entry.text
822 elif self.active_widget == self.btns and self.btns.active == 1:
823 self.btns.active = 2
824 cursor_hide()
825 answer = None
826 else:
827 self.active_widget = self.entry
828 self.btns.active = 0
829 cursor_show2()
830 elif ans == 0x14: # Ctrl-T
831 # this is a hack, we need to return to refresh Entry
832 return [self.entry.text]
833 elif ans == 10: # return values
834 quit = True
835 answer = self.entry.text
837 cursor_hide()
838 if answer:
839 # save new historic entries
840 if self.with_historic:
841 if self.entry.text and self.entry.text != '*':
842 if len(historic) < HISTORIC_MAXLEN:
843 historic.append(self.entry.text)
844 else:
845 historic.reverse()
846 historic.pop()
847 historic.reverse()
848 historic.append(self.entry.text)
849 self.pwin.hide()
850 return answer
853 ######################################################################
854 class DoubleEntry:
855 """An entry window to enter 2 dirs. or files, patterns, ..."""
857 def __init__(self, title, help1 = '', path1 = '',
858 with_historic1 = 1, with_complete1 = 1, panelpath1 = '',
859 help2 = '', path2 = '',
860 with_historic2 = 1, with_complete2 = 1, panelpath2 = '',
861 active_entry = 0):
862 h = 9
863 w = min(max(34, max(len(help1), len(help2))+5), app.maxw-2)
864 try:
865 win = curses.newwin(h, w, int((app.maxh-h)/2)-1, int((app.maxw-w)/2))
866 self.entry1 = EntryLine(w, h,
867 int((app.maxh-h)/2) + 1,
868 int((app.maxw-w+4) / 2),
869 path1, with_historic1, with_complete1,
870 panelpath1)
871 self.entry2 = EntryLine(w, h,
872 int((app.maxh-h)/2) + 4,
873 int((app.maxw-w+4) / 2),
874 path2, with_historic2, with_complete2,
875 panelpath2)
876 self.btns = Yes_No_Buttons(w, h, 2)
877 self.pwin = curses.panel.new_panel(win)
878 self.pwin.top()
879 except curses.error:
880 print 'Can\'t create window'
881 sys.exit(-1)
882 win.bkgd(curses.color_pair(1))
883 win.erase()
884 win.box(0, 0)
885 win.addstr(1, 2 , '%s:' % help1)
886 win.addstr(4, 2 , '%s:' % help2)
887 win.addstr(0, int((w-len(title)-2)/2), ' %s ' % title,
888 curses.color_pair(1) | curses.A_BOLD)
889 win.refresh()
891 self.with_historic = with_historic1 or with_historic2
892 self.active_entry_i = active_entry
893 if self.active_entry_i == 0:
894 self.active_entry = self.entry1
895 else:
896 self.active_entry = self.entry2
899 def run(self):
900 # needed to avoid a problem with blank paths
901 self.entry1.entry.refresh()
902 self.entry2.entry.refresh()
903 self.entry1.show()
904 self.entry2.show()
905 self.btns.show()
906 cursor_show2()
908 answer = True
909 quit = False
910 while not quit:
911 self.btns.show()
912 if self.active_entry_i in [0, 1]:
913 ans = self.active_entry.manage_keys()
914 else:
915 ans = self.btns.manage_keys()
916 if ans == -1: # Ctrl-C
917 quit = True
918 answer = False
919 elif ans == ord('\t'): # tab
920 self.active_entry_i += 1
921 if self.active_entry_i > 3:
922 self.active_entry_i = 0
923 if self.active_entry_i == 0:
924 self.active_entry = self.entry1
925 self.btns.active = 0
926 cursor_show2()
927 elif self.active_entry_i == 1:
928 self.active_entry = self.entry2
929 self.btns.active = 0
930 cursor_show2()
931 elif self.active_entry_i == 2:
932 self.btns.active = 1
933 cursor_hide()
934 answer = True
935 else:
936 self.btns.active = 2
937 cursor_hide()
938 answer = False
939 elif ans == 0x14: # Ctrl-T
940 # this is a hack, we need to return to refresh Entry
941 return [self.entry1.text, self.entry2.text, self.active_entry_i]
942 elif ans == 10: # return values
943 quit = True
944 answer = True
946 cursor_hide()
947 if answer:
948 # save new historic entries
949 if self.with_historic:
950 for text in self.entry1.text, self.entry2.text:
951 if text != None and text != '' and text != '*':
952 if len(historic) < 100:
953 historic.append(text)
954 else:
955 historic.reverse()
956 historic.pop()
957 historic.reverse()
958 historic.append(text)
959 ans1, ans2 = self.entry1.text, self.entry2.text
960 else:
961 ans1, ans2 = None, None
962 self.pwin.hide()
963 return ans1, ans2
966 ######################################################################
967 class SelectItem:
968 """A window to select an item"""
970 def __init__(self, entries, y0, x0, entry_i = ''):
971 h = (app.maxh-1) - (y0+1) + 1
972 # h = min(h, len(entries)+5)
973 w = min(max(map(len, entries)), int(app.maxw/2)) + 4
974 try:
975 win = curses.newwin(h, w, y0, x0)
976 self.pwin = curses.panel.new_panel(win)
977 self.pwin.top()
978 except curses.error:
979 print 'Can\'t create window'
980 sys.exit(-1)
981 win.keypad(1)
982 cursor_hide()
983 win.bkgd(curses.color_pair(4))
984 self.entries = entries
985 try:
986 self.entry_i = self.entries.index(entry_i)
987 except:
988 self.entry_i = 0
991 def show(self):
992 win = self.pwin.window()
993 win.erase()
994 win.refresh()
995 win.box(0, 0)
996 y, x = win.getbegyx()
997 h, w = win.getmaxyx()
998 h0 = h - 2
999 nels = len(self.entries)
1000 entry_a = int(self.entry_i/h0) * h0
1001 for i in xrange(h0):
1002 try:
1003 line = self.entries[entry_a + i]
1004 except IndexError:
1005 line = ''
1006 if len(line) > w - 3:
1007 if (w - 3) % 2 == 0: # even
1008 line = line[:int((w-3)/2)] + '~' + line[-int((w-3)/2)+2:]
1009 else: # odd
1010 line = line[:int((w-3)/2)+1] + '~' + line[-int((w-3)/2)+2:]
1011 if line != '':
1012 win.addstr(i+1, 2, line, curses.color_pair(4))
1013 win.refresh()
1014 # cursor
1015 cursor = curses.newpad(1, w-1)
1016 cursor.bkgd(curses.color_pair(1))
1017 cursor.erase()
1018 line = self.entries[self.entry_i]
1019 if len(line) > w - 2:
1020 if (w - 2) % 2 == 0: # even
1021 line = line[:int((w-2)/2)] + '~' + line[-int((w-2)/2)+2:]
1022 else: # odd
1023 line = line[:int((w-2)/2)+1] + '~' + line[-int((w-2)/2)+2:]
1024 cursor.addstr(0, 1, line, curses.color_pair(1) | curses.A_BOLD)
1025 y += 1; x += 1
1026 cur_row = y + self.entry_i % h0
1027 cursor.refresh(0, 0, cur_row, x, cur_row, x + w - 3)
1028 # scrollbar
1029 if nels > h0:
1030 n = max(int(h0*h0/nels), 1)
1031 y0 = min(max(int(int(self.entry_i/h0)*h0*h0/nels),0), h0 - n)
1032 else:
1033 y0 = n = 0
1034 win.vline(y0+1, w-1, curses.ACS_CKBOARD, n)
1035 if entry_a != 0:
1036 win.vline(1, w-1, '^', 1)
1037 if n == 1 and (y0 + 1 == 1):
1038 win.vline(2, w-1, curses.ACS_CKBOARD, n)
1039 if nels - 1 > entry_a + h0 - 1:
1040 win.vline(h0, w-1, 'v', 1)
1041 if n == 1 and (y0 == h0 - 1):
1042 win.vline(h0-1, w-1, curses.ACS_CKBOARD, n)
1045 def manage_keys(self):
1046 h, w = self.pwin.window().getmaxyx()
1047 nels = len(self.entries)
1048 while True:
1049 self.show()
1050 ch = self.pwin.window().getch()
1051 if ch in (0x03, 0x1B, ord('q'), ord('Q')): # Ctrl-C, ESC
1052 return -1
1053 elif ch in (curses.KEY_UP, ord('k'), ord('K')):
1054 if self.entry_i != 0:
1055 self.entry_i -= 1
1056 elif ch in (curses.KEY_DOWN, ord('j'), ord('J')):
1057 if self.entry_i != nels - 1:
1058 self.entry_i += 1
1059 elif ch in (curses.KEY_PPAGE, curses.KEY_BACKSPACE, 0x08, 0x02):
1060 if self.entry_i < h - 3:
1061 self.entry_i = 0
1062 else:
1063 self.entry_i -= h - 2
1064 elif ch in (curses.KEY_NPAGE, ord(' '), 0x06):
1065 if self.entry_i + (h-2) > nels - 1:
1066 self.entry_i = nels - 1
1067 else:
1068 self.entry_i += h - 2
1069 elif ch in (curses.KEY_HOME, 0x01):
1070 self.entry_i = 0
1071 elif ch in (curses.KEY_END, 0x05):
1072 self.entry_i = nels - 1
1073 elif ch == 0x13: # Ctrl-S
1074 theentries = self.entries[self.entry_i:]
1075 ch2 = self.pwin.window().getkey()
1076 for e in theentries:
1077 if e.find(ch2) == 0:
1078 break
1079 else:
1080 continue
1081 self.entry_i = self.entries.index(e)
1082 elif ch in (0x0A, 0x0D): # enter
1083 return self.entries[self.entry_i]
1084 else:
1085 curses.beep()
1088 def run(self):
1089 selected = self.manage_keys()
1090 self.pwin.below().top()
1091 self.pwin.hide()
1092 return selected
1095 ######################################################################
1096 class FindfilesWin:
1097 """A window to select a file"""
1099 def __init__(self, entries, entry_i = ''):
1100 y0 = 1
1101 h = (app.maxh-1) - (y0+1) + 1
1102 # w = max(map(len, entries)) + 4
1103 w = 64
1104 x0 = int((app.maxw-w) / 2)
1105 try:
1106 win = curses.newwin(h, w, y0, x0)
1107 self.pwin = curses.panel.new_panel(win)
1108 self.pwin.top()
1109 except curses.error:
1110 print 'Can\'t create window'
1111 sys.exit(-1)
1112 win.keypad(1)
1113 cursor_hide()
1114 win.bkgd(curses.color_pair(4))
1115 self.entries = entries
1116 try:
1117 self.entry_i = self.entries.index(entry_i)
1118 except:
1119 self.entry_i = 0
1120 self.btn_active = 0
1123 def show(self):
1124 win = self.pwin.window()
1125 win.erase()
1126 win.refresh()
1127 win.box(0, 0)
1128 y, x = win.getbegyx()
1129 h, w = win.getmaxyx()
1130 h0 = h - 4
1131 nels = len(self.entries)
1132 entry_a = int(self.entry_i/h0) * h0
1133 for i in xrange(h0):
1134 try:
1135 line = self.entries[entry_a+i]
1136 except IndexError:
1137 line = ''
1138 if len(line) >= w - 3:
1139 if (w - 3) % 2 == 0: # even
1140 line = line[:int((w-3)/2)] + '~' + line[-int((w-3)/2)+3:]
1141 else: # odd
1142 line = line[:int((w-3)/2)+1] + '~' + line[-int((w-3)/2)+3:]
1143 if line != '':
1144 win.addstr(i+1, 2, line, curses.color_pair(4))
1145 win.refresh()
1146 # cursor
1147 cursor = curses.newpad(1, w-2)
1148 cursor.attrset(curses.color_pair(1) | curses.A_BOLD)
1149 cursor.bkgdset(curses.color_pair(1))
1150 cursor.erase()
1151 line = self.entries[self.entry_i]
1152 if len(line) >= w - 3:
1153 if (w - 2) % 2 == 0: # even
1154 line = line[:int((w-2)/2)] + '~' + line[-int((w-2)/2)+3:]
1155 else: # odd
1156 line = line[:int((w-2)/2)+1] + '~' + line[-int((w-2)/2)+3:]
1157 cursor.addstr(0, 1, line, curses.color_pair(1) | curses.A_BOLD)
1158 y += 1; x += 1
1159 cursor.refresh(0, 0, y + self.entry_i % h0,
1160 x, y + self.entry_i % h0, x+w-3)
1161 # scrollbar
1162 if nels > h0:
1163 n = max(int(h0*h0/nels), 1)
1164 y0 = min(max(int(int(self.entry_i/h0)*h0*h0/nels),0), h0 - n)
1165 else:
1166 y0 = n = 0
1167 win.vline(y0+1, w-1, curses.ACS_CKBOARD, n)
1168 if entry_a != 0:
1169 win.vline(1, w-1, '^', 1)
1170 if n == 1 and (y0 + 1 == 1):
1171 win.vline(2, w-1, curses.ACS_CKBOARD, n)
1172 if nels - 1 > entry_a + h0 - 1:
1173 win.vline(h0, w-1, 'v', 1)
1174 if n == 1 and (y0 == h0 - 1):
1175 win.vline(h0-1, w-1, curses.ACS_CKBOARD, n)
1177 win.hline(h-3, 1, curses.ACS_HLINE, w-2)
1178 win.hline(h-3, 0, curses.ACS_LTEE, 1)
1179 win.hline(h-3, w-1, curses.ACS_RTEE, 1)
1180 win.addstr(h-2, 3,
1181 '[ Go ] [ Panelize ] [ View ] [ Edit ] [ Do ] [ Quit ]',
1182 curses.color_pair(4))
1183 if self.btn_active == 0:
1184 attr0 = curses.color_pair(1) | curses.A_BOLD
1185 attr1 = attr2 = attr3 = attr4 = attr5 = curses.color_pair(4)
1186 elif self.btn_active == 1:
1187 attr1 = curses.color_pair(1) | curses.A_BOLD
1188 attr0 = attr2 = attr3 = attr4 = attr5 = curses.color_pair(4)
1189 elif self.btn_active == 2:
1190 attr2 = curses.color_pair(1) | curses.A_BOLD
1191 attr0 = attr1 = attr3 = attr4 = attr5 = curses.color_pair(4)
1192 elif self.btn_active == 3:
1193 attr3 = curses.color_pair(1) | curses.A_BOLD
1194 attr0 = attr1 = attr2 = attr4 = attr5 = curses.color_pair(4)
1195 elif self.btn_active == 4:
1196 attr4 = curses.color_pair(1) | curses.A_BOLD
1197 attr0 = attr1 = attr2 = attr3 = attr5 = curses.color_pair(4)
1198 else:
1199 attr5 = curses.color_pair(1) | curses.A_BOLD
1200 attr0 = attr1 = attr2 = attr3 = attr4 = curses.color_pair(4)
1201 win.addstr(h-2, 3, '[ Go ]', attr0)
1202 win.addstr(h-2, 11, '[ PAnelize ]', attr1)
1203 win.addstr(h-2, 25, '[ View ]', attr2)
1204 win.addstr(h-2, 35, '[ Edit ]', attr3)
1205 win.addstr(h-2, 45, '[ Do ]', attr4)
1206 win.addstr(h-2, 53, '[ Quit ]', attr5)
1207 win.refresh()
1210 def manage_keys(self):
1211 h, w = self.pwin.window().getmaxyx()
1212 nels = len(self.entries)
1213 while True:
1214 self.show()
1215 ch = self.pwin.window().getch()
1216 if ch in (0x03, 0x1B, ord('q'), ord('Q')): # Ctrl-C, ESC
1217 return -1, None
1218 elif ch in (curses.KEY_UP, ord('k'), ord('K')):
1219 if self.entry_i != 0:
1220 self.entry_i -= 1
1221 elif ch in (curses.KEY_DOWN, ord('j'), ord('j')):
1222 if self.entry_i != nels - 1:
1223 self.entry_i += 1
1224 elif ch in (curses.KEY_PPAGE, curses.KEY_BACKSPACE, 0x08, 0x02):
1225 if self.entry_i < (h - 5):
1226 self.entry_i = 0
1227 else:
1228 self.entry_i -= (h - 4)
1229 elif ch in (curses.KEY_NPAGE, ord(' '), 0x06):
1230 if self.entry_i + (h-4) > nels - 1:
1231 self.entry_i = nels - 1
1232 else:
1233 self.entry_i += (h - 4)
1234 elif ch in (curses.KEY_HOME, 0x01):
1235 self.entry_i = 0
1236 elif ch in (curses.KEY_END, 0x05):
1237 self.entry_i = nels - 1
1238 elif ch == 0x13: # Ctrl-S
1239 theentries = self.entries[self.entry_i:]
1240 ch2 = self.pwin.window().getkey()
1241 for e in theentries:
1242 if e.find(ch2) == 0:
1243 break
1244 else:
1245 continue
1246 self.entry_i = self.entries.index(e)
1247 elif ch == 0x09: # tab
1248 if self.btn_active == 5:
1249 self.btn_active = 0
1250 else:
1251 self.btn_active += 1
1252 elif ch in (0x0A, 0x0D): # enter
1253 if self.btn_active == 0:
1254 return 0, self.entries[self.entry_i]
1255 elif self.btn_active == 1:
1256 return 1, None
1257 elif self.btn_active == 2:
1258 return 2, self.entries[self.entry_i]
1259 elif self.btn_active == 3:
1260 return 3, self.entries[self.entry_i]
1261 elif self.btn_active == 4:
1262 return 4, self.entries[self.entry_i]
1263 elif self.btn_active == 5:
1264 return -1, None
1265 elif ch in (ord('a'), ord('A')):
1266 return 1, None
1267 elif ch in (curses.KEY_F3, ord('v'), ord('V')):
1268 return 2, self.entries[self.entry_i]
1269 elif ch in (curses.KEY_F4, ord('e'), ord('E')):
1270 return 3, self.entries[self.entry_i]
1271 elif ch in (ord('@'), ord('d'), ord('D')):
1272 return 4, self.entries[self.entry_i]
1273 else:
1274 curses.beep()
1277 def run(self):
1278 selected = self.manage_keys()
1279 self.pwin.hide()
1280 return selected
1283 ######################################################################
1284 class MenuWin:
1285 """A window to select a menu option"""
1287 def __init__(self, title, entries):
1288 h = len(entries) + 4
1289 w = max(map(len, entries)) + 4
1290 y0 = int((app.maxh-h) / 2)
1291 x0 = int((app.maxw-w) / 2)
1292 try:
1293 win = curses.newwin(h, w, y0, x0)
1294 self.pwin = curses.panel.new_panel(win)
1295 self.pwin.top()
1296 except curses.error:
1297 print 'Can\'t create window'
1298 sys.exit(-1)
1299 win.keypad(1)
1300 cursor_hide()
1301 win.bkgd(curses.color_pair(3))
1302 self.title = title
1303 self.entries = entries
1304 self.entry_i = 0
1305 self.keys = [e[0] for e in entries]
1308 def show(self):
1309 win = self.pwin.window()
1310 win.erase()
1311 win.box(0, 0)
1312 y, x = win.getbegyx()
1313 h, w = win.getmaxyx()
1314 attr = curses.color_pair(7)
1315 win.addstr(0, int((w-len(self.title)-2)/2), ' %s ' % self.title, attr)
1316 for i in xrange(h-2):
1317 try:
1318 line = self.entries[i]
1319 except IndexError:
1320 line = ''
1321 if line != '':
1322 win.addstr(i+2, 2, line, curses.color_pair(3))
1323 win.refresh()
1324 # cursor
1325 cursor = curses.newpad(1, w-2)
1326 cursor.bkgd(curses.color_pair(1))
1327 cursor.erase()
1328 line = self.entries[self.entry_i]
1329 cursor.addstr(0, 1, line, curses.color_pair(1) | curses.A_BOLD)
1330 y += 1; x += 1
1331 cursor.refresh(0, 0, y + self.entry_i % (h-4) + 1,
1332 x, y + self.entry_i % (h-4) + 1, x+w-3)
1335 def manage_keys(self):
1336 while True:
1337 self.show()
1338 ch = self.pwin.window().getch()
1339 if ch in (0x03, 0x1B, ord('q'), ord('Q')): # Ctrl-C, ESC
1340 return -1
1341 elif ch in (curses.KEY_UP, ord('k'), ord('K')):
1342 if self.entry_i != 0:
1343 self.entry_i -= 1
1344 elif ch in (curses.KEY_DOWN, ord('j'), ord('J')):
1345 if self.entry_i != len(self.entries) - 1:
1346 self.entry_i += 1
1347 elif ch in (curses.KEY_HOME, 0x01, curses.KEY_PPAGE, 0x08, 0x02,
1348 curses.KEY_BACKSPACE):
1349 self.entry_i = 0
1350 elif ch in (curses.KEY_END, 0x05, curses.KEY_NPAGE, ord(' '), 0x06):
1351 self.entry_i = len(self.entries) - 1
1352 elif ch == 0x13: # Ctrl-S
1353 theentries = self.entries[self.entry_i:]
1354 ch2 = self.pwin.window().getkey()
1355 for e in theentries:
1356 if e.find(ch2) == 0:
1357 break
1358 else:
1359 continue
1360 self.entry_i = self.entries.index(e)
1361 elif ch in (0x0A, 0x0D): # enter
1362 return self.entries[self.entry_i]
1363 elif 0 <= ch <= 255 and chr(ch).lower() in self.keys:
1364 return self.entries[self.keys.index(chr(ch).lower())]
1365 else:
1366 curses.beep()
1369 def run(self):
1370 selected = self.manage_keys()
1371 self.pwin.hide()
1372 return selected
1375 ######################################################################
1376 class ChangePerms:
1377 """A window to change permissions, owner or group"""
1379 def __init__(self, file, fileinfo, i = 0, n = 0):
1380 h = 6 + 4
1381 w = 55 + 4
1382 y0 = int((app.maxh-h) / 2)
1383 x0 = int((app.maxw-w) / 2)
1384 try:
1385 win = curses.newwin(h, w, y0, x0)
1386 self.pwin = curses.panel.new_panel(win)
1387 self.pwin.top()
1388 except curses.error:
1389 print 'Can\'t create window'
1390 sys.exit(-1)
1391 win.keypad(1)
1392 cursor_hide()
1393 win.bkgd(curses.color_pair(1))
1395 self.file = file
1396 self.perms_old = files.perms2str(fileinfo[files.FT_PERMS])
1397 self.perms = [l for l in self.perms_old]
1398 self.owner = fileinfo[files.FT_OWNER]
1399 self.group = fileinfo[files.FT_GROUP]
1400 self.owner_old = self.owner[:]
1401 self.group_old = self.group[:]
1402 self.i = i
1403 self.n = n
1404 self.entry_i = 0
1405 self.w = w
1408 def show_btns(self):
1409 win = self.pwin.window()
1410 h, w = win.getmaxyx()
1411 attr1 = curses.color_pair(1) | curses.A_BOLD
1412 attr2 = curses.color_pair(9) | curses.A_BOLD
1413 win.addstr(h-2, w-21, '[<Ok>]', attr1)
1414 win.addstr(h-2, w-13, '[ Cancel ]', attr1)
1415 if self.entry_i == 5:
1416 win.addstr(h-2, w-21, '[<Ok>]', attr2)
1417 elif self.entry_i == 6:
1418 win.addstr(h-2, w-13, '[ Cancel ]', attr2)
1419 if self.i:
1420 win.addstr(h-2, 3, '[ All ]', attr1)
1421 win.addstr(h-2, 12, '[ Ignore ]', attr1)
1422 if self.entry_i == 7:
1423 win.addstr(h-2, 3, '[ All ]', attr2)
1424 elif self.entry_i == 8:
1425 win.addstr(h-2, 12, '[ Ignore ]', attr2)
1428 def show(self):
1429 win = self.pwin.window()
1430 win.getmaxyx()
1431 win.erase()
1432 win.box(0, 0)
1433 attr = curses.color_pair(1) | curses.A_BOLD
1434 title = 'Change permissions, owner or group'
1435 win.addstr(0, int((self.w-len(title)-2)/2), ' %s ' % title, attr)
1436 win.addstr(2, 2, '\'%s\'' % self.file, attr)
1437 if self.i:
1438 win.addstr(2, self.w-12-2, '%4d of %-4d' % (self.i, self.n))
1439 win.addstr(4, 7, 'owner group other owner group')
1440 win.addstr(5, 2, 'new: [---] [---] [---] [----------] [----------]')
1441 win.addstr(6, 2, 'old: [---] [---] [---] [----------] [----------]')
1442 win.addstr(6, 8, self.perms_old[0:3])
1443 win.addstr(6, 15, self.perms_old[3:6])
1444 win.addstr(6, 22, self.perms_old[6:9])
1445 l = len(self.owner_old)
1446 if l > 10:
1447 owner = self.owner_old[:10]
1448 else:
1449 owner = self.owner_old + '-' * (10-l)
1450 win.addstr(6, 32, owner)
1451 l = len(self.group_old)
1452 if l > 10:
1453 group = self.group_old[:10]
1454 else:
1455 group = self.group_old + '-' * (10-l)
1456 win.addstr(6, 46, group)
1458 perms = ''.join(self.perms)
1459 win.addstr(5, 8, perms[0:3])
1460 win.addstr(5, 15, perms[3:6])
1461 win.addstr(5, 22, perms[6:9])
1462 l = len(self.owner)
1463 if l > 10:
1464 owner = self.owner[:10]
1465 else:
1466 owner = self.owner + '-' * (10-l)
1467 win.addstr(5, 32, owner)
1468 l = len(self.group)
1469 if l > 10:
1470 group = self.group[:10]
1471 else:
1472 group = self.group + '-' * (10-l)
1473 win.addstr(5, 46, group)
1474 if self.entry_i == 0:
1475 win.addstr(5, 8, perms[0:3],
1476 curses.color_pair(5) | curses.A_BOLD)
1477 elif self.entry_i == 1:
1478 win.addstr(5, 15, perms[3:6],
1479 curses.color_pair(5) | curses.A_BOLD)
1480 elif self.entry_i == 2:
1481 win.addstr(5, 22, perms[6:9],
1482 curses.color_pair(5) | curses.A_BOLD)
1483 elif self.entry_i == 3:
1484 win.addstr(5, 32, owner,
1485 curses.color_pair(5) | curses.A_BOLD)
1486 elif self.entry_i == 4:
1487 win.addstr(5, 46, group,
1488 curses.color_pair(5) | curses.A_BOLD)
1489 self.show_btns()
1490 win.refresh()
1493 def manage_keys(self):
1494 y, x = self.pwin.window().getbegyx()
1495 while True:
1496 self.show()
1497 ch = self.pwin.window().getch()
1498 if ch in (0x03, 0x1B, ord('c'), ord('C'), ord('q'), ord('Q')):
1499 return -1
1500 elif ch in (ord('\t'), 0x09, curses.KEY_DOWN, curses.KEY_RIGHT):
1501 if self.i:
1502 if self.entry_i == 4:
1503 self.entry_i = 7
1504 elif self.entry_i == 8:
1505 self.entry_i = 5
1506 elif self.entry_i == 6:
1507 self.entry_i = 0
1508 else:
1509 self.entry_i += 1
1510 else:
1511 if self.entry_i == 6:
1512 self.entry_i = 0
1513 else:
1514 self.entry_i += 1
1515 elif ch in (curses.KEY_UP, curses.KEY_LEFT):
1516 if self.i:
1517 if self.entry_i == 0:
1518 self.entry_i = 6
1519 elif self.entry_i == 5:
1520 self.entry_i = 8
1521 elif self.entry_i == 7:
1522 self.entry_i = 4
1523 else:
1524 self.entry_i -= 1
1525 else:
1526 if self.entry_i == 0:
1527 self.entry_i = 6
1528 else:
1529 self.entry_i -= 1
1530 elif ch in (ord('r'), ord('R')):
1531 if not 0 <= self.entry_i <= 2:
1532 continue
1533 d = self.entry_i * 3
1534 if self.perms[d] == 'r':
1535 self.perms[d] = '-'
1536 else:
1537 self.perms[d] = 'r'
1538 elif ch in (ord('w'), ord('W')):
1539 if not 0 <= self.entry_i <= 2:
1540 continue
1541 d = 1 + self.entry_i * 3
1542 if self.perms[d] == 'w':
1543 self.perms[d] = '-'
1544 else:
1545 self.perms[d] = 'w'
1546 elif ch in (ord('x'), ord('X')):
1547 if not 0 <= self.entry_i <= 2:
1548 continue
1549 d = 2 + self.entry_i * 3
1550 if self.perms[d] == 'x':
1551 self.perms[d] = '-'
1552 else:
1553 self.perms[d] = 'x'
1554 elif ch in (ord('t'), ord('T')):
1555 if not self.entry_i == 2:
1556 continue
1557 if self.perms[8] == 't':
1558 self.perms[8] = self.perms_old[8]
1559 else:
1560 self.perms[8] = 't'
1561 elif ch in (ord('s'), ord('S')):
1562 if not 0 <= self.entry_i <= 1:
1563 continue
1564 d = 2 + self.entry_i * 3
1565 if self.perms[d] == 's':
1566 self.perms[d] = self.perms_old[d]
1567 else:
1568 self.perms[d] = 's'
1569 elif ch in (0x0A, 0x0D):
1570 if self.entry_i == 3:
1571 owners = files.get_owners()
1572 owners.sort()
1573 try:
1574 owners.index(self.owner)
1575 except:
1576 owners.append(self.owner)
1577 ret = SelectItem(owners, y+6, x+32, self.owner).run()
1578 if ret != -1:
1579 self.owner = ret
1580 app.display()
1581 elif self.entry_i == 4:
1582 groups = files.get_groups()
1583 groups.sort()
1584 try:
1585 groups.index(self.group)
1586 except:
1587 groups.append(self.group)
1588 ret = SelectItem(groups, y+6, x+32, self.group).run()
1589 if ret != -1:
1590 self.group = ret
1591 app.display()
1592 elif self.entry_i == 6:
1593 return -1
1594 elif self.i and self.entry_i == 7:
1595 return self.perms, self.owner, self.group, 1
1596 elif self.i and self.entry_i == 8:
1597 return 0
1598 else:
1599 return self.perms, self.owner, self.group, 0
1600 elif self.i and ch in (ord('i'), ord('I')):
1601 return 0
1602 elif self.i and ch in (ord('a'), ord('A')):
1603 return self.perms, self.owner, self.group, 1
1604 else:
1605 curses.beep()
1608 def run(self):
1609 selected = self.manage_keys()
1610 self.pwin.hide()
1611 return selected
1614 ######################################################################
1615 ##### some wrappers
1616 def cursor_show2():
1617 try: # some terminals don't allow '2'
1618 curses.curs_set(2)
1619 except:
1620 cursor_show()
1623 def cursor_show():
1624 try:
1625 curses.curs_set(1)
1626 except:
1627 pass
1630 def cursor_hide():
1631 try:
1632 curses.curs_set(0)
1633 except:
1634 pass
1637 ######################################################################