~ubuntu-branches/ubuntu/maverick/lfm/maverick-proposed

« back to all changes in this revision

Viewing changes to lfm/lfm.py

  • Committer: Bazaar Package Importer
  • Author(s): Sebastien Bacher
  • Date: 2004-07-26 18:08:07 UTC
  • Revision ID: james.westby@ubuntu.com-20040726180807-ocycfymqg3pvz22o
Tags: upstream-0.91.2
Import upstream version 0.91.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
# -*- coding: iso-8859-15 -*-
 
3
 
 
4
# Copyright (C) 2001-4  I�igo Serna
 
5
#
 
6
# This program is free software; you can redistribute it and/or
 
7
# modify it under the terms of the GNU General Public License
 
8
# as published by the Free Software Foundation; either version 2
 
9
# of the License, or (at your option) any later version.
 
10
#
 
11
# This program is distributed in the hope that it will be useful,
 
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
# GNU General Public License for more details.
 
15
#
 
16
# You should have received a copy of the GNU General Public License
 
17
# along with this program; if not, write to the Free Software
 
18
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
19
 
 
20
 
 
21
"""
 
22
Copyright (C) 2001-4, I�igo Serna <inigoserna@telefonica.net>.
 
23
All rights reserved.
 
24
 
 
25
This software has been realised under the GPL License, see the COPYING
 
26
file that comes with this package. There is NO WARRANTY.
 
27
 
 
28
'Last File Manager' is (tries to be) a simple 'midnight commander'-type
 
29
application for UNIX console.
 
30
"""
 
31
 
 
32
 
 
33
import os, os.path
 
34
import sys, time
 
35
import curses
 
36
 
 
37
from __init__ import *
 
38
import files
 
39
import actions
 
40
import vfs
 
41
import messages
 
42
import preferences
 
43
import pyview
 
44
 
 
45
 
 
46
##################################################
 
47
##### lfm
 
48
##################################################
 
49
class Lfm:
 
50
    """Main application class"""
 
51
 
 
52
    panels = []
 
53
    npanels = 0
 
54
    
 
55
    def __init__(self, win, paths, npanels = 2):
 
56
        self.win = win              # root window, needed for resizing
 
57
        self.npanels = npanels      # no. of panels showed
 
58
        self.panels = []            # list of panels
 
59
        self.panel = 0              # active panel
 
60
        self.initialized = 0
 
61
        # preferences
 
62
        self.prefs = preferences.Preferences(PREFSFILE, defaultprogs, filetypes)
 
63
        self.modes = self.prefs.modes
 
64
        # We need prefs now, but return code will be handled after
 
65
        # curses initalization, because we might need windows.
 
66
        ret = self.prefs.load()
 
67
        # check for valid programs
 
68
        self.prefs.check_progs(self.prefs.progs)
 
69
        # panels
 
70
        self.init_curses()
 
71
        # Ok, now we can handle error messages.
 
72
        if ret == -1:
 
73
            messages.error('Load Preferences',
 
74
                           '\'%s\' does not exist\nusing default values' %
 
75
                           PREFSFILE)
 
76
            self.prefs.save()
 
77
        elif ret == -2:
 
78
            messages.error('Load Preferences',
 
79
                           '\'%s\' seems corrupted\nusing default values' %
 
80
                           PREFSFILE)
 
81
            self.prefs.save()
 
82
        # rest of the panels initialization.
 
83
        if npanels == 2:
 
84
            self.panels.append(Panel(paths[0], 1, self))     # left panel
 
85
            self.panels.append(Panel(paths[1], 2, self))     # right panel
 
86
        else:
 
87
            self.panels.append(Panel(paths[0], 3, self))     # full panel
 
88
            self.panels.append(Panel(paths[1], 0, self))     # not shown
 
89
        self.initialized = 1
 
90
 
 
91
 
 
92
    def init_curses(self):
 
93
        """initialize curses stuff: windows, colors, ..."""
 
94
 
 
95
        self.maxh, self.maxw = self.win.getmaxyx()
 
96
        curses.cbreak()
 
97
        curses.raw()
 
98
        cursor_hide()
 
99
 
 
100
        # create top and bottom windows
 
101
        try:
 
102
            self.win_title = curses.newwin(1, 0, 0, 0)
 
103
            self.win_status = curses.newwin(1, 0, self.maxh-1, 0)
 
104
        except curses.error:
 
105
            print 'Can\'t create windows'
 
106
            sys.exit(-1)
 
107
 
 
108
        # colors
 
109
        if curses.has_colors():
 
110
            self.colors = self.prefs.colors
 
111
 
 
112
            # Translation table: color name -> curses color name.
 
113
            self.coltbl = {
 
114
                'black': curses.COLOR_BLACK,
 
115
                'blue': curses.COLOR_BLUE,
 
116
                'cyan': curses.COLOR_CYAN,
 
117
                'green': curses.COLOR_GREEN,
 
118
                'magenta': curses.COLOR_MAGENTA,
 
119
                'red': curses.COLOR_RED,
 
120
                'white': curses.COLOR_WHITE,
 
121
                'yellow': curses.COLOR_YELLOW }
 
122
 
 
123
            # Defaults of base objects. object, foregrounf, background
 
124
            colors = [
 
125
                ('title', 'yellow', 'blue'),
 
126
                ('files', 'white', 'black'),
 
127
                ('current_file', 'blue', 'cyan'),
 
128
                ('messages', 'magenta', 'cyan'),
 
129
                ('help', 'green', 'black'),
 
130
                ('file_info', 'red', 'black'),
 
131
                ('error_messages1', 'white', 'red'),
 
132
                ('error_messages2', 'black', 'red'),
 
133
                ('buttons', 'yellow', 'red'),
 
134
                ('selected_file', 'yellow', 'black'),
 
135
                ('current_selected_file', 'yellow', 'cyan') ]
 
136
 
 
137
            # Initialize every color pair with user colors or with the defaults.
 
138
            for i in range(len(colors)):
 
139
                curses.init_pair(i+1,
 
140
                    self.__set_color(self.colors[colors[i][0]][0], self.coltbl[colors[i][1]]),
 
141
                    self.__set_color(self.colors[colors[i][0]][1], self.coltbl[colors[i][2]]))
 
142
 
 
143
            self.win_title.attrset(curses.color_pair(1) | curses.A_BOLD)
 
144
            self.win_title.bkgdset(curses.color_pair(1))
 
145
            self.win_status.attrset(curses.color_pair(1))
 
146
            self.win_status.bkgdset(curses.color_pair(1))
 
147
 
 
148
 
 
149
    def resize(self):
 
150
        """resize windows"""
 
151
 
 
152
        h, w = self.win.getmaxyx()
 
153
        self.maxh, self.maxw = h, w
 
154
        if w == 0 or h == 2:
 
155
            return
 
156
        for p in self.panels:
 
157
            p.win_files.erase()
 
158
            p.win_files.refresh()
 
159
        self.win.resize(h, w)
 
160
        self.win_title.resize(1, w)
 
161
        self.win_status.resize(1, w)
 
162
        self.win_status.mvwin(h-1, 0)
 
163
        for p in self.panels:
 
164
            p.do_resize(h, w)
 
165
        self.show()
 
166
 
 
167
 
 
168
    def __set_color(self, col, defcol):
 
169
        """return curses color value if exists, otherwise return default"""
 
170
        if self.coltbl.has_key(col):
 
171
            return self.coltbl[col]
 
172
        else:
 
173
            return defcol
 
174
 
 
175
 
 
176
    def __show_bars(self):
 
177
        """show title and status bars"""
 
178
        
 
179
        self.win_title.erase()
 
180
        if self.maxw >= 60:
 
181
            title = '%s v%s   (c) %s, %s' % (LFM_NAME, VERSION, DATE, AUTHOR)
 
182
            pos = (self.maxw-len(title)) /2
 
183
            self.win_title.addstr(0, pos, title)
 
184
        self.win_title.refresh()
 
185
 
 
186
        self.win_status.erase()
 
187
        panel = self.panels[self.panel]
 
188
        if len(panel.selections):
 
189
            if self.maxw >= 45:
 
190
                size = 0
 
191
                for f in panel.selections:
 
192
                    size += panel.files[f][files.FT_SIZE]
 
193
                self.win_status.addstr('    %s bytes in %d files' % \
 
194
                                       (num2str(size), len(panel.selections)))
 
195
        else:
 
196
            if self.maxw >= 80:
 
197
                self.win_status.addstr('File: %4d of %-4d' % \
 
198
                                       (panel.file_i + 1, panel.nfiles))
 
199
                filename = panel.sorted[panel.file_i]
 
200
                if not panel.vfs:
 
201
                    realpath = files.get_realpath(panel.path, filename,
 
202
                                                  panel.files[filename][files.FT_TYPE])
 
203
                else:
 
204
                    realpath = os.path.join(vfs.join(app, panel), filename)
 
205
                if len(realpath) > self.maxw - 35:
 
206
                    path = '~' + realpath[-(self.maxw-37):]
 
207
                else:
 
208
                    path = realpath
 
209
                path = files.fix_chars_in_filename(path)
 
210
                self.win_status.addstr(0, 20, 'Path: ' + path)
 
211
        if self.maxw > 10:
 
212
            try:
 
213
                self.win_status.addstr(0, self.maxw-8, 'F1=Help')
 
214
            except:
 
215
                pass
 
216
        self.win_status.refresh()
 
217
 
 
218
 
 
219
    def show(self):
 
220
        """show title, files panel(s) and status bar"""
 
221
        
 
222
        self.__show_bars()
 
223
        for panel in self.panels:
 
224
            panel.show()
 
225
        
 
226
 
 
227
    def run(self):
 
228
        """run application"""
 
229
 
 
230
        while 1:
 
231
            self.show()
 
232
            oldpanel = self.panels[self.panel].manage_keys()
 
233
            if oldpanel < 0:
 
234
                if self.prefs.options['save_conf_at_exit']:
 
235
                    self.prefs.save()
 
236
                if oldpanel == -1:
 
237
                    for panel in self.panels:
 
238
                        if panel.vfs:
 
239
                            vfs.exit(app, panel)
 
240
                    return self.panels[self.panel].path
 
241
                else:
 
242
                    for panel in self.panels:
 
243
                        if panel.vfs:
 
244
                            vfs.exit(app, panel)
 
245
                    return
 
246
            # change panel active
 
247
            if oldpanel == 0:
 
248
                self.panel = 1
 
249
            else:
 
250
                self.panel = 0
 
251
 
 
252
 
 
253
    def get_otherpanel(self):
 
254
        """return the panel not active"""
 
255
        
 
256
        if self.panel == 0:
 
257
            return self.panels[1]
 
258
        else:
 
259
            return self.panels[0]
 
260
 
 
261
 
 
262
##################################################
 
263
##### panel
 
264
##################################################
 
265
class Panel:
 
266
    """Panel class"""
 
267
    
 
268
    def __init__(self, path, pos, app):
 
269
        self.maxh, self.maxw = app.maxh, app.maxw
 
270
        self.__calculate_columns()
 
271
        self.pos = pos
 
272
        self.init_curses(pos)
 
273
        self.init_dir(path, app)
 
274
 
 
275
 
 
276
    def __calculate_columns(self):
 
277
        self.pos_col2 = self.maxw / 2 - 14
 
278
        self.pos_col1 = self.pos_col2 - 8
 
279
 
 
280
        
 
281
    # GUI
 
282
    def __calculate_dims(self, pos=0):
 
283
        if pos == 0:      # not visible panel
 
284
            return (self.maxh-2, self.maxw, 0, 0)     # h, w, y, x
 
285
        elif pos == 1:    # left panel
 
286
            return (self.maxh-2, int(self.maxw/2), 1, 0)
 
287
        elif pos == 2:    # right panel
 
288
            return (self.maxh-2, int(self.maxw/2), 1, int(self.maxw/2))
 
289
        elif pos == 3:    # full panel
 
290
            return (self.maxh-2, self.maxw, 1, 0)     # h, w, y, x
 
291
        else:             # error
 
292
            messages.error('Initialize Panels',
 
293
                           'Incorrect panel number.\nLook for bugs if you can see this.')
 
294
            return (self.maxh-2, int(self.maxw/2), 1, int(self.maxw/2))
 
295
 
 
296
 
 
297
    def init_curses(self, pos=0):
 
298
        self.dims = self.__calculate_dims(pos)
 
299
        try:
 
300
            self.win_files = curses.newwin(self.dims[0], self.dims[1], self.dims[2], self.dims[3])
 
301
        except curses.error:
 
302
            print 'Can\'t create panel window'
 
303
            sys.exit(-1)
 
304
        self.win_files.keypad(1)
 
305
        if curses.has_colors():
 
306
            self.win_files.attrset(curses.color_pair(2))
 
307
            self.win_files.bkgdset(curses.color_pair(2))
 
308
        
 
309
 
 
310
    def do_resize(self, h, w):
 
311
        self.maxh, self.maxw = h, w
 
312
        self.dims = self.__calculate_dims(self.pos)
 
313
        self.win_files.resize(self.dims[0], self.dims[1])
 
314
        self.win_files.mvwin(self.dims[2], self.dims[3])
 
315
        self.__calculate_columns()
 
316
        self.fix_limits()
 
317
 
 
318
        
 
319
    def show(self):
 
320
        """show panel"""
 
321
 
 
322
        # invisible
 
323
        if self.pos == 0:
 
324
            return
 
325
 
 
326
        self.win_files.erase()
 
327
        if self.maxw < 65:
 
328
            return
 
329
        # headers
 
330
        if self.pos != 3:
 
331
            if self == app.panels[app.panel]:
 
332
                self.win_files.attrset(curses.color_pair(5))
 
333
                attr = curses.color_pair(6) | curses.A_BOLD
 
334
            else:
 
335
                self.win_files.attrset(curses.color_pair(2))
 
336
                attr = curses.color_pair(2)
 
337
            if not self.vfs:
 
338
                path = self.path
 
339
            else:
 
340
                path = vfs.join(app, self)
 
341
            if len(path) > int(self.maxw/2) - 5:
 
342
                title_path = '~' + path[-int(self.maxw/2)+5:]
 
343
            else:
 
344
                title_path = path
 
345
            self.win_files.box()
 
346
 
 
347
            self.win_files.addstr(0, 2, title_path, attr)
 
348
            self.win_files.addstr(1, 1, center('Name', self.pos_col1-2),
 
349
                                  curses.color_pair(2) | curses.A_BOLD)
 
350
            self.win_files.addstr(1, self.pos_col1 + 2, 'Size',
 
351
                                  curses.color_pair(2) | curses.A_BOLD)
 
352
            self.win_files.addstr(1, self.pos_col2 + 5, 'Date',
 
353
                                  curses.color_pair(2) | curses.A_BOLD)
 
354
 
 
355
        # files
 
356
        for i in range(self.file_z - self.file_a + 1):
 
357
            filename = self.sorted[i + self.file_a]
 
358
            try:
 
359
                self.selections.index(filename)
 
360
            except ValueError:
 
361
                attr = curses.color_pair(2)
 
362
            else:
 
363
                attr = curses.color_pair(10) | curses.A_BOLD
 
364
            # get file info
 
365
            res = files.get_fileinfo_dict(self.path, filename,
 
366
                                          self.files[filename])
 
367
            # full panel
 
368
            if self.pos == 3:
 
369
                buf = self.__get_fileinfo_str_long(res)
 
370
                self.win_files.addstr(i, 0, buf, attr)
 
371
            # left or right panels
 
372
            else:
 
373
                buf = self.__get_fileinfo_str_short(res)
 
374
                self.win_files.addstr(i + 2, 1, buf, attr)
 
375
 
 
376
        # vertical separators
 
377
        if self.pos != 3:
 
378
            self.win_files.vline(1, self.pos_col1,
 
379
                                 curses.ACS_VLINE, self.dims[0]-2)
 
380
            self.win_files.vline(1, self.pos_col2,
 
381
                                 curses.ACS_VLINE, self.dims[0]-2)
 
382
 
 
383
        # vertical scroll bar
 
384
        if self.pos != 0:
 
385
            if self.pos == 3:
 
386
                h = self.maxh - 2
 
387
            else:
 
388
                h = self.maxh - 5
 
389
            y0, n = calculate_scrollbar_dims(h, self.nfiles, self.file_i)
 
390
            if self.pos == 3:
 
391
                if n != 0:
 
392
                    self.win_files.vline(0, self.maxw - 1,
 
393
                                         curses.ACS_VLINE, h)
 
394
                self.win_files.vline(y0, self.maxw - 1, curses.ACS_CKBOARD, n)
 
395
                if self.file_a != 0:
 
396
                    self.win_files.vline(0, self.maxw - 1, '^', 1)
 
397
                    if n == 1 and (y0 == 0):
 
398
                        self.win_files.vline(1, self.maxw - 1,
 
399
                                             curses.ACS_CKBOARD, n)
 
400
                if self.nfiles - 1 > self.file_a + h - 1:
 
401
                    self.win_files.vline(h - 1, self.maxw - 1, 'v', 1)
 
402
                    if n == 1 and (y0 == h - 1):
 
403
                        self.win_files.vline(h - 2, self.maxw - 1,
 
404
                                             curses.ACS_CKBOARD, n)
 
405
            else:
 
406
                self.win_files.vline(y0 + 2, int(self.maxw/2) - 1,
 
407
                                     curses.ACS_CKBOARD, n)
 
408
                if self.file_a != 0:
 
409
                    self.win_files.vline(2, int(self.maxw/2) - 1, '^', 1)
 
410
                    if n == 1 and (y0 + 2 == 2):
 
411
                        self.win_files.vline(3, int(self.maxw/2) - 1,
 
412
                                             curses.ACS_CKBOARD, n)
 
413
                if self.nfiles - 1 > self.file_a + h - 1:
 
414
                    self.win_files.vline(h + 1, int(self.maxw/2) - 1, 'v', 1)
 
415
                    if n == 1 and (y0 + 2 == h + 1):
 
416
                        self.win_files.vline(h, int(self.maxw/2) - 1,
 
417
                                             curses.ACS_CKBOARD, n)
 
418
 
 
419
        self.win_files.refresh()
 
420
        self.__showbar()
 
421
 
 
422
 
 
423
    def __showbar(self):
 
424
        if self != app.panels[app.panel]:     # panel not active
 
425
            return
 
426
        if self.pos == 3:      # full panel
 
427
            cursorbar = curses.newpad(1, self.maxw)
 
428
        else:                  # left or right panel
 
429
            cursorbar = curses.newpad(1, int(self.maxw/2) - 1)
 
430
 
 
431
        cursorbar.attrset(curses.color_pair(3))
 
432
        cursorbar.bkgdset(curses.color_pair(3))
 
433
        filename = self.sorted[self.file_i]
 
434
#        print "FILENAME: <%s>" % filename
 
435
 
 
436
        try:
 
437
            self.selections.index(filename)
 
438
        except ValueError:
 
439
            attr = curses.color_pair(3)
 
440
        else:
 
441
            attr = curses.color_pair(11) | curses.A_BOLD
 
442
        cursorbar.erase()
 
443
        # get file info
 
444
        res = files.get_fileinfo_dict(self.path, filename,
 
445
                                      self.files[filename])
 
446
        if self.pos == 3:
 
447
            buf = self.__get_fileinfo_str_long(res)
 
448
            cursorbar.addstr(0, 0, buf, attr)
 
449
            cursorbar.refresh(0, 0,
 
450
                              self.file_i % self.dims[0] + 1, 0,
 
451
                              self.file_i % self.dims[0] + 1, self.maxw - 2)
 
452
        else:
 
453
            buf = self.__get_fileinfo_str_short(res)
 
454
            cursorbar.addstr(0, 0, buf, attr)
 
455
            cursorbar.addch(0, self.pos_col1-1, curses.ACS_VLINE)
 
456
            cursorbar.addch(0, self.pos_col2-1, curses.ACS_VLINE)
 
457
            if self.pos == 1:
 
458
                cursorbar.refresh(0, 0,
 
459
                                  self.file_i % (self.dims[0]-3) + 3, 1,
 
460
                                  self.file_i % (self.dims[0]-3) + 3,
 
461
                                  int(self.maxw/2) - 2)
 
462
            else:
 
463
                cursorbar.refresh(0, 0,
 
464
                                  self.file_i % (self.dims[0]-3) + 3,
 
465
                                  int(self.maxw/2) + 1,
 
466
                                  self.file_i % (self.dims[0]-3) + 3,
 
467
                                  self.maxw - 2)
 
468
 
 
469
 
 
470
    def __get_fileinfo_str_short(self, res):
 
471
        filewidth = self.maxw/2-24
 
472
        fname = res['filename']
 
473
        if len(fname) > filewidth:
 
474
            half = int(filewidth / 2)
 
475
            fname = fname[:half+2] + '~' + fname[-half+3:]
 
476
        fname = ljust(fname, self.pos_col1-2)
 
477
        if res['dev']:
 
478
            buf = '%c%s %3d,%3d %12s' % \
 
479
                  (res['type_chr'], fname,
 
480
                   res['maj_rdev'], res['min_rdev'],
 
481
                   res['mtime2'])
 
482
        else:
 
483
            buf = '%c%s %7s %12s' % \
 
484
                  (res['type_chr'], fname,
 
485
                   res['size'], res['mtime2'])                    
 
486
        return buf
 
487
 
 
488
 
 
489
    def __get_fileinfo_str_long(self, res):
 
490
        fname = res['filename']
 
491
        if len(fname) > self.maxw-57:
 
492
            half = int((self.maxw-57) / 2)
 
493
            fname = fname[:half+2] + '~' + fname[-half+2:]
 
494
        if res['dev']:
 
495
            buf = '%c%9s %-8s %-8s %3d,%3d  %16s  %s' % \
 
496
                  (res['type_chr'], res['perms'],
 
497
                   res['owner'][:8], res['group'][:8],
 
498
                   res['maj_rdev'], res['min_rdev'],
 
499
                   res['mtime'], fname)
 
500
        else:
 
501
            buf = '%c%9s %-8s %-8s %7s  %16s  %s' % \
 
502
                  (res['type_chr'], res['perms'],
 
503
                   res['owner'][:8], res['group'][:8],
 
504
                   res['size'],
 
505
                   res['mtime'], fname)
 
506
        return buf
 
507
 
 
508
 
 
509
    # Files
 
510
    def init_dir(self, path, application = None):
 
511
        try:
 
512
            # HACK: hack to achieve to pass prefs to this function
 
513
            #       the first time it is executed
 
514
            if application != None:
 
515
                self.nfiles, self.files = files.get_dir(path, application.prefs.options['show_dotfiles'])
 
516
                sortmode = application.modes['sort']
 
517
                sort_mix_dirs = application.modes['sort_mix_dirs']
 
518
                sort_mix_cases = application.modes['sort_mix_cases']
 
519
            else:
 
520
                self.nfiles, self.files = files.get_dir(path, app.prefs.options['show_dotfiles'])
 
521
                sortmode = app.modes['sort']
 
522
                sort_mix_dirs = app.modes['sort_mix_dirs']
 
523
                sort_mix_cases = app.modes['sort_mix_cases']
 
524
            self.sorted = files.sort_dir(self.files, sortmode,
 
525
                                         sort_mix_dirs, sort_mix_cases)
 
526
            self.file_i = self.file_a = self.file_z = 0
 
527
            self.fix_limits()
 
528
            self.path = os.path.abspath(path)
 
529
            self.selections = []
 
530
        except (IOError, OSError), (errno, strerror):
 
531
            messages.error('Enter In Directory', '%s (%d)' %
 
532
                           (strerror, errno), path)
 
533
            # if not initialized, python can retrive app.initialized variable,
 
534
            # so the try-except statement
 
535
            try:
 
536
                if not app.initialized:
 
537
                    sys.exit(-1)
 
538
            except:
 
539
                lfm_exit(-1)
 
540
                return
 
541
        # vfs variables
 
542
        self.vfs = ''          # vfs? if not -> blank string
 
543
        self.base = ''         # tempdir basename
 
544
        self.vbase = self.path # virtual directory basename
 
545
        
 
546
 
 
547
 
 
548
    def fix_limits(self):
 
549
        if self.file_i < 0:
 
550
            self.file_i = 0
 
551
        if self.file_i > self.nfiles - 1:
 
552
            self.file_i = self.nfiles - 1
 
553
        if self.pos == 3 or self.pos == 0:    # full or invisible panel
 
554
            height = self.dims[0]
 
555
        else:                                 # left or right panel
 
556
            height = self.dims[0] - 3
 
557
        self.file_a = int(self.file_i / height) * height
 
558
        self.file_z = self.file_a + height - 1
 
559
        if self.file_z > self.nfiles - 1:
 
560
            self.file_z = self.nfiles - 1
 
561
 
 
562
 
 
563
    def refresh_panel(self, panel):
 
564
        """this is needed because panel could be changed"""
 
565
 
 
566
        path = panel.path
 
567
        if path[-1] == os.sep:
 
568
            path = path[:-1]
 
569
        while not os.path.exists(path):
 
570
            path = os.path.dirname(path)
 
571
 
 
572
        if path != panel.path:
 
573
            panel.path = path
 
574
            panel.file_i = 0
 
575
            pvfs, base, vbase = panel.vfs, panel.base, panel.vbase
 
576
            panel.init_dir(panel.path)
 
577
            panel.vfs, panel.base, panel.vbase = pvfs, base, vbase
 
578
            panel.fix_limits()
 
579
            panel.selections = []
 
580
        else:
 
581
            filename_old = panel.sorted[panel.file_i]
 
582
            selections_old = panel.selections[:]
 
583
            pvfs, base, vbase = panel.vfs, panel.base, panel.vbase
 
584
            panel.init_dir(panel.path)
 
585
            panel.vfs, panel.base, panel.vbase = pvfs, base, vbase
 
586
            try:
 
587
                panel.file_i = panel.sorted.index(filename_old)
 
588
            except ValueError:
 
589
                panel.file_i = 0
 
590
            panel.fix_limits()
 
591
            panel.selections = selections_old[:]
 
592
            for f in panel.selections:
 
593
                if f not in panel.sorted:
 
594
                    panel.selections.remove(f)
 
595
 
 
596
 
 
597
    # Keys
 
598
    def manage_keys(self):
 
599
        while 1:
 
600
            app.show()
 
601
            ch = self.win_files.getch()
 
602
            if ch == 0x1B:     # ESC
 
603
                ch = self.win_files.getch() + 0x400
 
604
#             print 'key: \'%s\' <=> %c <=> 0x%X <=> %d' % \
 
605
#                   (curses.keyname(ch), ch & 255, ch, ch)
 
606
#             messages.win('Keyboard hitted:',
 
607
#                          'key: \'%s\' <=> %c <=> 0x%X <=> %d' % \
 
608
#                          (curses.keyname(ch), ch & 255, ch, ch))
 
609
            ret = actions.do(app, self, ch)
 
610
            if ret != None:
 
611
                return ret
 
612
 
 
613
#             # refresh screen
 
614
#             elif ch in [0x12]:             # Ctrl-r
 
615
#                 app.show()
 
616
#                 self.refresh_panel(self)
 
617
#                 self.refresh_panel(app.get_otherpanel())
 
618
 
 
619
 
 
620
##################################################
 
621
##### Wrappers
 
622
##################################################
 
623
def cursor_show():
 
624
    try:
 
625
        curses.curs_set(1)
 
626
    except:
 
627
        pass
 
628
 
 
629
 
 
630
def cursor_hide():
 
631
    try:
 
632
        curses.curs_set(0)
 
633
    except:
 
634
        pass
 
635
 
 
636
 
 
637
##################################################
 
638
##### Utils
 
639
##################################################
 
640
def center(s, maxlen):
 
641
    return s.center(maxlen)[:maxlen]
 
642
    
 
643
 
 
644
def ljust(s, maxlen):
 
645
    return s.ljust(maxlen)[:maxlen]
 
646
    
 
647
 
 
648
def rjust(s, maxlen):
 
649
    return s.rjust(maxlen)[maxlen:]
 
650
    
 
651
 
 
652
def num2str(num):
 
653
    num_list = []
 
654
    while num / 1000.0 >= 0.001:
 
655
        num_list.append('%.3d' % (num % 1000))
 
656
        num /= 1000.0
 
657
    else:
 
658
        num_str = '0'
 
659
    if len(num_list) != 0:
 
660
        num_list.reverse()
 
661
        num_str = ','.join(num_list)
 
662
        while num_str[0] == '0':
 
663
            num_str = num_str[1:]
 
664
    return num_str
 
665
 
 
666
 
 
667
def calculate_scrollbar_dims(h, nels, i):
 
668
    """calculate scrollbar initial position and size"""
 
669
 
 
670
    if nels > h:
 
671
        n = int(h * h / nels)
 
672
        if n == 0:
 
673
            n = 1
 
674
        a = int(i / h) * h
 
675
        y0 = int(a * h / nels)
 
676
        if y0 < 0:
 
677
            y0 = 0
 
678
        elif y0 + n > h:
 
679
            y0 = h - n
 
680
    else:
 
681
        y0 = 0
 
682
        n = 0
 
683
    return y0, n
 
684
 
 
685
 
 
686
##################################################
 
687
##### Main
 
688
##################################################
 
689
def usage(prog, msg = ""):
 
690
    prog = os.path.basename(prog)
 
691
    if msg != "":
 
692
        print '%s:\t%s\n' % (prog, msg)
 
693
    print """\
 
694
%s v%s - (C) %s, by %s
 
695
 
 
696
A program trying to recover command line good ol' times feelings...
 
697
Released under GNU Public License, read COPYING for more details.
 
698
 
 
699
Usage:\t%s\t[-h | --help]
 
700
\t\t[-d | --debug]
 
701
\t\t[-1 | -2] [pathtodir1 | pathtofile [pathtodir2]]
 
702
Options:
 
703
    -1\t\t\tstart in 1 panel mode
 
704
    -2\t\t\tstart in 2 panels mode (default)
 
705
    -d, --debug\t\tcreate debug file
 
706
    -h, --help\t\tshow help
 
707
    pathtodir1\t\tdirectory
 
708
    pathtofile\t\tfile to view/edit
 
709
    pathtodir2\t\tdirectory to show in panel 2\
 
710
""" % (LFM_NAME, VERSION, DATE, AUTHOR, prog)
 
711
 
 
712
 
 
713
def lfm_exit(ret_code, ret_path='.'):
 
714
    f = open('/tmp/lfm-%s.path' % (os.getppid()), 'w')
 
715
    f.write(ret_path)
 
716
    f.close()
 
717
    sys.exit(ret_code)
 
718
 
 
719
 
 
720
def main(win, path, npanels):
 
721
    global app
 
722
 
 
723
    app = Lfm(win, path, npanels)
 
724
    if app == OSError:
 
725
        sys.exit(-1)
 
726
    ret = app.run()
 
727
    return ret
 
728
 
 
729
    
 
730
def LfmApp(sysargs):
 
731
    import getopt
 
732
 
 
733
    # defaults
 
734
    DEBUG = 0
 
735
    npanels = 2
 
736
    paths = []
 
737
   
 
738
    # args
 
739
    try:
 
740
        opts, args = getopt.getopt(sysargs[1:], '12dh',
 
741
                                   ['', '', 'debug', 'help'])
 
742
    except getopt.GetoptError:
 
743
        usage(sysargs[0], 'Bad argument(s)')
 
744
        lfm_exit(-1)
 
745
    for o, a in opts:
 
746
        if o == '-1':
 
747
            npanels = 1
 
748
        if o == '-2':
 
749
            npanels = 2
 
750
        if o in ('-d', '--debug'):
 
751
            DEBUG = 1
 
752
        if o in ('-h', '--help'):
 
753
            usage(sysargs[0])
 
754
            lfm_exit(2)
 
755
 
 
756
    if len(args) == 0:
 
757
        paths.append(os.path.abspath('.'))
 
758
        paths.append(os.path.abspath('.'))
 
759
    elif len(args) == 1:
 
760
        buf = os.path.abspath(args[0])
 
761
        if not os.path.isdir(buf):
 
762
            if os.path.isfile(buf):
 
763
                # edit/view file
 
764
                pass
 
765
            else:
 
766
                usage(sysargs[0], '<%s> is not a file or directory' % args[0])
 
767
                lfm_exit(-1)
 
768
        paths.append(buf)
 
769
        paths.append(os.path.abspath('.'))
 
770
    elif len(args) == 2:
 
771
        buf = os.path.abspath(args[0])
 
772
        if not os.path.isdir(buf):
 
773
            usage(sysargs[0], '<%s> is not a file or directory' % args[0])
 
774
            lfm_exit(-1)
 
775
        paths.append(buf)
 
776
        buf = os.path.abspath(args[1])
 
777
        if not os.path.isdir(buf):
 
778
            usage(sysargs[0], '<%s> is not a file or directory' % args[1])
 
779
            lfm_exit(-1)
 
780
        paths.append(buf)
 
781
    else:
 
782
        usage(sysargs[0], 'Incorrect number of arguments')
 
783
        lfm_exit(-1)
 
784
 
 
785
    DEBUGFILE = './lfm-log.debug'
 
786
    if DEBUG:
 
787
        debug = open(DEBUGFILE, 'w')
 
788
        debug.write('********** Start:   ')
 
789
        debug.write(time.ctime(time.time()) + ' **********\n')
 
790
        sys.stdout = debug
 
791
        sys.stderr = debug
 
792
 
 
793
    path = curses.wrapper(main, paths, npanels)
 
794
 
 
795
    if DEBUG:
 
796
        debug.write('********** End:     ')
 
797
        debug.write(time.ctime(time.time()) + ' **********\n')
 
798
        debug.close()
 
799
 
 
800
    sys.stdout = sys.__stdout__
 
801
    sys.stdout = sys.__stderr__
 
802
 
 
803
    if path != None:
 
804
        lfm_exit(0, path)
 
805
    else:
 
806
        lfm_exit(0)
 
807
 
 
808
 
 
809
if __name__ == '__main__':
 
810
    LfmApp(sys.argv)