fslint-gui (fslint-2.44) | : | fslint-gui (fslint-2.46) | ||
---|---|---|---|---|
#!/usr/bin/env python | #!/usr/bin/env python2 | |||
# vim:fileencoding=utf-8 | # vim:fileencoding=utf-8 | |||
# Note both python and vim understand the above encoding declaration | # Note both python and vim understand the above encoding declaration | |||
# Note using env above will cause the system to register | # Note using env above will cause the system to register | |||
# the name (in ps etc.) as "python" rather than fslint-gui | # the name (in ps etc.) as "python" rather than fslint-gui | |||
# (because "python" is passed to exec by env). | # (because "python" is passed to exec by env). | |||
""" | """ | |||
FSlint - A utility to find File System lint. | FSlint - A utility to find File System lint. | |||
Copyright © 2000-2010 by Pádraig Brady <P@draigBrady.com>. | Copyright © 2000-2014 by Pádraig Brady <P@draigBrady.com>. | |||
This program is free software; you can redistribute it and/or modify | This program is free software; you can redistribute it and/or modify | |||
it under the terms of the GNU General Public License as published by | it under the terms of the GNU General Public License as published by | |||
the Free Software Foundation; either version 2 of the License, or | the Free Software Foundation; either version 2 of the License, or | |||
any later version. | any later version. | |||
This program is distributed in the hope that it will be useful, | This program is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | |||
See the GNU General Public License for more details, | See the GNU General Public License for more details, | |||
which is available at www.gnu.org | which is available at www.gnu.org | |||
""" | """ | |||
import types, os, sys, pipes, time, stat, tempfile, errno | ||||
import gettext | import gettext | |||
import locale | import locale | |||
import gtk | ||||
import gtk.glade | ||||
import types, os, sys, pipes, time, stat, tempfile, errno | try: | |||
import gtk | ||||
except RuntimeError: | ||||
etype, emsg, etb = sys.exc_info() | ||||
sys.stderr.write(str(emsg)+'\n') | ||||
sys.exit(1) | ||||
import gtk.glade | ||||
time_commands=False #print sub commands timing on status line | time_commands=False #print sub commands timing on status line | |||
def puts(string=""): #print is problematic in python 3 | def puts(string=""): #print is problematic in python 3 | |||
sys.stdout.write(str(string)+'\n') | sys.stdout.write(str(string)+'\n') | |||
liblocation=os.path.dirname(os.path.abspath(sys.argv[0])) | liblocation=os.path.dirname(os.path.abspath(sys.argv[0])) | |||
locale_base=liblocation+'/po/locale' | locale_base=liblocation+'/po/locale' | |||
try: | try: | |||
import fslint | import fslint | |||
skipping to change at line 180 | skipping to change at line 188 | |||
lOpts, lArgs = getopt.getopt(sys.argv[1:], "", ["help","version"]) | lOpts, lArgs = getopt.getopt(sys.argv[1:], "", ["help","version"]) | |||
if len(lArgs) == 0: | if len(lArgs) == 0: | |||
lArgs = [os.getcwd()] | lArgs = [os.getcwd()] | |||
if ("--help","") in lOpts: | if ("--help","") in lOpts: | |||
Usage() | Usage() | |||
sys.exit(None) | sys.exit(None) | |||
if ("--version","") in lOpts: | if ("--version","") in lOpts: | |||
puts("FSlint 2.44") | puts("FSlint 2.46") | |||
sys.exit(None) | sys.exit(None) | |||
except getopt.error, msg: | except getopt.error, msg: | |||
puts(msg) | puts(msg) | |||
puts() | puts() | |||
Usage() | Usage() | |||
sys.exit(2) | sys.exit(2) | |||
def human_num(num, divisor=1, power=""): | def human_num(num, divisor=1, power=""): | |||
num=float(num) | num=float(num) | |||
if divisor == 1: | if divisor == 1: | |||
skipping to change at line 402 | skipping to change at line 410 | |||
# Determine what type of distro we're on. | # Determine what type of distro we're on. | |||
class distroType: | class distroType: | |||
def __init__(self): | def __init__(self): | |||
types = ("rpm", "dpkg", "pacman") | types = ("rpm", "dpkg", "pacman") | |||
for dist in types: | for dist in types: | |||
setattr(self, dist, False) | setattr(self, dist, False) | |||
if os.path.exists("/etc/redhat-release"): | if os.path.exists("/etc/redhat-release"): | |||
self.rpm = True | self.rpm = True | |||
elif os.path.exists("/etc/debian_version"): | elif os.path.exists("/etc/debian_version"): | |||
self.dpkg = True | self.dpkg = True | |||
elif os.path.exists("/etc/arch-release"): | ||||
self.pacman = True | ||||
else: | else: | |||
for dist in types: | for dist in types: | |||
cmd = dist + " --version >/dev/null 2>&1" | cmd = dist + " --version >/dev/null 2>&1" | |||
# We check for != 127 rather than == 0 here | if os.WEXITSTATUS(os.system(cmd)) == 0: | |||
# because pacman --version returns 2 ? | ||||
if os.WEXITSTATUS(os.system(cmd)) != 127: | ||||
setattr(self, dist, True) | setattr(self, dist, True) | |||
break | break | |||
dist_type=distroType() | dist_type=distroType() | |||
def human_space_left(where): | def human_space_left(where): | |||
(device, total, used_b, avail, used_p, mount)=\ | (device, total, used_b, avail, used_p, mount)=\ | |||
os.popen("df -Ph %s | tail -n1" % where).read().split() | os.popen("df -Ph %s | tail -n1" % where).read().split() | |||
translation_map={"percent":used_p, "disk":mount, "size":avail} | translation_map={"percent":used_p, "disk":mount, "size":avail} | |||
return _("%(percent)s of %(disk)s is used leaving %(size)sB available") %\ | return _("%(percent)s of %(disk)s is used leaving %(size)sB available") %\ | |||
translation_map | translation_map | |||
# Avoid using clist.get_selectable() with is O(n) | # Avoid using clist.get_selectable() which is O(n) | |||
def get_selectable(row, row_data): | def get_selectable(row, row_data): | |||
return row_data[row][0] != '#' | return row_data[row][0] != '#' | |||
class dlgUserInteraction(GladeWrapper): | class dlgUserInteraction(GladeWrapper): | |||
""" | """ | |||
Note input buttons tuple text should not be translated | Note input buttons tuple text should not be translated | |||
so that the stock buttons are used if possible. But the | so that the stock buttons are used if possible. But the | |||
translations should be available, so use N_ for input | translations should be available, so use N_ for input | |||
buttons tuple text. Note the returned response is not | buttons tuple text. Note the returned response is not | |||
translated either. Note also, that if you know a stock | translated either. Note also, that if you know a stock | |||
skipping to change at line 512 | skipping to change at line 520 | |||
# to save results there | # to save results there | |||
self.GtkWindow.set_action(gtk.FILE_CHOOSER_ACTION_SAVE) | self.GtkWindow.set_action(gtk.FILE_CHOOSER_ACTION_SAVE) | |||
self.GtkWindow.set_current_folder(self.pwd) | self.GtkWindow.set_current_folder(self.pwd) | |||
self.GtkWindow.set_current_name(filename) | self.GtkWindow.set_current_name(filename) | |||
else: | else: | |||
self.GtkWindow.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER) | self.GtkWindow.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER) | |||
# set_filename doesn't highlight dir after first call | # set_filename doesn't highlight dir after first call | |||
# so using select_filename for consistency. Also I think | # so using select_filename for consistency. Also I think | |||
# select_filename is better than set_current_folder as | # select_filename is better than set_current_folder as | |||
# it allows one to more easily select items at the same level | # it allows one to more easily select items at the same level | |||
print self.pwd | ||||
self.GtkWindow.select_filename(self.pwd) | self.GtkWindow.select_filename(self.pwd) | |||
self.GtkWindow.set_transient_for(self.app.GtkWindow)#center on main win | self.GtkWindow.set_transient_for(self.app.GtkWindow)#center on main win | |||
self.GtkWindow.show() | self.GtkWindow.show() | |||
if self.GtkWindow.modal: | if self.GtkWindow.modal: | |||
gtk.main() #synchronous call from parent | gtk.main() #synchronous call from parent | |||
#else: not supported | #else: not supported | |||
################# | ################# | |||
# Signal handlers | # Signal handlers | |||
################# | ################# | |||
skipping to change at line 702 | skipping to change at line 709 | |||
return "" | return "" | |||
if self.ok_dirs.rows == 0: | if self.ok_dirs.rows == 0: | |||
return _("No search paths specified") | return _("No search paths specified") | |||
search_dirs = "" | search_dirs = "" | |||
for row in range(self.ok_dirs.rows): | for row in range(self.ok_dirs.rows): | |||
search_dirs += " " + pipes.quote(self.ok_dirs.get_row_data(row)) | search_dirs += " " + pipes.quote(self.ok_dirs.get_row_data(row)) | |||
exclude_dirs="" | exclude_dirs="" | |||
for row in range(self.bad_dirs.rows): | # One can't remove directories listed as empty if | |||
if exclude_dirs == "": | # they have .git/ dirs etc. so ignore the exluded paths here | |||
exclude_dirs = '\(' + ' -path ' | if self.mode != self.mode_ed: | |||
else: | for row in range(self.bad_dirs.rows): | |||
exclude_dirs += ' -o -path ' | if exclude_dirs == "": | |||
exclude_dirs += pipes.quote(self.bad_dirs.get_row_data(row)) | exclude_dirs = '\(' + ' -path ' | |||
else: | ||||
exclude_dirs += ' -o -path ' | ||||
exclude_dirs += pipes.quote(self.bad_dirs.get_row_data(row)) | ||||
if exclude_dirs != "": | if exclude_dirs != "": | |||
exclude_dirs += ' \) -prune -o ' | exclude_dirs += ' \) -prune -o ' | |||
if not self.recurseDirs.get_active(): | if not self.recurseDirs.get_active(): | |||
recurseParam=" -r " | recurseParam=" -r " | |||
else: | else: | |||
recurseParam=" " | recurseParam=" " | |||
self.findParams = search_dirs + " -f " + recurseParam + exclude_dirs | self.findParams = search_dirs + " -f " + recurseParam + exclude_dirs | |||
if self.mode == self.mode_up: | ||||
min_size = self.min_size.get_text() | ||||
if min_size == '0' or min_size == '': | ||||
min_size='' | ||||
elif min_size[-1].isdigit(): | ||||
min_size=str(int(min_size)-1) | ||||
min_size += 'c' | ||||
if min_size: | ||||
self.findParams += '-size +' + min_size + ' ' | ||||
self.findParams += self.extra_find_params.get_text() | self.findParams += self.extra_find_params.get_text() | |||
return "" | return "" | |||
def addDirs(self, dirlist, dirs): | def addDirs(self, dirlist, dirs): | |||
"""Adds items to passed clist.""" | """Adds items to passed clist.""" | |||
dirlist.append([I18N.displayable_utf8(dirs,path=True)]) | dirlist.append([I18N.displayable_utf8(dirs,path=True)]) | |||
dirlist.set_row_data(dirlist.rows-1, dirs) | dirlist.set_row_data(dirlist.rows-1, dirs) | |||
def removeDirs(self, dirlist): | def removeDirs(self, dirlist): | |||
skipping to change at line 784 | skipping to change at line 805 | |||
rows_left.reverse() | rows_left.reverse() | |||
for row in rows_left: | for row in rows_left: | |||
if not get_selectable(row, row_data): | if not get_selectable(row, row_data): | |||
if row == clist.rows-1 or not get_selectable(row+1, row_data): | if row == clist.rows-1 or not get_selectable(row+1, row_data): | |||
clist.remove(row) | clist.remove(row) | |||
row_data.pop(row) | row_data.pop(row) | |||
else: #remove single item groups | else: #remove single item groups | |||
if row == clist.rows-1 and not get_selectable(row-1, row_data): | if row == clist.rows-1 and not get_selectable(row-1, row_data): | |||
clist.remove(row) | clist.remove(row) | |||
row_data.pop(row) | row_data.pop(row) | |||
elif not (get_selectable(row-1, row_data) or | elif not ((row!=0 and get_selectable(row-1, row_data)) or | |||
get_selectable(row+1, row_data)): | (row!=clist.rows-1 and get_selectable(row+1, row_data) | |||
)): | ||||
clist.remove(row) | clist.remove(row) | |||
row_data.pop(row) | row_data.pop(row) | |||
def whatRequires(self, packages, level=0): | def whatRequires(self, packages, level=0): | |||
if not packages: return | if not packages: return | |||
if level==0: self.checked_pkgs={} | if level==0: self.checked_pkgs={} | |||
for package in packages: | for package in packages: | |||
#puts("\t"*level+package) | #puts("\t"*level+package) | |||
self.checked_pkgs[package]='' | self.checked_pkgs[package]='' | |||
if dist_type.rpm: | if dist_type.rpm: | |||
skipping to change at line 835 | skipping to change at line 856 | |||
#Package names unique on debian | #Package names unique on debian | |||
cmd = r"dpkg-query -W --showformat='${Package}\t${Installed-Size}" | cmd = r"dpkg-query -W --showformat='${Package}\t${Installed-Size}" | |||
cmd += r"\t${Status}\n' | LANG=C grep -F 'installed' | cut -f1,2 | " | cmd += r"\t${Status}\n' | LANG=C grep -F 'installed' | cut -f1,2 | " | |||
cmd += r"sed 's/[^0-9]$/\t0/'" #packages with missing size = 0 | cmd += r"sed 's/[^0-9]$/\t0/'" #packages with missing size = 0 | |||
elif dist_type.rpm: | elif dist_type.rpm: | |||
#Must include version names to uniquefy on redhat | #Must include version names to uniquefy on redhat | |||
cmd = r"rpm -qa --queryformat '%{N}-%{V}-%{R}.%{ARCH}\t%{SIZE}\n'" | cmd = r"rpm -qa --queryformat '%{N}-%{V}-%{R}.%{ARCH}\t%{SIZE}\n'" | |||
elif dist_type.pacman: | elif dist_type.pacman: | |||
cmd = r"pacman -Q | sed -n 's/^\([^ ]\{1,\}\) .*/\1/p' | " | cmd = r"pacman -Q | sed -n 's/^\([^ ]\{1,\}\) .*/\1/p' | " | |||
cmd += r"LC_ALL=C xargs pacman -Qi | sed -n -e " | cmd += r"LC_ALL=C xargs pacman -Qi | sed -n -e " | |||
cmd += r"'s/Name *: *\(.*\)/\1\t/p' -e " | cmd += r"'s/Installed Size : \([^ ]*\) \([^ ]*\)B/\1\2/p' | " | |||
cmd += r"'s/\(Installed \)\?Size *: *\([^ ]\+\)\( K\)\?/\2|/p' | " | cmd += r"numfmt --from=auto" | |||
cmd += r"tr '\n' ' ' | tr -d ' ' | tr '|' '\n'" | ||||
else: | else: | |||
return ("", _("Sorry, FSlint does not support this functionality \ | return ("", _("Sorry, FSlint does not support this functionality \ | |||
on your system at present.")) | on your system at present.")) | |||
po, pe = self.get_fslint(cmd + " | LANG=C sort -k2,2rn") | po, pe = self.get_fslint(cmd + " | LANG=C sort -k2,2rn") | |||
pkg_tot = 0 | pkg_tot = 0 | |||
row = 0 | row = 0 | |||
for pkg_info in po: | for pkg_info in po: | |||
pkg_name, pkg_size= pkg_info.split() | pkg_name, pkg_size= pkg_info.split() | |||
pkg_size = int(float(pkg_size)) | pkg_size = int(float(pkg_size)) | |||
if dist_type.dpkg: | if dist_type.dpkg: | |||
pkg_size *= 1024 | pkg_size *= 1024 | |||
elif dist_type.pacman: | ||||
frugalware_cmd="pacman-g2 --version >/dev/null 2>&1" | ||||
if os.WEXITSTATUS(os.system(frugalware_cmd)) == 127: | ||||
#Arch linux has trailing 'K' removed above | ||||
pkg_size *= 1024 | ||||
pkg_tot += pkg_size | pkg_tot += pkg_size | |||
clist_pkgs.append([pkg_name,human_num(pkg_size,1000).strip(), | clist_pkgs.append([pkg_name,human_num(pkg_size,1000).strip(), | |||
"%010ld"%pkg_size,"0"]) | "%010ld"%pkg_size,"0"]) | |||
clist_pkgs.set_row_data(row, pkg_name) | clist_pkgs.set_row_data(row, pkg_name) | |||
row += 1 | row += 1 | |||
return (str(row) + _(" packages, ") + | return (str(row) + _(" packages, ") + | |||
_("consuming %sB. ") % human_num(pkg_tot,1000).strip() + | _("consuming %sB. ") % human_num(pkg_tot,1000).strip() + | |||
_("Note %s.") % human_space_left('/'), pe) | _("Note %s.") % human_space_left('/'), pe) | |||
#Note pkgs generally installed on root partition so report space left. | #Note pkgs generally installed on root partition so report space left. | |||
skipping to change at line 939 | skipping to change at line 954 | |||
elif self.opt_bl_relative.get_active(): | elif self.opt_bl_relative.get_active(): | |||
cmd += " -l" | cmd += " -l" | |||
elif self.opt_bl_absolute.get_active(): | elif self.opt_bl_absolute.get_active(): | |||
cmd += " -A" | cmd += " -A" | |||
elif self.opt_bl_redundant.get_active(): | elif self.opt_bl_redundant.get_active(): | |||
cmd += " -n" | cmd += " -n" | |||
po, pe = self.get_fslint(cmd) | po, pe = self.get_fslint(cmd) | |||
row = 0 | row = 0 | |||
for line in po: | for line in po: | |||
link, target = line.split(" -> ", 2) | link, target = line.split(" -> ", 2) | |||
print target, ord(target) | ||||
self.clist_append_path(clist_bl, link, '', target) | self.clist_append_path(clist_bl, link, '', target) | |||
row += 1 | row += 1 | |||
return (str(row) + _(" links"), pe) | return (str(row) + _(" links"), pe) | |||
def findtf(self, clist_tf): | def findtf(self, clist_tf): | |||
cmd = "./findtf " + self.findParams | cmd = "./findtf " + self.findParams | |||
cmd += " --age=" + str(int(self.spin_tf_core.get_value())) | cmd += " --age=" + str(int(self.spin_tf_core.get_value())) | |||
if self.chk_tf_core.get_active(): | if self.chk_tf_core.get_active(): | |||
cmd += " -c" | cmd += " -c" | |||
skipping to change at line 1025 | skipping to change at line 1039 | |||
sensitivity, '\0') | sensitivity, '\0') | |||
row=0 | row=0 | |||
for record in po: | for record in po: | |||
self.clist_append_path(clist_nl, record, pathInfo(record).ls_colour) | self.clist_append_path(clist_nl, record, pathInfo(record).ls_colour) | |||
row += 1 | row += 1 | |||
return (str(row) + _(" files"), pe) | return (str(row) + _(" files"), pe) | |||
def findup(self, clist_dups): | def findup(self, clist_dups): | |||
po, pe = self.get_fslint("./findup " + self.findParams) | po, pe = self.get_fslint("./findup --gui" + self.findParams) | |||
numdups = 0 | numdups = 0 | |||
du = size = 0 | du = size = 0 | |||
dups = [] | dups = [] | |||
alldups = [] | alldups = [] | |||
inodes = {} | inodes = {} | |||
#inodes required to correctly report disk usage of | #inodes required to correctly report disk usage of | |||
#duplicate files with seperate inode groups. | #duplicate files with seperate inode groups. | |||
for line in po: | for line in po: | |||
if line == '': #grouped == 1 | if line == '': #grouped == 1 | |||
skipping to change at line 1237 | skipping to change at line 1251 | |||
else: | else: | |||
self.hscale_findnl_level.show() | self.hscale_findnl_level.show() | |||
self.lbl_findnl_sensitivity.show() | self.lbl_findnl_sensitivity.show() | |||
def on_fslint_functions_switch_page(self, widget, dummy, pagenum): | def on_fslint_functions_switch_page(self, widget, dummy, pagenum): | |||
self.ClearErrors() | self.ClearErrors() | |||
self.mode=pagenum | self.mode=pagenum | |||
self.status.set_text(self.mode_descs[self.mode]) | self.status.set_text(self.mode_descs[self.mode]) | |||
if self.mode == self.mode_up: | if self.mode == self.mode_up: | |||
self.autoMerge.show() | self.autoMerge.show() | |||
self.autoSymlink.show() | ||||
else: | else: | |||
self.autoMerge.hide() | self.autoMerge.hide() | |||
self.autoSymlink.hide() | ||||
if self.mode == self.mode_ns or self.mode == self.mode_rs: #bl in future | if self.mode == self.mode_ns or self.mode == self.mode_rs: #bl in future | |||
self.autoClean.show() | self.autoClean.show() | |||
else: | else: | |||
self.autoClean.hide() | self.autoClean.hide() | |||
def on_selection_menu_button_press_event(self, widget, event): | def on_selection_menu_button_press_event(self, widget, event): | |||
if self.mode == self.mode_up or self.mode == self.mode_sn: | if self.mode == self.mode_up or self.mode == self.mode_sn: | |||
self.groups_menu.show() | self.groups_menu.show() | |||
else: | else: | |||
self.groups_menu.hide() | self.groups_menu.hide() | |||
skipping to change at line 1291 | skipping to change at line 1307 | |||
clist = self.clists[self.mode] | clist = self.clists[self.mode] | |||
os.chdir(liblocation+"/fslint/") | os.chdir(liblocation+"/fslint/") | |||
errors = self.buildFindParameters() | errors = self.buildFindParameters() | |||
if errors: | if errors: | |||
self.ShowErrors(errors) | self.ShowErrors(errors) | |||
return | return | |||
self.status.delete_text(0,-1) | self.status.delete_text(0,-1) | |||
clist.clear() | clist.clear() | |||
#All GtkClist operations seem to be O(n), | #All GtkClist operations seem to be O(n), | |||
#so doing the following for example will be O((n/2)*(n+1)) | #so doing the following for example will be O((n/2)*(n+1)) | |||
# for row in range(clist.rows): | # for row in xrange(clist.rows): | |||
# path = clist.get_row_data(row) | # path = clist.get_row_data(row) | |||
#Therefore we use a python list to store row data. | #Therefore we use a python list to store row data. | |||
clist.set_data("row_data",[]) | clist.set_data("row_data",[]) | |||
if self.mode == self.mode_pkgs: | if self.mode == self.mode_pkgs: | |||
self.pkg_info.get_buffer().set_text("") | self.pkg_info.get_buffer().set_text("") | |||
self.look_busy() | self.look_busy() | |||
self.stopflag=self.pauseflag=False | self.stopflag=self.pauseflag=False | |||
self.paused = False | self.paused = False | |||
while gtk.events_pending(): gtk.main_iteration(False)#update GUI | while gtk.events_pending(): gtk.main_iteration(False)#update GUI | |||
clist.freeze() | clist.freeze() | |||
skipping to change at line 1420 | skipping to change at line 1436 | |||
self.ClearErrors() | self.ClearErrors() | |||
fileSaveAs = self.dlgPath.GtkWindow.get_filename() | fileSaveAs = self.dlgPath.GtkWindow.get_filename() | |||
try: | try: | |||
fileSaveAs = open(fileSaveAs, 'w') | fileSaveAs = open(fileSaveAs, 'w') | |||
except: | except: | |||
etype, emsg, etb = sys.exc_info() | etype, emsg, etb = sys.exc_info() | |||
self.ShowErrors(str(emsg)+'\n') | self.ShowErrors(str(emsg)+'\n') | |||
return | return | |||
rows_to_save = clist.selection | rows_to_save = clist.selection | |||
if self.mode == self.mode_pkgs: | if self.mode == self.mode_pkgs: | |||
for row in range(clist.rows): | for row in xrange(clist.rows): | |||
if len(rows_to_save): | if len(rows_to_save): | |||
if row not in rows_to_save: continue | if row not in rows_to_save: continue | |||
rowtext = '' | rowtext = '' | |||
for col in (0,2): #ignore "human number" col | for col in (0,2): #ignore "human number" col | |||
rowtext += clist.get_text(row,col) +'\t' | rowtext += clist.get_text(row,col) +'\t' | |||
fileSaveAs.write(rowtext[:-1]+'\n') | fileSaveAs.write(rowtext[:-1]+'\n') | |||
else: | else: | |||
row_data=clist.get_data("row_data") | row_data=clist.get_data("row_data") | |||
if len(rows_to_save): | if len(rows_to_save): | |||
for row in range(clist.rows): | for row in xrange(clist.rows): | |||
if get_selectable(row, row_data): | if get_selectable(row, row_data): | |||
if row not in rows_to_save: continue | if row not in rows_to_save: continue | |||
else: continue #don't save group headers | else: continue #don't save group headers | |||
fileSaveAs.write(row_data[row]) | fileSaveAs.write(row_data[row]) | |||
else: | else: | |||
fileSaveAs.writelines(row_data) | fileSaveAs.writelines(row_data) | |||
def get_selected_path(self, row=None, folder=False): | def get_selected_path(self, row=None, folder=False): | |||
clist = self.clists[self.mode] | clist = self.clists[self.mode] | |||
if self.mode==self.mode_pkgs: | if self.mode==self.mode_pkgs: | |||
skipping to change at line 1490 | skipping to change at line 1506 | |||
#could run again with os.popen3() to get actual error | #could run again with os.popen3() to get actual error | |||
#but that's a bit hacky I think? | #but that's a bit hacky I think? | |||
self.ShowErrors(_("Error starting xdg-open") + '\n') | self.ShowErrors(_("Error starting xdg-open") + '\n') | |||
# select all other duplicates from selected item folder | # select all other duplicates from selected item folder | |||
def on_select_from_that_folder_activate(self, src): | def on_select_from_that_folder_activate(self, src): | |||
clist = self.clists[self.mode] | clist = self.clists[self.mode] | |||
if self.mode==self.mode_pkgs or len(clist.selection)!=1: | if self.mode==self.mode_pkgs or len(clist.selection)!=1: | |||
return | return | |||
row = clist.selection[0] #menu item only enabled if 1 item selected | row = clist.selection[0] #menu item only enabled if 1 item selected | |||
path = clist.get_text(row, 1) | row_data = clist.get_data("row_data") | |||
path = os.path.split(row_data[row])[0] | ||||
select_func=clist.select_row | select_func=clist.select_row | |||
for row in range(clist.rows): | for row in xrange(clist.rows): | |||
if clist.get_text(row, 1) == path: | if os.path.split(row_data[row])[0] == path: | |||
select_func(row, 0) | select_func(row, 0) | |||
def on_rename_activate(self, src): | def on_rename_activate(self, src): | |||
clist = self.clists[self.mode] | clist = self.clists[self.mode] | |||
if self.mode==self.mode_pkgs or len(clist.selection)!=1: | if self.mode==self.mode_pkgs or len(clist.selection)!=1: | |||
return | return | |||
row_data=clist.get_data("row_data") | row_data=clist.get_data("row_data") | |||
row=clist.selection[0] #menu item only enabled if 1 item selected | row=clist.selection[0] #menu item only enabled if 1 item selected | |||
utf8_name=clist.get_text(row,0) | utf8_name=clist.get_text(row,0) | |||
skipping to change at line 1534 | skipping to change at line 1551 | |||
if os.path.exists(new_encoded_path): | if os.path.exists(new_encoded_path): | |||
translation_map={"old_path":utf8_name, "new_path":new_path} | translation_map={"old_path":utf8_name, "new_path":new_path} | |||
self.ShowErrors(_( | self.ShowErrors(_( | |||
"Error: Can't rename [%(old_path)s] as "\ | "Error: Can't rename [%(old_path)s] as "\ | |||
"[%(new_path)s] exists") % translation_map + '\n') | "[%(new_path)s] exists") % translation_map + '\n') | |||
else: | else: | |||
try: | try: | |||
os.rename(path_name, new_encoded_path) | os.rename(path_name, new_encoded_path) | |||
row_data.pop(row) | row_data.pop(row) | |||
clist.remove(row) | clist.remove(row) | |||
self.clist_remove_handled_groups(clist) | if self.mode!=self.mode_up: | |||
# We may want to delete the other item | ||||
# in a group of two | ||||
self.clist_remove_handled_groups(clist) | ||||
except OSError: | except OSError: | |||
self.ShowErrors(str(sys.exc_info()[1])+'\n') | self.ShowErrors(str(sys.exc_info()[1])+'\n') | |||
def on_unselect_using_wildcard_activate(self, event): | def on_unselect_using_wildcard_activate(self, event): | |||
self.select_using_wildcard(False) | self.select_using_wildcard(False) | |||
def on_select_using_wildcard_activate(self, event): | def on_select_using_wildcard_activate(self, event): | |||
self.select_using_wildcard(True) | self.select_using_wildcard(True) | |||
def select_using_wildcard(self, select): | def select_using_wildcard(self, select): | |||
clist = self.clists[self.mode] | clist = self.clists[self.mode] | |||
if clist.rows == 0: | if clist.rows == 0: | |||
skipping to change at line 1572 | skipping to change at line 1592 | |||
#Convert to unicode to match unicode string below | #Convert to unicode to match unicode string below | |||
wildcard=unicode(wildcard, "utf-8") | wildcard=unicode(wildcard, "utf-8") | |||
select_func=(select and clist.select_row or clist.unselect_row) | select_func=(select and clist.select_row or clist.unselect_row) | |||
if '/' not in wildcard or self.mode == self.mode_pkgs: | if '/' not in wildcard or self.mode == self.mode_pkgs: | |||
#Convert to unicode so ? in glob matching works correctly | #Convert to unicode so ? in glob matching works correctly | |||
get_text = lambda row: unicode(clist.get_text(row,0),"utf-8") | get_text = lambda row: unicode(clist.get_text(row,0),"utf-8") | |||
else: #Note fnmatch ignores trailing \n | else: #Note fnmatch ignores trailing \n | |||
row_data = clist.get_data("row_data") | row_data = clist.get_data("row_data") | |||
get_text = lambda row: row_data[row] | get_text = lambda row: row_data[row] | |||
import fnmatch | import fnmatch | |||
for row in range(clist.rows): | for row in xrange(clist.rows): | |||
if fnmatch.fnmatch(get_text(row), wildcard): | if fnmatch.fnmatch(get_text(row), wildcard): | |||
select_func(row, 0) | select_func(row, 0) | |||
def on_select_all_but_first_in_each_group_activate(self, event): | def on_select_all_but_first_in_each_group_activate(self, event): | |||
self.on_select_all_but_one_in_each_group_activate("first") | self.on_select_all_but_one_in_each_group_activate("first") | |||
def on_select_all_but_newest_in_each_group_activate(self, event): | def on_select_all_but_newest_in_each_group_activate(self, event): | |||
self.on_select_all_but_one_in_each_group_activate("newest") | self.on_select_all_but_one_in_each_group_activate("newest") | |||
def on_select_all_but_oldest_in_each_group_activate(self, event): | def on_select_all_but_oldest_in_each_group_activate(self, event): | |||
self.on_select_all_but_one_in_each_group_activate("oldest") | self.on_select_all_but_one_in_each_group_activate("oldest") | |||
skipping to change at line 1601 | skipping to change at line 1621 | |||
return row+1 | return row+1 | |||
elif which == "newest": | elif which == "newest": | |||
unselect_mtime=-1 | unselect_mtime=-1 | |||
comp=operator.gt | comp=operator.gt | |||
elif which == "oldest": | elif which == "oldest": | |||
unselect_mtime=2**32 | unselect_mtime=2**32 | |||
comp=operator.lt | comp=operator.lt | |||
if row!=0 or not get_selectable(row, row_data): | if row!=0 or not get_selectable(row, row_data): | |||
row += 1 #for all except first row in clist_sn | row += 1 #for all except first row in clist_sn | |||
unselect_row = -1 # avoid rh bug 726252 (mtimes for group = -1?) | unselect_row = -1 # avoid rh bug 726252 (mtimes for group = -1?) | |||
while get_selectable(row, row_data) and row < clist.rows: | while row < clist.rows and get_selectable(row, row_data): | |||
mtime = clist.get_row_data(row) | mtime = clist.get_row_data(row) | |||
if comp(mtime,unselect_mtime): | if comp(mtime,unselect_mtime): | |||
unselect_mtime = mtime | unselect_mtime = mtime | |||
unselect_row=row | unselect_row=row | |||
row += 1 | row += 1 | |||
return unselect_row | return unselect_row | |||
clist = self.clists[self.mode] | clist = self.clists[self.mode] | |||
row_data = clist.get_data("row_data") | row_data = clist.get_data("row_data") | |||
clist.freeze() | clist.freeze() | |||
clist.select_all() | clist.select_all() | |||
for row in range(clist.rows): | for row in xrange(clist.rows): | |||
if row==0 or not get_selectable(row, row_data): #New group | if row==0 or not get_selectable(row, row_data): #New group | |||
unselect_row = find_row_to_unselect(clist, row, which) | unselect_row = find_row_to_unselect(clist, row, which) | |||
if unselect_row == -1: | if unselect_row == -1: | |||
clist.unselect_all() | clist.unselect_all() | |||
clist.thaw() | clist.thaw() | |||
self.ShowErrors("Error getting age of group containing ["+ | self.ShowErrors("Error getting age of group containing ["+ | |||
self.get_selected_path(row=row+1)+']\n') | self.get_selected_path(row=row+1)+']\n') | |||
return | return | |||
clist.unselect_row(unselect_row, 0) | clist.unselect_row(unselect_row, 0) | |||
clist.thaw() | clist.thaw() | |||
skipping to change at line 1640 | skipping to change at line 1660 | |||
clist = self.clists[self.mode] | clist = self.clists[self.mode] | |||
selected = clist.selection | selected = clist.selection | |||
if not selected: | if not selected: | |||
return False | return False | |||
row_data = clist.get_data("row_data") | row_data = clist.get_data("row_data") | |||
def group_all_selected(clist, row): | def group_all_selected(clist, row): | |||
if row!=0 or not get_selectable(row, row_data): #for first sn row | if row!=0 or not get_selectable(row, row_data): #for first sn row | |||
row += 1 | row += 1 | |||
while get_selectable(row, row_data) and row < clist.rows: | while row < clist.rows and get_selectable(row, row_data): | |||
if not row in selected: | if not row in selected: | |||
return False | return False | |||
row += 1 | row += 1 | |||
return True | return True | |||
group=1 | group=1 | |||
for row in range(clist.rows): | for row in xrange(clist.rows): | |||
if row==0 or not get_selectable(row, row_data): #New group | if row==0 or not get_selectable(row, row_data): #New group | |||
if group_all_selected(clist, row): | if group_all_selected(clist, row): | |||
if group > skip_groups: | if group > skip_groups: | |||
clist.moveto(row,0,0.0,0.0) | clist.moveto(row,0,0.0,0.0) | |||
return True | return True | |||
group += 1 | group += 1 | |||
return False | return False | |||
def on_unselect_all_activate(self, event): | def on_unselect_all_activate(self, event): | |||
skipping to change at line 1680 | skipping to change at line 1700 | |||
clist.select_all() | clist.select_all() | |||
for row in selected: | for row in selected: | |||
clist.unselect_row(row, 0) | clist.unselect_row(row, 0) | |||
clist.thaw() | clist.thaw() | |||
def on_selection_clicked(self, widget): | def on_selection_clicked(self, widget): | |||
self.on_selection_menu_button_press_event(self.selection, None) | self.on_selection_menu_button_press_event(self.selection, None) | |||
def clist_pkgs_sort_by_selection(self, clist): | def clist_pkgs_sort_by_selection(self, clist): | |||
selection = clist.selection | selection = clist.selection | |||
for row in range(clist.rows): | for row in xrange(clist.rows): | |||
if row in selection: | if row in selection: | |||
clist.set_text(row,3,"1") | clist.set_text(row,3,"1") | |||
else: | else: | |||
clist.set_text(row,3,"0") | clist.set_text(row,3,"0") | |||
clist.set_sort_type(gtk.SORT_DESCENDING) | clist.set_sort_type(gtk.SORT_DESCENDING) | |||
clist.set_sort_column(3) | clist.set_sort_column(3) | |||
clist.sort() | clist.sort() | |||
clist.moveto(0,0,0.0,0.0) | clist.moveto(0,0,0.0,0.0) | |||
def on_clist_pkgs_click_column(self, clist, col): | def on_clist_pkgs_click_column(self, clist, col): | |||
skipping to change at line 1803 | skipping to change at line 1823 | |||
#determine if we need to select more packages | #determine if we need to select more packages | |||
self.status.set_text(_("Calculating dependencies...")) | self.status.set_text(_("Calculating dependencies...")) | |||
while gtk.events_pending(): gtk.main_iteration(False) | while gtk.events_pending(): gtk.main_iteration(False) | |||
all_deps=self.whatRequires(pkgs_selected) | all_deps=self.whatRequires(pkgs_selected) | |||
if len(all_deps) > len(pkgs_selected): | if len(all_deps) > len(pkgs_selected): | |||
num_new_pkgs = 0 | num_new_pkgs = 0 | |||
for package in all_deps: | for package in all_deps: | |||
if package not in pkgs_selected: | if package not in pkgs_selected: | |||
num_new_pkgs += 1 | num_new_pkgs += 1 | |||
#Note clist.find_row_from_data() only compares pointers | #Note clist.find_row_from_data() only compares pointers | |||
for row in range(clist.rows): | for row in xrange(clist.rows): | |||
if package == clist.get_row_data(row): | if package == clist.get_row_data(row): | |||
clist.select_row(row,0) | clist.select_row(row,0) | |||
#make it easy to review by grouping all selected packages | #make it easy to review by grouping all selected packages | |||
self.clist_pkgs_sort_by_selection(clist) | self.clist_pkgs_sort_by_selection(clist) | |||
self.set_cursor(None) | self.set_cursor(None) | |||
self.msgbox( | self.msgbox( | |||
_("%d extra packages need to be deleted.\n") % num_new_pkgs + | _("%d extra packages need to be deleted.\n") % num_new_pkgs + | |||
_("Please review the updated selection.") | _("Please review the updated selection.") | |||
skipping to change at line 1872 | skipping to change at line 1892 | |||
clist.select_row(clist.focus_row,0) | clist.select_row(clist.focus_row,0) | |||
clist.thaw() | clist.thaw() | |||
status = str(numDeleted) + _(" items deleted") | status = str(numDeleted) + _(" items deleted") | |||
if self.mode == self.mode_pkgs: | if self.mode == self.mode_pkgs: | |||
status += ". " + human_space_left('/') | status += ". " + human_space_left('/') | |||
self.status.set_text(status) | self.status.set_text(status) | |||
self.set_cursor(None) | self.set_cursor(None) | |||
def on_autoSymlink_clicked(self, event): | ||||
self.on_autoMerge(symlink=True) | ||||
def on_autoMerge_clicked(self, event): | def on_autoMerge_clicked(self, event): | |||
self.on_autoMerge(symlink=False) | ||||
def on_autoMerge(self, symlink): | ||||
self.ClearErrors() | self.ClearErrors() | |||
self.status.delete_text(0,-1) | self.status.delete_text(0,-1) | |||
clist = self.clists[self.mode] | clist = self.clists[self.mode] | |||
if clist.rows < 3: | if clist.rows < 3: | |||
return | return | |||
question=_("Are you sure you want to merge ALL files?\n") | if symlink: | |||
question=_("Are you sure you want to symlink ALL files?\n") | ||||
else: | ||||
question=_("Are you sure you want to merge ALL files?\n") | ||||
paths_to_leave = clist.selection | paths_to_leave = clist.selection | |||
if len(paths_to_leave): | if len(paths_to_leave): | |||
question += _("(Ignoring those selected)\n") | question += _("(Ignoring those selected)\n") | |||
(response,) = self.msgbox(question, ('Yes', 'No')) | (response,) = self.msgbox(question, ('Yes', 'No')) | |||
if response != "Yes": | if response != "Yes": | |||
return | return | |||
self.set_cursor(self.WATCH) | self.set_cursor(self.WATCH) | |||
newGroup = False | newGroup = False | |||
row_data=clist.get_data("row_data") | row_data=clist.get_data("row_data") | |||
for row in range(clist.rows): | for row in xrange(clist.rows): | |||
if row in paths_to_leave: | if row in paths_to_leave: | |||
continue | continue | |||
if not get_selectable(row, row_data): #new group | if not get_selectable(row, row_data): #new group | |||
newGroup = True | newGroup = True | |||
else: | else: | |||
path = row_data[row][:-1] #strip '\n' | path = row_data[row][:-1] #strip '\n' | |||
if newGroup: | if newGroup: | |||
keepfile = path | keepfile = path | |||
newGroup = False | newGroup = False | |||
else: | else: | |||
dupfile = path | dupfile = path | |||
dupdir = os.path.dirname(dupfile) | dupdir = os.path.dirname(dupfile) | |||
tmpfile = tempfile.mktemp(dir=dupdir) | tmpfile = tempfile.mktemp(dir=dupdir) | |||
try: | try: | |||
try: | try: | |||
os.link(keepfile,tmpfile) | if symlink: | |||
os.symlink(os.path.realpath(keepfile),tmpfile) | ||||
else: | ||||
os.link(keepfile,tmpfile) | ||||
except OSError, value: | except OSError, value: | |||
if value.errno == errno.EXDEV: | if value.errno == errno.EXDEV and not symlink: | |||
os.symlink(os.path.realpath(keepfile),tmpfile) | os.symlink(os.path.realpath(keepfile),tmpfile) | |||
else: | else: | |||
raise | raise | |||
os.rename(tmpfile, dupfile) | os.rename(tmpfile, dupfile) | |||
clist.set_background(row, self.bg_colour) | clist.set_background(row, self.bg_colour) | |||
except OSError: | except OSError: | |||
self.ShowErrors(str(sys.exc_info()[1])+'\n') | self.ShowErrors(str(sys.exc_info()[1])+'\n') | |||
try: | try: | |||
#always try this as POSIX has bad requirement that | #always try this as POSIX has bad requirement that | |||
#rename(file1,file2) where both are links to the same | #rename(file1,file2) where both are links to the same | |||
End of changes. 39 change blocks. | ||||
47 lines changed or deleted | 80 lines changed or added |