With a complete reorganisation of the directory structure and most of the content converted to pandoc-flavoured markdown. Some TODO's left before this can go live: - Main page - Atom feeds - Bug tracker
248 lines
6.6 KiB
Perl
248 lines
6.6 KiB
Perl
#!/usr/bin/perl
|
|
|
|
# grenamr v0.1
|
|
# 2008-08-01
|
|
# Yoran Heling
|
|
|
|
# License: MIT
|
|
|
|
|
|
use warnings;
|
|
use strict;
|
|
use Gtk2 -init;
|
|
|
|
our $VERSION = '0.1';
|
|
|
|
my %w;
|
|
my $m;
|
|
use constant {
|
|
C_OLD => 0,
|
|
C_NEW => 1,
|
|
C_FLG => 2,
|
|
F_REG => 0x01,
|
|
F_MAN => 0x02, # not implemented yet...
|
|
F_CON => 0x04,
|
|
};
|
|
|
|
|
|
create();
|
|
Gtk2->main;
|
|
|
|
|
|
sub create {
|
|
$w{win} = Gtk2::Window->new('toplevel');
|
|
$w{win}->signal_connect(destroy => \&close);
|
|
$w{win}->resize(500, 500);
|
|
$w{win}->move(300, 300);
|
|
$w{win}->set(title => 'GRenamR v'.$VERSION);
|
|
|
|
$w{vbGlobal} = Gtk2::VBox->new(0, 0);
|
|
$w{win}->add($w{vbGlobal});
|
|
|
|
$w{hbDir} = Gtk2::HBox->new(0, 0);
|
|
$w{vbGlobal}->pack_start($w{hbDir}, 0, 1, 0);
|
|
|
|
$w{lbDir} = Gtk2::Label->new('_Directory');
|
|
$w{hbDir}->pack_start($w{lbDir}, 0, 0, 5);
|
|
|
|
$w{fcDir} = Gtk2::FileChooserButton->new('Select a directory', 'select-folder');
|
|
$w{fcDir}->signal_connect('current-folder-changed', \&fetchdir);
|
|
$w{hbDir}->pack_start($w{fcDir}, 1, 1, 0);
|
|
$w{lbDir}->set(mnemonic_widget => $w{fcDir}, use_underline => 1);
|
|
|
|
$w{swFiles} = Gtk2::ScrolledWindow->new;
|
|
$w{swFiles}->set_policy('automatic', 'automatic');
|
|
$w{vbGlobal}->pack_start($w{swFiles}, 1, 1, 0);
|
|
|
|
$w{tvFiles} = Gtk2::TreeView->new;
|
|
$w{swFiles}->add($w{tvFiles});
|
|
$m = $w{lsFiles} = Gtk2::ListStore->new('Glib::String', 'Glib::String', 'Glib::Int');
|
|
$w{tvFiles}->set_model($w{lsFiles});
|
|
$w{tvFiles}->insert_column_with_data_func(0, '', Gtk2::CellRendererPixbuf->new, \&column_func, 0);
|
|
my $c = Gtk2::CellRendererText->new;
|
|
$w{tvFiles}->insert_column_with_data_func(1, 'Old', $c, \&column_func, 1);
|
|
$w{tvFiles}->insert_column_with_data_func(2, 'New', $c, \&column_func, 2);
|
|
$w{tvFiles}->get_column(1)->set(sizing => 'autosize');
|
|
$w{tvFiles}->get_column(2)->set(sizing => 'autosize');
|
|
|
|
$w{hbPerl} = Gtk2::HBox->new(0, 0);
|
|
$w{vbGlobal}->pack_start($w{hbPerl}, 0, 1, 0);
|
|
|
|
$w{lbPerl} = Gtk2::Label->new('_Expression');
|
|
$w{hbPerl}->pack_start($w{lbPerl}, 0, 0, 5);
|
|
|
|
$w{enPerl} = Gtk2::Entry->new;
|
|
$w{enPerl}->signal_connect('changed', \&applyfilter);
|
|
$w{hbPerl}->pack_start($w{enPerl}, 1, 1, 0);
|
|
$w{lbPerl}->set(mnemonic_widget => $w{enPerl}, use_underline => 1);
|
|
|
|
$w{imPerl} = Gtk2::Image->new_from_stock('gtk-yes', 'menu');
|
|
$w{hbPerl}->pack_start($w{imPerl}, 0, 1, 0);
|
|
|
|
$w{hbBottom} = Gtk2::HBox->new(0, 0);
|
|
$w{vbGlobal}->pack_start($w{hbBottom}, 0, 1, 0);
|
|
|
|
$w{alStatus} = Gtk2::Alignment->new(0,0.8,0,0);
|
|
$w{hbBottom}->pack_start($w{alStatus}, 1, 1, 5);
|
|
|
|
$w{lbStatus} = Gtk2::Label->new('');
|
|
$w{alStatus}->add($w{lbStatus});
|
|
|
|
$w{alBottom} = Gtk2::Alignment->new(1,1,0,1);
|
|
$w{hbBottom}->pack_start($w{alBottom}, 0, 1, 0);
|
|
|
|
$w{bbBottom} = Gtk2::HButtonBox->new;
|
|
$w{bbBottom}->set(spacing => 5);
|
|
$w{alBottom}->add($w{bbBottom});
|
|
|
|
$w{btApply} = Gtk2::Button->new('_Rename');
|
|
$w{btApply}->signal_connect('clicked', \&dorename);
|
|
$w{bbBottom}->pack_start($w{btApply}, 0, 0, 5);
|
|
|
|
$w{btClose} = Gtk2::Button->new('_Close');
|
|
$w{btClose}->signal_connect('clicked', \&close);
|
|
$w{bbBottom}->pack_start($w{btClose}, 0, 0, 5);
|
|
|
|
$w{win}->show_all;
|
|
$w{enPerl}->grab_focus;
|
|
|
|
if($ARGV[0] && -d $ARGV[0]) {
|
|
$w{fcDir}->set_current_folder($ARGV[0]);
|
|
} else {
|
|
fetchdir();
|
|
}
|
|
}
|
|
|
|
|
|
sub column_func {
|
|
my ($column, $cell, $model, $iter, $col) = @_;
|
|
my $flags = $model->get($iter, 2);
|
|
$cell->set(
|
|
$col > 0 ? (
|
|
text => $model->get($iter, $col-1),
|
|
) : (
|
|
'stock-id' => $flags & F_CON ? 'gtk-dialog-error'
|
|
: $flags & F_REG ? 'gtk-edit'
|
|
: $flags & F_MAN ? 'gtk-apply'
|
|
: 'gtk-file',
|
|
),
|
|
$flags & F_CON ? ( cell_background => '#ffcccc' ) :
|
|
$flags & F_REG ? ( cell_background => '#ccffcc' ) :
|
|
$flags & F_MAN ? ( cell_background => '#ffffcc' ) :
|
|
( cell_background_set => 0 ),
|
|
);
|
|
}
|
|
|
|
|
|
sub close {
|
|
Gtk2->main_quit;
|
|
}
|
|
|
|
|
|
sub fetchdir {
|
|
Gtk2->main_iteration_do(0);
|
|
my $d = $w{fcDir}->get_current_folder;
|
|
|
|
$w{lbStatus}->set(label => 'Loading...');
|
|
|
|
my @lst;
|
|
opendir(my $D, $d)
|
|
|| return $w{lbStatus}->set(label => 'Unable to open the selected directory');
|
|
-f $d.'/'.$_ and push @lst, $_ for (readdir $D);
|
|
closedir $D;
|
|
|
|
$m->clear;
|
|
$m->set($m->append, C_OLD, $_, C_NEW, $_, C_FLG, 0)
|
|
for (sort @lst);
|
|
$w{lbStatus}->set(label => sprintf '%d files found.', scalar @lst);
|
|
applyfilter();
|
|
}
|
|
|
|
|
|
sub applyfilter {
|
|
my $p = $w{enPerl}->get_text;
|
|
|
|
# create a list of files
|
|
my @list;
|
|
my $i = $m->get_iter_first;
|
|
do {
|
|
push @list, $m->get($i, C_OLD)
|
|
} while ($i = $m->iter_next($i));
|
|
|
|
# eval using a for loop
|
|
$i=-1;
|
|
eval 'no strict; no warnings; for (@list) { ++$i; '.$p.' }';
|
|
my $e = $@;
|
|
$w{imPerl}->set(stock => $e ? 'gtk-no' : 'gtk-yes');
|
|
|
|
# compare and update the list
|
|
my $j=0; my $matched=0;
|
|
$i = $m->get_iter_first;
|
|
do {{
|
|
next if $m->get($i, C_FLG) & F_MAN;
|
|
my $match = $e || $m->get($i, C_OLD) eq $list[$j] ? 0 : 1;
|
|
$m->set($i, C_NEW, $list[$j++], C_FLG, $match);
|
|
$matched += $match;
|
|
}} while ($i = $m->iter_next($i));
|
|
|
|
# update status
|
|
$w{lbStatus}->set(label => $e
|
|
? 'Invalid expression'
|
|
: sprintf 'Matched %d/%d files.', $matched, scalar @list);
|
|
|
|
findconflicts();
|
|
}
|
|
|
|
|
|
sub findconflicts {
|
|
# create a list of old filenames
|
|
my @old;
|
|
my $i = $m->get_iter_first;
|
|
do {
|
|
push @old, $m->get($i, C_OLD);
|
|
} while ($i = $m->iter_next($i));
|
|
|
|
# search for modified items having a new filename in that list
|
|
my $e=0;
|
|
$i = $m->get_iter_first;
|
|
do {{
|
|
next if !$m->get($i, C_FLG);
|
|
my $new = $m->get($i, C_NEW);
|
|
my $match = $new eq '' || $new =~ /\// || grep $_ eq $new, @old;
|
|
$m->set($i, C_FLG, $m->get($i, C_FLG) | F_CON)
|
|
if $match;
|
|
$e++ if $match;
|
|
}} while ($i = $m->iter_next($i));
|
|
|
|
$w{lbStatus}->set(label => 'Errors found!') if $e;
|
|
}
|
|
|
|
|
|
sub dorename {
|
|
my $d = $w{fcDir}->get_current_folder;
|
|
|
|
# count number of renamable files
|
|
my $i = $m->get_iter_first;
|
|
my $count=0;
|
|
do {
|
|
$count++ if $m->get($i, C_FLG) && !($m->get($i, C_FLG) & F_CON);
|
|
} while ($i = $m->iter_next($i));
|
|
|
|
return $w{lbStatus}->set(label => 'Nothing to do...') if !$count;
|
|
|
|
$w{lbStatus}->set(label => 'Renaming...');
|
|
my $j=0;
|
|
$i = $m->get_iter_first;
|
|
do {{
|
|
next if !$m->get($i, C_FLG) || $m->get($i, C_FLG) & F_CON;
|
|
rename $d.'/'.$m->get($i, C_OLD), $d.'/'.$m->get($i, C_NEW);
|
|
$m->set($i, C_OLD, $m->get($i, C_NEW), C_FLG, 0);
|
|
$w{lbStatus}->set(label => sprintf 'Renaming file %d/%d...', ++$j, $count);
|
|
Gtk2->main_iteration_do(0);
|
|
}} while ($i = $m->iter_next($i));
|
|
|
|
$w{lbStatus}->set(label => sprintf 'Renamed %d files.', $j);
|
|
}
|
|
|
|
|
|
|