about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--Makefile4
-rw-r--r--doc/ranger.13
-rw-r--r--doc/ranger.pod7
-rw-r--r--doc/rifle.1223
-rw-r--r--doc/rifle.pod124
-rw-r--r--ranger/core/loader.py12
-rw-r--r--ranger/gui/ui.py4
-rw-r--r--ranger/gui/widgets/browserview.py7
-rw-r--r--ranger/gui/widgets/pager.py13
-rwxr-xr-xsetup.py2
10 files changed, 393 insertions, 6 deletions
diff --git a/Makefile b/Makefile
index a88a001f..2a4975ce 100644
--- a/Makefile
+++ b/Makefile
@@ -3,6 +3,8 @@
 
 NAME = ranger
 VERSION = $(shell grep -m 1 -o '[0-9][0-9.]\+' README)
+NAME_RIFLE = rifle
+VERSION_RIFLE = $(VERSION)
 SNAPSHOT_NAME ?= $(NAME)-$(VERSION)-$(shell git rev-parse HEAD | cut -b 1-8).tar.gz
 # Find suitable python version (need python >= 2.6 or 3.1):
 PYTHON ?= $(shell python -c 'import sys; sys.exit(sys.version < "2.6")' && \
@@ -66,6 +68,8 @@ test:
 man:
 	pod2man --stderr --center='ranger manual' --date='$(NAME)-$(VERSION)' \
 		--release=$(shell date +%x) doc/ranger.pod doc/ranger.1
+	pod2man --stderr --center='rifle manual' --date='$(NAME_RIFLE)-$(VERSION_RIFLE)' \
+		--release=$(shell date +%x) doc/rifle.pod doc/rifle.1
 
 manhtml:
 	pod2html doc/ranger.pod --outfile=doc/ranger.1.html
diff --git a/doc/ranger.1 b/doc/ranger.1
index b43fcc62..663b786a 100644
--- a/doc/ranger.1
+++ b/doc/ranger.1
@@ -1086,6 +1086,9 @@ copy, run:
 .Vb 1
 \& git clone git://git.savannah.nongnu.org/ranger.git
 .Ve
+.SH "SEE ALSO"
+.IX Header "SEE ALSO"
+\&\fIrifle\fR\|(1)
 .SH "BUGS"
 .IX Header "BUGS"
 Report bugs here: <http://savannah.nongnu.org/bugs/?group=ranger>
diff --git a/doc/ranger.pod b/doc/ranger.pod
index 1d9c022d..43275b95 100644
--- a/doc/ranger.pod
+++ b/doc/ranger.pod
@@ -1193,6 +1193,13 @@ copy, run:
 
 
 
+=head1 SEE ALSO
+
+rifle(1)
+
+
+
+
 =head1 BUGS
 
 Report bugs here: L<http://savannah.nongnu.org/bugs/?group=ranger>
diff --git a/doc/rifle.1 b/doc/rifle.1
new file mode 100644
index 00000000..2cfaed65
--- /dev/null
+++ b/doc/rifle.1
@@ -0,0 +1,223 @@
+.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.20)
+.\"
+.\" Standard preamble:
+.\" ========================================================================
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Vb \" Begin verbatim text
+.ft CW
+.nf
+.ne \\$1
+..
+.de Ve \" End verbatim text
+.ft R
+.fi
+..
+.\" Set up some character translations and predefined strings.  \*(-- will
+.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
+.\" double quote, and \*(R" will give a right double quote.  \*(C+ will
+.\" give a nicer C++.  Capital omega is used to do unbreakable dashes and
+.\" therefore won't be available.  \*(C` and \*(C' expand to `' in nroff,
+.\" nothing in troff, for use with C<>.
+.tr \(*W-
+.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
+.ie n \{\
+.    ds -- \(*W-
+.    ds PI pi
+.    if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
+.    if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\"  diablo 12 pitch
+.    ds L" ""
+.    ds R" ""
+.    ds C` ""
+.    ds C' ""
+'br\}
+.el\{\
+.    ds -- \|\(em\|
+.    ds PI \(*p
+.    ds L" ``
+.    ds R" ''
+'br\}
+.\"
+.\" Escape single quotes in literal strings from groff's Unicode transform.
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\"
+.\" If the F register is turned on, we'll generate index entries on stderr for
+.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
+.\" entries marked with X<> in POD.  Of course, you'll have to process the
+.\" output yourself in some meaningful fashion.
+.ie \nF \{\
+.    de IX
+.    tm Index:\\$1\t\\n%\t"\\$2"
+..
+.    nr % 0
+.    rr F
+.\}
+.el \{\
+.    de IX
+..
+.\}
+.\"
+.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
+.\" Fear.  Run.  Save yourself.  No user-serviceable parts.
+.    \" fudge factors for nroff and troff
+.if n \{\
+.    ds #H 0
+.    ds #V .8m
+.    ds #F .3m
+.    ds #[ \f1
+.    ds #] \fP
+.\}
+.if t \{\
+.    ds #H ((1u-(\\\\n(.fu%2u))*.13m)
+.    ds #V .6m
+.    ds #F 0
+.    ds #[ \&
+.    ds #] \&
+.\}
+.    \" simple accents for nroff and troff
+.if n \{\
+.    ds ' \&
+.    ds ` \&
+.    ds ^ \&
+.    ds , \&
+.    ds ~ ~
+.    ds /
+.\}
+.if t \{\
+.    ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
+.    ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
+.    ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
+.    ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
+.    ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
+.    ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
+.\}
+.    \" troff and (daisy-wheel) nroff accents
+.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
+.ds 8 \h'\*(#H'\(*b\h'-\*(#H'
+.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
+.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
+.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
+.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
+.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
+.ds ae a\h'-(\w'a'u*4/10)'e
+.ds Ae A\h'-(\w'A'u*4/10)'E
+.    \" corrections for vroff
+.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
+.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
+.    \" for low resolution devices (crt and lpr)
+.if \n(.H>23 .if \n(.V>19 \
+\{\
+.    ds : e
+.    ds 8 ss
+.    ds o a
+.    ds d- d\h'-1'\(ga
+.    ds D- D\h'-1'\(hy
+.    ds th \o'bp'
+.    ds Th \o'LP'
+.    ds ae ae
+.    ds Ae AE
+.\}
+.rm #[ #] #H #V #F C
+.\" ========================================================================
+.\"
+.IX Title "RIFLE 1"
+.TH RIFLE 1 "rifle-1.5.5" "02/18/2013" "rifle manual"
+.\" For nroff, turn off justification.  Always turn off hyphenation; it makes
+.\" way too many mistakes in technical documents.
+.if n .ad l
+.nh
+.SH "NAME"
+rifle \- ranger's file opener
+.SH "SYNOPSIS"
+.IX Header "SYNOPSIS"
+\&\fBrifle\fR [\fB\-\-help\fR] [\fB\-f\fR \fI\s-1FLAGS\s0\fR] [\fB\-l\fR] [\fB\-p\fR \fI\s-1KEYWORD\s0\fR]
+[\fB\-w\fR \fI\s-1PROGRAM\s0\fR] \fIfiles\fR
+.SH "DESCRIPTION"
+.IX Header "DESCRIPTION"
+rifle is a powerful file executor that allows for complex file type checking,
+written to meet the needs of the file manager \fIranger\fR.  rifle's strength lies
+in automatically determining file types, depending on which programs are
+installed on the system, even without any user interaction.
+.SH "OPTIONS"
+.IX Header "OPTIONS"
+.IP "\fB\-f\fR \fI\s-1FLAGS\s0\fR" 14
+.IX Item "-f FLAGS"
+Specify flags for opening the files.  Flags are letters that changes how the
+program is executed.  Any combination of flags will work.  Writing uppercase
+flags will negate the effect of all previously used lowercase flags of the same
+letter.
+.Sp
+Table of all flags:
+ f   fork program to background
+ r   run program as root, using sudo
+ t   run program in a separate terminal, as specified by \f(CW$TERMCMD\fR
+.IP "\fB\-l\fR" 14
+.IX Item "-l"
+List all possible ways to open the specified files.  Each line will contain information in the format of \fIid:label:flags:command\fR. \fIid\fR is the identification number. \fIlabel\fR is an arbitrary string that was specified for this command, \fIflags\fR are the flags that are used by default, and \fIcommand\fR is the command that is going to be executed.
+.IP "\fB\-p\fR \fI\s-1KEYWORD\s0\fR" 14
+.IX Item "-p KEYWORD"
+Pick a method to open the files.
+.Sp
+\&\fI\s-1KEYWORD\s0\fR is either the \s-1ID\s0 number listed by \f(CW\*(C`rifle \-l\*(C'\fR or a string that matches a label in the configuration file.
+.IP "\fB\-w\fR \fI\s-1PROGRAM\s0\fR" 14
+.IX Item "-w PROGRAM"
+Open the files with the program \fI\s-1PROGRAM\s0\fR
+.IP "\fB\-h\fR, \fB\-\-help\fR" 14
+.IX Item "-h, --help"
+Print a list of options and exit.
+.SH "FILES"
+.IX Header "FILES"
+rifle shares configuration files with ranger, though ranger is not required in
+order to use rifle.  The configuration file \fIrifle.conf\fR is expected to be at
+\&\fI~/.config/ranger/rifle.conf\fR.
+.PP
+This file specifies patterns for determining the commands to open files with.
+The syntax is described in the comments of the default \fIrifle.conf\fR that ships
+with ranger.  To obtain it, you need to run: \f(CW\*(C`ranger \-\-copy\-config=rifle\*(C'\fR
+.SH "ENVIRONMENT"
+.IX Header "ENVIRONMENT"
+.IP "\s-1EDITOR\s0" 8
+.IX Item "EDITOR"
+Determines which editor to use for editing files (in the default \fIrifle.conf\fR).
+.IP "\s-1PAGER\s0" 8
+.IX Item "PAGER"
+Determines which pager to use for displaying files (in the default \fIrifle.conf\fR).
+.IP "\s-1TERMCMD\s0" 8
+.IX Item "TERMCMD"
+Determines the terminal emulator command for use with the \fIt\fR flag.  It is required that the value is the path to an executable file which accepts the \*(L"\-e \s-1COMMAND\s0\*(R" argument.
+.IP "\s-1XDG_CONFIG_HOME\s0" 8
+.IX Item "XDG_CONFIG_HOME"
+Specifies the directory for configuration files. Defaults to \fI\f(CI$HOME\fI/.config\fR.
+.SH "EXAMPLES"
+.IX Header "EXAMPLES"
+List all the different methods:
+.PP
+.Vb 4
+\& $ rifle \-l helloworld.py
+\& 0:editor::"$EDITOR" \-\- "$@"
+\& 1:pager::"$PAGER" \-\- "$@"
+\& 2:::python \-\- "$1"
+.Ve
+.PP
+Display its content by opening it with \*(L"cat\*(R":
+.PP
+.Vb 2
+\& $ rifle \-w cat helloworld.py
+\& print("Hello World!")
+.Ve
+.PP
+Run it by picking the method 2, which calls 'python \*(-- \*(L"$1\*(R"':
+.PP
+.Vb 2
+\& $ rifle \-p 2 helloworld.py
+\& Hello World!
+.Ve
+.PP
+Display the file in a pager inside a new terminal, run as root:
+.PP
+.Vb 1
+\& $ rifle \-p 1 \-f tr helloworld.py
+.Ve
diff --git a/doc/rifle.pod b/doc/rifle.pod
new file mode 100644
index 00000000..ea04c5ee
--- /dev/null
+++ b/doc/rifle.pod
@@ -0,0 +1,124 @@
+=head1 NAME
+
+rifle - ranger's file opener
+
+
+
+
+=head1 SYNOPSIS
+
+B<rifle> [B<--help>] [B<-f> I<FLAGS>] [B<-l>] [B<-p> I<KEYWORD>]
+[B<-w> I<PROGRAM>] I<files>
+
+
+
+
+=head1 DESCRIPTION
+
+rifle is a powerful file executor that allows for complex file type checking,
+written to meet the needs of the file manager I<ranger>.  rifle's strength lies
+in automatically determining file types, depending on which programs are
+installed on the system, even without any user interaction.
+
+
+
+
+=head1 OPTIONS
+
+=over 14
+
+=item B<-f> I<FLAGS>
+
+Specify flags for opening the files.  Flags are letters that changes how the
+program is executed.  Any combination of flags will work.  Writing uppercase
+flags will negate the effect of all previously used lowercase flags of the same
+letter.
+
+Table of all flags:
+ f   fork program to background
+ r   run program as root, using sudo
+ t   run program in a separate terminal, as specified by $TERMCMD
+
+=item B<-l>
+
+List all possible ways to open the specified files.  Each line will contain information in the format of I<id:label:flags:command>. I<id> is the identification number. I<label> is an arbitrary string that was specified for this command, I<flags> are the flags that are used by default, and I<command> is the command that is going to be executed.
+
+=item B<-p> I<KEYWORD>
+
+Pick a method to open the files.
+
+I<KEYWORD> is either the ID number listed by C<rifle -l> or a string that matches a label in the configuration file.
+
+=item B<-w> I<PROGRAM>
+
+Open the files with the program I<PROGRAM>
+
+=item B<-h>, B<--help>
+
+Print a list of options and exit.
+
+=back
+
+
+
+
+=head1 FILES
+
+rifle shares configuration files with ranger, though ranger is not required in
+order to use rifle.  The configuration file F<rifle.conf> is expected to be at
+F<~/.config/ranger/rifle.conf>.
+
+This file specifies patterns for determining the commands to open files with.
+The syntax is described in the comments of the default F<rifle.conf> that ships
+with ranger.  To obtain it, you need to run: C<ranger --copy-config=rifle>
+
+
+
+
+=head1 ENVIRONMENT
+
+=over 8
+
+=item EDITOR
+
+Determines which editor to use for editing files (in the default F<rifle.conf>).
+
+=item PAGER
+
+Determines which pager to use for displaying files (in the default F<rifle.conf>).
+
+=item TERMCMD
+
+Determines the terminal emulator command for use with the I<t> flag.  It is required that the value is the path to an executable file which accepts the "-e COMMAND" argument.
+
+=item XDG_CONFIG_HOME
+
+Specifies the directory for configuration files. Defaults to F<$HOME/.config>.
+
+=back
+
+
+
+
+=head1 EXAMPLES
+
+List all the different methods:
+
+ $ rifle -l helloworld.py
+ 0:editor::"$EDITOR" -- "$@"
+ 1:pager::"$PAGER" -- "$@"
+ 2:::python -- "$1"
+
+Display its content by opening it with "cat":
+
+ $ rifle -w cat helloworld.py
+ print("Hello World!")
+
+Run it by picking the method 2, which calls 'python -- "$1"':
+
+ $ rifle -p 2 helloworld.py
+ Hello World!
+
+Display the file in a pager inside a new terminal, run as root:
+
+ $ rifle -p 1 -f tr helloworld.py
diff --git a/ranger/core/loader.py b/ranger/core/loader.py
index 926c11d1..e968b2eb 100644
--- a/ranger/core/loader.py
+++ b/ranger/core/loader.py
@@ -204,7 +204,12 @@ class CommandLoader(Loadable, SignalDispatcher, FileManagerAware):
         if not self.finished and not self.paused:
             if self.kill_on_pause:
                 self.finished = True
-                self.process.kill()
+                try:
+                    self.process.kill()
+                except OSError:
+                    # probably a race condition where the process finished
+                    # between the last poll()ing and this point.
+                    pass
                 return
             try:
                 self.process.send_signal(20)
@@ -225,7 +230,10 @@ class CommandLoader(Loadable, SignalDispatcher, FileManagerAware):
     def destroy(self):
         self.signal_emit('destroy', process=self.process, loader=self)
         if self.process:
-            self.process.kill()
+            try:
+                self.process.kill()
+            except OSError:
+                pass
 
 
 def safeDecode(string):
diff --git a/ranger/gui/ui.py b/ranger/gui/ui.py
index f35b11bf..5760090c 100644
--- a/ranger/gui/ui.py
+++ b/ranger/gui/ui.py
@@ -119,8 +119,8 @@ class UI(DisplayableContainer):
 
     def destroy(self):
         """Destroy all widgets and turn off curses"""
-        self.suspend()
         DisplayableContainer.destroy(self)
+        self.suspend()
 
     def handle_mouse(self):
         """Handles mouse input"""
@@ -321,6 +321,7 @@ class UI(DisplayableContainer):
         self.browser.visible = True
 
     def open_pager(self):
+        self.browser.columns[-1].clear_image(force=True)
         if self.console.focused:
             self.console.focused = False
         self.pager.open()
@@ -351,6 +352,7 @@ class UI(DisplayableContainer):
         self.close_pager()
 
     def open_taskview(self):
+        self.browser.columns[-1].clear_image(force=True)
         self.pager.close()
         self.pager.visible = False
         self.pager.focused = False
diff --git a/ranger/gui/widgets/browserview.py b/ranger/gui/widgets/browserview.py
index 742bd9d4..e0a2275e 100644
--- a/ranger/gui/widgets/browserview.py
+++ b/ranger/gui/widgets/browserview.py
@@ -170,6 +170,7 @@ class BrowserView(Widget, DisplayableContainer):
         self.addch(self.hei - 1, right_end, curses.ACS_LRCORNER)
 
     def _draw_bookmarks(self):
+        self.columns[-1].clear_image(force=True)
         self.fm.bookmarks.update_if_outdated()
         self.color_reset()
         self.need_clear = True
@@ -194,6 +195,7 @@ class BrowserView(Widget, DisplayableContainer):
         self.win.chgat(ystart - 1, 0, curses.A_UNDERLINE)
 
     def _draw_info(self, lines):
+        self.columns[-1].clear_image(force=True)
         self.need_clear = True
         hei = min(self.hei - 1, len(lines))
         ystart = self.hei - hei
@@ -207,6 +209,7 @@ class BrowserView(Widget, DisplayableContainer):
             i += 1
 
     def _draw_hints(self):
+        self.columns[-1].clear_image(force=True)
         self.need_clear = True
         hints = []
         for k, v in self.fm.ui.keybuffer.pointer.items():
@@ -341,4 +344,8 @@ class BrowserView(Widget, DisplayableContainer):
             self.columns[-1].visible = True
 
         if self.preview and self.is_collapsed != self._collapse():
+            if (self.fm.settings.preview_images and
+                self.fm.settings.preview_files):
+                # force clearing the image when resizing preview column
+                self.columns[-1].clear_image(force=True)
             self.resize(self.y, self.x, self.hei, self.wid)
diff --git a/ranger/gui/widgets/pager.py b/ranger/gui/widgets/pager.py
index 8bfdf42d..de73ddc2 100644
--- a/ranger/gui/widgets/pager.py
+++ b/ranger/gui/widgets/pager.py
@@ -30,6 +30,7 @@ class Pager(Widget):
         self.markup = None
         self.lines = []
         self.image = None
+        self.image_drawn = False
 
     def open(self):
         self.scroll_begin = 0
@@ -38,10 +39,11 @@ class Pager(Widget):
         self.startx = 0
         self.need_redraw = True
 
-    def clear_image(self):
-        if self.need_clear_image:
+    def clear_image(self, force=False):
+        if force or self.need_clear_image:
             img_display.clear(self.x, self.y, self.wid, self.hei)
             self.need_clear_image = False
+            self.image_drawn = False
 
     def close(self):
         if self.image:
@@ -50,6 +52,10 @@ class Pager(Widget):
         if self.source and self.source_is_stream:
             self.source.close()
 
+    def destroy(self):
+        if self.image_drawn:
+            img_display.clear(self.x, self.y, self.wid, self.hei)
+
     def finalize(self):
         self.fm.ui.win.move(self.y, self.x)
 
@@ -65,8 +71,10 @@ class Pager(Widget):
                 self.old_startx != self.startx:
             self.old_startx = self.startx
             self.old_scroll_begin = self.scroll_begin
+            self.need_redraw = True
 
         if self.need_redraw:
+            self.win.erase()
             self.need_redraw_image = True
             self.clear_image()
 
@@ -91,6 +99,7 @@ class Pager(Widget):
                         descr="loading preview image",
                         silent=True, kill_on_pause=True)
                 self.fm.loader.add(cmd)
+                self.image_drawn = True
             except img_display.ImgDisplayUnsupportedException:
                 self.fm.settings.preview_images = False
             except Exception as e:
diff --git a/setup.py b/setup.py
index 5552257c..b4be9965 100755
--- a/setup.py
+++ b/setup.py
@@ -16,7 +16,7 @@ if __name__ == '__main__':
         license=ranger.__license__,
         url='http://savannah.nongnu.org/projects/ranger',
         scripts=['scripts/ranger', 'scripts/rifle'],
-        data_files=[('share/man/man1', ['doc/ranger.1'])],
+        data_files=[('share/man/man1', ['doc/ranger.1', 'doc/rifle.1'])],
         package_data={'ranger': ['data/*', 'config/rc.conf',
             'config/rifle.conf']},
         packages=('ranger',