Download the script here: relister.tgz
Source Code follows:
#!/usr/bin/env python
#Written by Jarrod Lombardo,
#Copylefted under GPL, 2004 (http://www.gnu.org/copyleft/gpl.html)
#relister.py Usage documentation
#executable input_file output_file
#Documentation:
#Key stroke Function
#q quit, discarding changes
#w write and quit
#up-arrow move cursor up
#down-arrow move cursor down
#left-arrow move item up
#right-arrow move item down
#definitions
#input-file: a text file which is a list, separated by '\n'
#output-file: a text file which is a list, separated by '\n',
# generated by this program
import sys, curses, os.path
# - - - - - o p e n f i l e s - - - - -
def openfiles(argv):
"""Open the 2 files named in argv[1] and argv[2] or error
"""
#check if the args are there
if (len(argv) != 3):
sys.stderr.write("Usage " + argv[0] + " input_file output_file\n")
sys.exit(0)
#error if the files are the same
if (os.path.realpath(argv[1]) == os.path.realpath(argv[2])):
sys.stderr.write("Usage " + argv[0] + " input_file output_file\n")
sys.stderr.write(
"input_file and output_file must be different files.\n")
sys.stdout.write(
"Please re-run the program with a different output file.\n")
sys.exit(0)
#prompt if the output_file exists
if os.path.isfile(argv[2]):
sys.stdout.write("\n" + os.path.basename(argv[2]) + " exists.\n")
ynval = raw_input("Would you like to overwrite it (y/n)?")
if (ynval[0].upper() != 'Y'):
sys.stdout.write(
"Please re-run the program with a different output file.\n")
sys.exit(0)
#actually open the files
return file(argv[1]), file(argv[2], 'w')
# - - - - - i n i t i a l i z e E v e r y t h i n g - - - - -
def initialize(stdscr, itemlist):
"""Initialize the screen and all of the positions.
"""
ymax, xmax = stdscr.getmaxyx() #the size of the window
stdscr.move(0, 0) #move the the top left corner (it should already
#be there (at 0,0), but do it just in case).
#Print ymax lines to the screen
yoffset = 0
for line in itemlist:
if (yoffset <= ymax - 2):
#it's really anoying, but the next 2 lines must be
#separate for it to work.
line = '%.4d ' + line
line = line % (yoffset)
stdscr.addnstr(yoffset, 0, line, xmax - 1)
#move down one line
yoffset += 1
else:
break
#create the cursor (highlight) initial position
line = '0000 ' + itemlist[0]
stdscr.addnstr(0, 0, line, xmax - 1, curses.A_REVERSE)
stdscr.move(0, 0)
#refresh the screen
stdscr.refresh()
return (stdscr, itemlist, 0)
# - - - - - c u r d o w n - - - - -
def curdown(stdscr, itemlist, listpos):
"""Move the cursor down one line.
"""
#if at bottom of list, do nothing
if (len(itemlist) - 1 == listpos):
return (stdscr, itemlist, listpos)
ymax, xmax = stdscr.getmaxyx()
ycur, xcur = stdscr.getyx()
#if at bottom of screen
if (ycur == ymax - 2):
#shift all items up one line on the screen
stdscr.move(0,0)
stdscr.deleteln() #removes the line at 0,0 and moves all
#other lines up one
#display itemlist[listpos] as A_NORMAL
line1 = '%.4d ' + itemlist[listpos]
line1 = line1 % listpos
stdscr.addnstr(ycur - 1, 0, line1, xmax - 1)
#display itemlist[listpos + 1] as A_REVERSE
listpos = listpos + 1
line2 = '%.4d ' + itemlist[listpos]
line2 = line2 % listpos
stdscr.addnstr(ycur, 0, line2, xmax - 1, curses.A_REVERSE)
stdscr.move(ycur, 0)
#if anywhere else
else:
line1 = '%.4d ' + itemlist[listpos]
line1 = line1 % listpos
stdscr.addnstr(ycur, 0, line1, xmax - 1)
listpos += 1
line2 = '%.4d ' + itemlist[listpos]
line2 = line2 % listpos
stdscr.addnstr(ycur + 1, 0, line2, xmax - 1, curses.A_REVERSE)
stdscr.move(ycur + 1, 0)
return (stdscr, itemlist, listpos)
# - - - - - c u r u p - - - - -
def curup(stdscr, itemlist, listpos):
"""Move the cursor up one line.
"""
#if at top of list, do nothing
if (0 == listpos):
return (stdscr, itemlist, listpos)
ymax, xmax = stdscr.getmaxyx()
ycur, xcur = stdscr.getyx()
#if at top of screen
if (ycur == 0):
#shift all items down one line on the screen
stdscr.move(0, 0)
stdscr.insertln() #inserts a blank line at 0,0 and moves all other
#lines down one
stdscr.move(ymax-1, 0)
stdscr.deleteln()
#display itemlist[listpos] as A_NORMAL
line1 = '%.4d ' + itemlist[listpos]
line1 = line1 % listpos
stdscr.addnstr(ycur + 1, 0, line1, xmax - 1, curses.A_NORMAL)
#display itemlist[listpos + 1] as A_REVERSE
listpos -= 1
line2 = '%.4d ' + itemlist[listpos]
line2 = line2 % listpos
stdscr.addnstr(ycur, 0, line2, xmax - 1, curses.A_REVERSE)
stdscr.move(ycur, 0)
#if anywhere else
else:
line1 = '%.4d ' + itemlist[listpos]
line1 = line1 % listpos
stdscr.addnstr(ycur, 0, line1, xmax - 1)
listpos -= 1
line2 = '%.4d ' + itemlist[listpos]
line2 = line2 % listpos
stdscr.addnstr(ycur - 1, 0, line2, xmax - 1, curses.A_REVERSE)
stdscr.move(ycur - 1, 0)
return (stdscr, itemlist, listpos)
# - - - - - i t e m u p - - - - -
def itemup(stdscr, itemlist, listpos):
"""Move the current item and cursor up one line.
"""
#if at top of list, do nothing
if (0 == listpos):
return (stdscr, itemlist, listpos)
ymax, xmax = stdscr.getmaxyx()
ycur, xcur = stdscr.getyx()
#swap the current item up one position
movingitem = itemlist[listpos]
itemlist[listpos] = itemlist[listpos - 1]
itemlist[listpos - 1] = movingitem
#if at top of screen
if (ycur == 0):
#clean up the top line and shift all items down one line on the screen
stdscr.move(1,0)
stdscr.deleteln()
stdscr.insertln()
stdscr.insertln()
stdscr.move(ymax-1, 0)
stdscr.deleteln()
#display itemlist[listpos + 1] as A_NORMAL
listpos += 1
line1 = '%.4d ' + itemlist[listpos]
line1 = line1 % listpos
stdscr.addnstr(ycur + 2, 0, line1, xmax - 1)
#display itemlist[listpos] as A_NORMAL
listpos -= 1
line2 = '%.4d ' + itemlist[listpos]
line2 = line2 % listpos
stdscr.addnstr(ycur + 1, 0, line2, xmax - 1)
#display itemlist[listpos - 1] as A_REVERSE
listpos -= 1
line3 = '%.4d ' + itemlist[listpos]
line3 = line3 % listpos
stdscr.addnstr(ycur, 0, line3, xmax - 1, curses.A_REVERSE)
stdscr.move(ycur, 0)
#if anywhere else
else:
#clean up the 2 lines being swapped
stdscr.move(ycur - 1, 0)
stdscr.deleteln()
stdscr.deleteln()
stdscr.insertln()
stdscr.insertln()
line1 = '%.4d ' + itemlist[listpos]
line1 = line1 % listpos
stdscr.addnstr(ycur, 0, line1, xmax - 1)
listpos -= 1
line2 = '%.4d ' + itemlist[listpos]
line2 = line2 % listpos
stdscr.addnstr(ycur - 1, 0, line2, xmax - 1, curses.A_REVERSE)
stdscr.move(ycur - 1, 0)
return (stdscr, itemlist, listpos)
# - - - - - i t e m d o w n - - - - -
def itemdown(stdscr, itemlist, listpos):
"""Move the current item and cursor down one line.
"""
#if at bottom of list, do nothing
if (len(itemlist) - 1 == listpos):
return (stdscr, itemlist, listpos)
ymax, xmax = stdscr.getmaxyx()
ycur, xcur = stdscr.getyx()
#swap the current item up one position
movingitem = itemlist[listpos]
itemlist[listpos] = itemlist[listpos + 1]
itemlist[listpos + 1] = movingitem
#if at bottom of screen
if (ycur == ymax - 2):
#shift all items up one line on the screen
stdscr.move(0,0)
stdscr.deleteln() #removes the line at 0,0 and moves all
#other lines up one
#clean up the 2 lines being swapped
stdscr.move(ycur - 1, 0)
stdscr.deleteln()
stdscr.deleteln()
stdscr.insertln()
stdscr.insertln()
#display itemlist[listpos] as A_NORMAL
line1 = '%.4d ' + itemlist[listpos]
line1 = line1 % listpos
stdscr.addnstr(ycur - 1, 0, line1, xmax - 1)
#display itemlist[listpos + 1] as A_REVERSE
listpos += 1
line2 = '%.4d ' + itemlist[listpos]
line2 = line2 % listpos
stdscr.addnstr(ycur, 0, line2, xmax - 1, curses.A_REVERSE)
stdscr.move(ycur, 0)
#if anywhere else
else:
#clean up the 2 lines being swapped
stdscr.move(ycur, 0)
stdscr.deleteln()
stdscr.deleteln()
stdscr.insertln()
stdscr.insertln()
line1 = '%.4d ' + itemlist[listpos]
line1 = line1 % listpos
stdscr.addnstr(ycur, 0, line1, xmax - 1)
listpos += 1
line2 = '%.4d ' + itemlist[listpos]
line2 = line2 % listpos
stdscr.addnstr(ycur + 1, 0, line2, xmax - 1, curses.A_REVERSE)
stdscr.move(ycur + 1, 0)
return (stdscr, itemlist, listpos)
# - - - - - c u r s e s h a n d l e r - - - - -
def curseshandler(stdscr, itemlist):
"""do curses crap, and return the reordered list
"""
#display itemlist and initialise everything
updateneeded = 0
stdscr, itemlist, listpos = initialize(stdscr, itemlist)
#wait for a signal
while (1):
c=stdscr.getch() # Get a keystroke
#call whatever function is appropriate
if (c == curses.KEY_DOWN):
stdscr, itemlist, listpos = curdown(stdscr, itemlist, listpos)
updateneeded = 1
elif (c == curses.KEY_UP):
stdscr, itemlist, listpos = curup(stdscr, itemlist, listpos)
updateneeded = 1
elif (c == curses.KEY_LEFT):
stdscr, itemlist, listpos = itemup(stdscr, itemlist, listpos)
updateneeded = 1
elif (c == curses.KEY_RIGHT):
stdscr, itemlist, listpos = itemdown(stdscr, itemlist, listpos)
updateneeded = 1
elif (c == ord('w')):
return itemlist
elif (c == ord('q')):
return None
#update the screen if necessary
if (updateneeded == 1):
#update the screen
stdscr.refresh()
updateneeded = 0
# - - - - - m a i n - - - - -
#Take a input-file and return an output-file
#open files cleanly or error
infile, outfile = openfiles(sys.argv)
#read data from file into a list
itemlist = infile.readlines()
#curses
itemlist = curses.wrapper(curseshandler, itemlist)
#write data to file
if (itemlist != None):
print "\nWriting list to file."
outfile.writelines(itemlist)
else:
print "\nDiscarding changes to list."
#close our files
infile.close()
outfile.close()