#!/usr/bin/env perl
my $ID = q$Id: lyricue,v 1.209 2006/11/23 01:04:09 cjdebenh Exp $;

#****** lyricue/pod
# NAME
#   Pod documentation
# DESCRIPTION
#   Documentation for lyricue that is displayed by perldoc
# SOURCE
#

=head1 NAME

lyricue

=head1 SYNOPSIS

lyricue [ C<-v|-b|-l> C<-d[q]> C<-s> I<server>]

=head1 DESCRIPTION

This program creates a GTK2 gui to add, edit and remove songs from the song database and also control a lyric display server

=head1 OPTIONS

=over 4

=item B<-v>

Get lyricue version

=item B<-d>

Turn on debugging mode. Prints out debugging information

=item B<-dq>

Turn on query debugging mode. Prints out debugging information and all db queries

=item B<-l>

List available songs. Outputs a list of songs in HTML format

=item B<-b>

Turn off background changing ability. Speeds program load

=item B<-s>

Set server to connect to for Db and screen

=head1 CONFIGURATION

All configuration is done by editing the configuration section in the program

=head1 REQUIRES

Perl 5.6 or later, DBI::MySQL, Gtk2-Perl, MySql database

=head1 AUTHOR

Chris Debenham <chris@adebenham.com>

=head1 COPYRIGHT

This program is released under the GPL (http://www.gnu.org/copyleft/gpl.html)

=head1 VERSION

Lyric_interface Version 1.9.3

=cut

#***

#****** lyricue/setup
# NAME
#   Setup section
# DESCRIPTION
#   Loads required modules, sets some global variables,
#   and other global things
# SOURCE
#

#
# Modules we use.
#
use strict;
use warnings;
use DBI;
use POSIX;
use IO::Socket::INET;
use Encode;
die "The gtk2-perl bindings could not be initialized; we need them to run!\n"
  unless (Gtk2->init_check);
use Gtk2::GladeXML;
use Gtk2::Pango;
use Gtk2::Gdk::Keysyms;
eval { require Locale::gettext };

if ($@) {
    print "Gettext not available, english text only\n";

    sub gettext {
        return $_[0];
    }
} else {
    import Locale::gettext;
    textdomain('lyricue');

    # Hack to get this working under mandrake
    use lib qw(/usr/lib/libDrakX);
    eval { require c::stuff };
    if ($@) {
        bind_textdomain_codeset('lyricue', "UTF-8");
    } else {
        c::stuff::bind_textdomain_codeset('lyricue', "UTF-8");
    }
}
binmode(STDOUT, ":utf8");
binmode(STDERR, ":utf8");

#
# Site Configuration.  You should only have to
# edit this section.
#
my ($globals);

if ($^O eq 'MSWin32') {
    $globals->{'etcdir'}   = "etc/lyricue/";
    $globals->{'basedir'}  = ".lyricue/";
    $globals->{'sharedir'} = "";
} else {
    $globals->{'etcdir'}   = "/etc/lyricue/";
    $globals->{'basedir'}  = $ENV{'HOME'} . "/.lyricue/";
    $globals->{'sharedir'} = "/usr/share/lyricue/";
}
#
# You shouldn't have to change anything after this line
#

# convenience variables for true and false
use constant FALSE => 0;
use constant TRUE  => 1;

$globals->{'version'}     = "1.9.3";
$globals->{'accessfile'}  = $globals->{'etcdir'} . "access.conf";
$globals->{'defaultconf'} = $globals->{'etcdir'} . "default.conf";
$globals->{'configfile'}  = $globals->{'basedir'} . "config2";
$globals->{'gladefile'}   = $globals->{'sharedir'} . "lyricue.glade";

$globals->{'host'}        = "localhost";
$globals->{'mysqlhost'}   = "";
$globals->{'lyricdb'}     = "lyricDb";
$globals->{'bibledb'}     = "";
$globals->{'biblename'}   = "";
$globals->{'access'}      = "";
$globals->{'usesword'}    = TRUE;
$globals->{'mediadb'}     = "mediaDb";
$globals->{'sortby'}      = "title";
$globals->{'bg_previews'} = TRUE;
if ($^O eq 'MSWin32') {
    $globals->{'diatheke'}    = "";
} else {
    $globals->{'diatheke'}    = `which diatheke`;
    chomp $globals->{'diatheke'};
}
$globals->{'server_port'}        = "2346";    #port used for lyric server
$globals->{'preview_port'}       = "2347";    #port used for preview
$globals->{'miniview_port'}      = "2348";    #port used for miniview
$globals->{'update_timer'}       = FALSE;
$globals->{'nav_update_timer'}   = FALSE;
$globals->{'timer'}              = FALSE;
$globals->{'debugging'}          = FALSE;
$globals->{'spell'}              = TRUE;
$globals->{'trayicon'}           = TRUE;
$globals->{'preview_pid'}        = FALSE;
$globals->{'miniview_pid'}       = FALSE;
$globals->{'hovering_over_link'} = FALSE;
$globals->{'mini'}               = FALSE;
$globals->{'db_adminuser'}       = "";
$globals->{'current_item'}       = -1;

# Optional modules
eval { require Gtk2::Spell };
if ($@) {
    print "Gtk-Spell not available, spell checking turned off\n";
    $globals->{'spell'} = FALSE;
} else {
    import Gtk2::Spell;
}
eval { require Gtk2::TrayIcon };
if ($@) {
    print "Gtk-Trayicon not available, Tray icon turned off\n";
    $globals->{'trayicon'} = FALSE;
} else {
    import Gtk2::TrayIcon;
}
if ($globals->{'diatheke'} eq "") {
    $globals->{'diatheke'} = "true";
}

# Transitions
use constant NORMAL     => 0;
use constant SLIDE_TEXT => 1;
use constant WIPE       => 2;
use constant CLIP       => 3;

# Transition directions
use constant NONE  => 0;
use constant UP    => 1;
use constant DOWN  => 2;
use constant RIGHT => 4;
use constant LEFT  => 8;
use constant WAIT  => 16;

# Quick globals
my $config;
my $widgets;
my $bibleMenu;
my @ASSOCIATE;
my %pageOrder;
my %selectedimages;
my ($lyricDbh, $mediaDbh, $bibleDbh);

my ($errorcodes);
$errorcodes->{'lyricdbopen'} = Encode::decode(
    "utf-8",
    gettext(
"I'm sorry but I could not open the lyric database.\nPlease confirm that Lyricue is installed correctly and MySql is running"
    )
);
$errorcodes->{'bibledbopen'} = Encode::decode(
    "utf-8",
    gettext(
"I'm sorry but I could not open the bible database.\nPlease confirm that Lyricue is installed correctly and the current bible database exists.\nThe requested database was named "
    )
);
$errorcodes->{'mediadbopen'} = Encode::decode(
    "utf-8",
    gettext(
"I'm sorry but I could not open the media database.\nPlease confirm that Lyricue is installed correctly and MySql is running"
    )
);
$errorcodes->{'sqlprepare'} =
  Encode::decode("utf-8", gettext("Unable to prepare query.\nHas mysql died?"));
$errorcodes->{'sqlexecute'} =
  Encode::decode("utf-8", gettext("Unable to execute query.\nHas mysql died?"));
$errorcodes->{'socketopen'} = Encode::decode(
    "utf-8",
    gettext(
"Sorry, I was unable to listen on the network.\nPlease make sure I am not already running"
    )
);
$errorcodes->{'erroropen'} =
  Encode::decode("utf-8", gettext("Could not open "));
$errorcodes->{'fileopenread'} = Encode::decode(
    "utf-8",
    gettext(
"Unable to read the file, are you sure it exists?\nThe file asked for was "
    )
);
$errorcodes->{'fileopenwrite'} = Encode::decode(
    "utf-8",
    gettext(
"Unable to write to the file, you may not have sufficent permissions.\nPlease check the permissions for "
    )
);
$errorcodes->{'usage'} = Encode::decode(
    "utf-8",
    gettext(
            "\nUsage: lyricue <-v|-l> <-b> <-k> <-d[q]> <-s>\n\n"
          . "\t-v:  Prints Lyricue version information & exits\n"
          . "\t-l:  Outputs song list in HTML & exits\n"
          . "\t-b:  Loads Lyricue without background previews\n"
          . "\t-s:  Specify the host on which the lyric server is located\n"
          . "\t-r:  Specify the host on which the mysql server is located\n"
          . "\t-d:  Prints debugging messages\n"
          . "\t-w:  Don't maximize on startup\n"
          . "\t-dq: Prints debugging messages + sql\n\n"
    )
);

# Widgets affected by access controls
# access 'e'
my @edit_items = (
    "buttonMainAdd", "buttonMainEdit", "add_song1", "edit_song1",
    "buttonQuickSave"
);

# access 'd'
my @delete_items = ("delete_song1");

# access 's'
my @display_items = (
    "previous_page1",  "next_page1",
    "display_now1",    "blank_display1",
    "buttonMainPrev",  "buttonMainNext",
    "buttonMainPoint", "buttonMainBlank",
    "notebookRight",   "buttonQuickShow",
    "buttonMainClear", "clear_text1"
);

# access 'p'
my @playlist_items = (
    "buttonAddToPlaylist", "playlist1",
    "vboxMainRight",       "buttonMainImage",
    "buttonMainVerse",     "buttonMainSublist"
);

# access 'a'
my @admin_items = ("user_administration1");

#***

#****** lyricue/main_code
# NAME
#   main_code - main code section, not in subroutine
# SYNOPSIS
#   No output
# FUNCTION
#   Figure out where to go
# INPUTS
#   Commandline
# OUTPUT
#   Everything
# SOURCE
#
if ($ARGV[0]) {
    foreach (0 .. (@ARGV - 1)) {
        if ($ARGV[$_] eq "-v") {
            print "Lyric Interface version " . $globals->{'version'} . "\n";
            exit;
        } elsif ($ARGV[$_] eq "-l") {
            print_songs();
        } elsif ($ARGV[$_] eq "-b") {
            $globals->{'bg_previews'} = FALSE;
        } elsif ($ARGV[$_] eq "-d") {
            $globals->{'debugging'} = 1;
            debug(  "Lyric Interface version "
                  . $globals->{'version'} . "\n"
                  . $ID);
        } elsif ($ARGV[$_] eq "-dq") {
            $globals->{'debugging'} = 2;
        } elsif ($ARGV[$_] eq "-sqlite") {
            $globals->{'force_sqlite'} = TRUE;
        } elsif ($ARGV[$_] eq "-w") {
            $globals->{'run_windowed'} = TRUE;
        } elsif ($ARGV[$_] eq "-s") {
            $globals->{'host'} = $ARGV[$_ + 1];
            $ARGV[$_ + 1] = "";
        } elsif ($ARGV[$_] eq "-r") {
            $globals->{'mysqlhost'} = $ARGV[$_ + 1];
            $ARGV[$_ + 1] = "";
        } elsif ($ARGV[$_] eq "-i") {
            import_song_from_file($ARGV[$_ + 1]);
            exit;
        } elsif ($ARGV[$_] eq "-m") {
            $globals->{'gladefile'} = "lyricue_mini.glade";
            $globals->{'mini'}      = TRUE;
        } elsif ($ARGV[$_] eq "") {

            # ignore
        } else {
            print $errorcodes->{'usage'};
            exit;
        }
    }
}

# Set mysql host if not already set
if ($globals->{'mysqlhost'} eq "") {
    $globals->{'mysqlhost'} = $globals->{'host'};
}

# Some global stuff
my ($query, $row, $sth, $rv);

# Set umask
umask 0002;

# Check if user .lyricue directory exists, otherwire create
if (-e $globals->{'basedir'}) {
    if (!-d $globals->{'basedir'}) {
        print
"Old ~/.lyricue existed but was not a directory, moving to ~/.lyricue.orig\n";
        rename $globals->{'basedir'}, $globals->{'basedir'} . ".orig";
        mkdir $globals->{'basedir'}, 0777;
    }
} else {
    mkdir $globals->{'basedir'}, 0777;
}

$widgets->{'main'} =
  Gtk2::GladeXML->new($globals->{'gladefile'}, 'windowMain', 'lyricue');
$widgets->{'main'}->signal_autoconnect_from_package('');
if ($globals->{'mini'}) {
    my $bitmap = Gtk2::Gdk::Bitmap->create_from_data(undef, 0, 1, 1);
    my $cursor = Gtk2::Gdk::Cursor->new_from_pixmap(
        $bitmap, $bitmap,
        Gtk2::Gdk::Color->new(0, 0, 0),
        Gtk2::Gdk::Color->new(0, 0, 0),
        0, 0
    );
    $widgets->{'main'}->get_widget('windowMain')->window->set_cursor($cursor);
}

# Load the config file
if (-d $globals->{'basedir'}) {
    if (!-e $globals->{'configfile'}) {
        debug("Creating new configuration");
        system(
            "cp " . $globals->{'defaultconf'} . " " . $globals->{'configfile'});
        $config = load_config();
        $globals->{'access'} = load_access();
        db_select();
        create_dialog_prefs();
        $globals->{'configured'} = FALSE;
        while (!$globals->{'configured'}) {
            do_pending();
        }
    } else {
        debug("Loading config and access");
        $config = load_config();
        $globals->{'access'} = load_access();

        # Open lyricDB, bibleDB and mediaDb
        db_select();
    }
}

if (defined $config->{'DefBible'} && ($config->{'DefBible'} ne "")) {
    my @tmpbible = split(/;/, $config->{'DefBible'}, 2);
    $globals->{'biblename'} = $tmpbible[1];
    @tmpbible = split(/:/, $tmpbible[0], 2);
    do_change_bible($tmpbible[1], $tmpbible[0]);
}

# Create tray icon
if ($globals->{'trayicon'}) {
    debug("Creating Tray icons");
    foreach
      my $trayicon ('trayClear', 'trayDown', 'trayUp', 'trayRight', 'trayLeft')
    {
        my ($tray);
        eval { $tray = Gtk2::TrayIcon->new("Lyricue-" . $trayicon); };
        if ($@) {
            debug("Unable to load system tray");
        } else {
            my $trayxml =
              Gtk2::GladeXML->new($globals->{'gladefile'}, $trayicon,
                'lyricue');
            $trayxml->signal_autoconnect_from_package('');
            $tray->add($trayxml->get_widget($trayicon));
            $tray->show_all;
        }
    }
}
$0 = "Lyricue Interface";

init_mainWindow();
choose_playlist();

# Don't let them cancel out of the playlist dialog yet
$widgets->{'choose'}->get_widget('buttonCancel')->hide;
$globals->{'tracker_timer'} = Glib::Timeout->add(1000, \&check_tracker);

Gtk2->main();

# Should never get here
exit(0);

#***

#****m* lyricue/close_main
# NAME
#   close_main -- close the main window
# SYNOPSIS
#   close_main ()
# FUNCTION
#   Callback function to close the window
# INPUTS
#   none
# OUTPUT
#   Closes the interface
# SOURCE
sub close_main {
    debug("Quitting");
    do_pending();

    # Save current state
    unlink $globals->{'configfile'} . ".bak";
    open(CONFIG, "$globals->{'configfile'}")
      || display_fatal($errorcodes->{'fileopenread'}, $!);
    my $config = "";
    while (<CONFIG>) {
        if (!/^Frame/) {
            $config .= $_;
        }
    }
    close CONFIG;
    rename($globals->{'configfile'}, $globals->{'configfile'} . ".bak");
    open(CONFIG, ">$globals->{'configfile'}")
      || display_fatal($errorcodes->{'fileopenwrite'}, $!);
    print CONFIG $config;
    print CONFIG save_state();
    close CONFIG;

    $lyricDbh->disconnect;
    if (!$globals->{'usesword'}) {
        $bibleDbh->disconnect;
    }
    $mediaDbh->disconnect;
    Gtk2->main_quit;
    if ($globals->{'preview_pid'}) {
        debug("Killing $globals->{'preview_pid'}");
        kill 9, $globals->{'preview_pid'};
    }
    if ($globals->{'miniview_pid'}) {
        debug("Killing $globals->{'miniview_pid'}");
        kill 9, $globals->{'miniview_pid'};
    }
    exit();
    return FALSE;
}

#***

#****m* lyricue/update_playlist
# NAME
#   update_playlist -- update the playlist
# SYNOPSIS
#   update_playlist ()
# FUNCTION
#   Clear the playlist area and redisplay with updated info
# INPUTS
# OUTPUT
#   refreshed playlist
# SOURCE
sub update_playlist {
    my ($selectedid) = @_;
    debug("Updating playlist");
    if (!defined $selectedid) {
        my $selection =
          $widgets->{'main'}->get_widget('treePlaylist')->get_selection;
        if ($selection) {
            my ($m, $i) = $selection->get_selected;
            if ($m) {
                $selectedid = $m->get($i, 2);
            }
        }
    }

    my $playlist =
      $widgets->{'main'}->get_widget('labelCurrentPlaylist')->{user_data};

    my ($expanded);
    my $model = $widgets->{'main'}->get_widget('treePlaylist')->get_model();
    if ($model) {
        $widgets->{'main'}->get_widget('treePlaylist')
          ->map_expanded_rows(\&save_expanded, \$expanded);
        $model->clear;
    } else {

        # Column 1 -> Visible text
        # Column 2 -> Colour
        # Column 3 -> playorder
        # Column 4 -> transition
        $model =
          Gtk2::TreeStore->new('Glib::String', 'Glib::String', 'Glib::String',
            'Glib::String');
        $widgets->{'main'}->get_widget('treePlaylist')->set_model($model);
        my $column = Gtk2::TreeViewColumn->new_with_attributes(
            "",
            Gtk2::CellRendererText->new,
            text       => 0,
            background => 1
        );
        $widgets->{'main'}->get_widget('treePlaylist')->append_column($column);

        $widgets->{'main'}->get_widget('treePlaylist')->set_model($model);
    }
    add_playlist($playlist, undef, $model, $selectedid, \$expanded);
    $globals->{'current_item'} = 0;
}

#***

#****m* lyricue/add_playlist
# NAME
#   add_playlist - Add a playlist to the playlist area
# SYNOPSIS
#   add_playlist ($playlist, $iter, $model, $selectedid)
# FUNCTION
#   Add a playlist to the playlist area
# INPUTS
#   $playlist - Playlist to add
#   $iter - Where to add it in the playlist area
#   $model - The playlist tree model
# OUTPUT
#   A bigger playlist
# SOURCE
sub add_playlist {
    my ($playlist, $iter, $model, $selectedid, $expanded) = @_;
    debug("Add playlist");
    my $query =
        "SELECT * FROM playlist WHERE playlist="
      . $playlist
      . " ORDER BY playorder";
    qdebug($query);
    my $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    my $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);
    while (my $row = $sth->fetchrow_hashref()) {

        my $title = "";
        if ($row->{'type'} eq "back") {
            my $query2 =
              "SELECT description FROM media WHERE id=\""
              . $row->{'data'} . "\"";
            qdebug($query2);
            my $sth2 = $mediaDbh->prepare($query2)
              || display_fatal($errorcodes->{'sqlprepare'}, $!);
            my $rv2 = $sth2->execute
              || display_fatal($errorcodes->{'sqlexecute'}, $!);
            my $row2 = $sth2->fetchrow_hashref();
            $title = "Background : " . $row2->{'description'};
        } elsif ($row->{'type'} eq "file") {
            $title = "File: " . $row->{'data'};
        } elsif ($row->{'type'} eq "imag") {
            my $query2 =
              "SELECT description FROM media WHERE id=\""
              . $row->{'data'} . "\"";
            qdebug($query2);
            my $sth2 = $mediaDbh->prepare($query2)
              || display_fatal($errorcodes->{'sqlprepare'}, $!);
            my $rv2 = $sth2->execute
              || display_fatal($errorcodes->{'sqlexecute'}, $!);
            my $row2 = $sth2->fetchrow_hashref();
            $title = "Image: " . $row2->{'description'};
        } elsif ($row->{'type'} eq "vers") {
            $title = "Verses " . $row->{'data'};
        } elsif ($row->{'type'} eq "song") {
            my $query2 =
              "SELECT lyrics FROM page WHERE pageid=" . $row->{'data'};
            qdebug($query2);
            my $sth2 = $lyricDbh->prepare($query2)
              || display_fatal($errorcodes->{'sqlprepare'}, $!);
            my $rv2 = $sth2->execute
              || display_fatal($errorcodes->{'sqlexecute'}, $!);
            my $row2   = $sth2->fetchrow_hashref();
            my $lyrics = $row2->{'lyrics'};
            if ($globals->{'invert'}) {
                my @lyricl = split(/\n/, $lyrics);
                $title = $lyricl[@lyricl - 1];
            } else {
                ($title, undef) = split(/\n/, $lyrics);
            }
            if (!$title) {
                $title = "";
            }
        } elsif ($row->{'type'} eq "play" | $row->{'type'} eq "sub") {

            my $query2 = "SELECT * FROM playlists WHERE id=" . $row->{'data'};
            qdebug($query2);
            my $sth2 = $lyricDbh->prepare($query2)
              || display_fatal($errorcodes->{'sqlprepare'}, $!);
            my $rv2 = $sth2->execute
              || display_fatal($errorcodes->{'sqlexecute'}, $!);
            my $row2 = $sth2->fetchrow_hashref();

            $title = $row2->{'title'};

            if ($row2->{'ref'} && $row2->{'ref'} != 0) {
                $query2 =
                  "SELECT songnum FROM lyricMain WHERE id=" . $row2->{'ref'};
                qdebug($query2);
                $sth2 = $lyricDbh->prepare($query2)
                  || display_fatal($errorcodes->{'sqlprepare'}, $!);
                $rv2 = $sth2->execute
                  || display_fatal($errorcodes->{'sqlexecute'}, $!);
                $row2 = $sth2->fetchrow_hashref();
                if ($row2->{'songnum'} != 0) {
                    $title = $row2->{'songnum'} . " - " . $title;
                }
            }
        } else {
            $title = "Unknown type";
        }

        # Add image name to playlist item title
        my $query3 =
          "SELECT * FROM associations WHERE playlist=" . $row->{'playorder'};
        qdebug($query3);
        my $sth3 = $lyricDbh->prepare($query3)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        my $rv3 = $sth3->execute
          || display_fatal($errorcodes->{'sqlexecute'}, $!);
        my $imagename = "";
        while (my $row3 = $sth3->fetchrow_hashref()) {
            $imagename = $row3->{'imagename'};
        }

        my $playorder = $row->{'playorder'};
        my $newiter   = $model->append($iter);
        $model->set(
            $newiter,
            0 => $title,
            1 => undef,
            2 => $row->{'playorder'},
            3 => $row->{'transition'}
        );

        # Set sub-item if image associated
        if ($imagename) {
            my $query2 =
              "SELECT description FROM media WHERE id=\"" . $imagename . "\"";
            qdebug($query2);
            my $sth2 = $mediaDbh->prepare($query2)
              || display_fatal($errorcodes->{'sqlprepare'}, $!);
            my $rv2 = $sth2->execute
              || display_fatal($errorcodes->{'sqlexecute'}, $!);
            my $row2    = $sth2->fetchrow_hashref();
            my $imgiter = $model->append($newiter);
            $model->set(
                $imgiter,
                0 => "* Background: " . $row2->{'description'},
                1 => "",
                2 => $row->{'playorder'},
                3 => $row->{'transition'}
            );
        }

        # Set sub-item if transition associated
        if ($row->{'transition'}) {
            my $transiter = $model->append($newiter);
            $model->set(
                $transiter,
                0 => "* Page Transition Set",
                1 => "",
                2 => $row->{'playorder'},
                3 => $row->{'transition'}
            );
        }

        # Add sublists/playlists
        if ($row->{'type'} eq "play" | $row->{'type'} eq "sub") {
            add_playlist($row->{'data'}, $newiter, $model, $selectedid,
                $expanded);
        }

        if ($$expanded->{$playorder}) {
            $widgets->{'main'}->get_widget('treePlaylist')
              ->expand_to_path($model->get_path($newiter));
        }

        # Select what was selected and expand its parent
        if (defined $selectedid && $row->{'playorder'} == $selectedid) {
            debug("Selecting $selectedid");
            my $path       = $model->get_path($newiter);
            my $pathstring = $path->to_string();
            $pathstring =~ s/^(.*):(.*?)$/$1/g;
            if (defined($2)) {
                $widgets->{'main'}->get_widget('treePlaylist')
                  ->expand_to_path(
                    Gtk2::TreePath->new_from_string($pathstring));
            }
            $widgets->{'main'}->get_widget('treePlaylist')
              ->get_selection->select_iter($newiter);
        }
    }
}

#***

# Save the expanded state
sub save_expanded {
    debug("Save expanded");
    my ($tree, $path, $expanded) = @_;
    my $iter = $tree->get_model->get_iter($path);
    $$expanded->{$tree->get_model->get($iter, 2)} = TRUE;
    return FALSE;
}

#****m* lyricue/change_sort_order
# NAME
#   change_sort_order -- Change the sort order
# SYNOPSIS
#   change_sort_order (undef,$column)
# FUNCTION
#   Change the order by which the available songs are displayed
# INPUTS
#   $column - which column to order by
# OUTPUT
#   Calls update_available to redisplay available songs
# SOURCE
sub change_sort_order {
    debug("change sort order");
    my (undef, $column) = @_;
    if ($column == 1) {
        $globals->{'sortby'} = "book";
    } elsif ($column == 2) {
        $globals->{'sortby'} = "songnum";
    } elsif ($column == 3) {
        $globals->{'sortby'} = "id";
    } elsif ($column == 4) {
        $globals->{'sortby'} = "book,songnum,title";
    } elsif ($column == 5) {
        $globals->{'sortby'} = "book,title";
    } else {
        $globals->{'sortby'} = "title";
    }
    debug("Changing sort order to " . $globals->{'sortby'});
    update_available();
}

#***

#****m* lyricue/update_available
# NAME
#   update_available -- Display list of available songs
# SYNOPSIS
#   update_available ()
# FUNCTION
#   Update the list of available songs, limited by keyword and sorted by orderby
# INPUTS
# OUTPUT
#   updated list of available songs
# SOURCE
sub update_available {
    debug("update available");
    reset_timer($globals->{'update_timer'});

    my $store =
      Gtk2::ListStore->new('Glib::String', 'Glib::String', 'Glib::Uint',
        'Glib::Uint');

    if (
        $widgets->{'main'}->get_widget('treeAvailable')->{user_data}
        && ($widgets->{'main'}->get_widget('treeAvailable')->{user_data} eq
            "load")
      )
    {
        $widgets->{'main'}->get_widget('treeAvailable')->{data} = ();
    } else {
        $widgets->{'main'}->get_widget('treeAvailable')->{user_data} = "load";
        my $column1 =
          Gtk2::TreeViewColumn->new_with_attributes(
          Encode::decode("utf-8", gettext("Title")),
            Gtk2::CellRendererText->new, text => 0);
        my $column2 =
          Gtk2::TreeViewColumn->new_with_attributes(
          Encode::decode("utf-8", gettext("Book")),
            Gtk2::CellRendererText->new, text => 1);
        my $column3 =
          Gtk2::TreeViewColumn->new_with_attributes(
          Encode::decode("utf-8", gettext("Song Number")),
            Gtk2::CellRendererText->new, text => 2);
        $column1->set_resizable(TRUE);
        $column2->set_resizable(TRUE);
        $column3->set_resizable(TRUE);
        $widgets->{'main'}->get_widget('treeAvailable')
          ->append_column($column1);
        $widgets->{'main'}->get_widget('treeAvailable')
          ->append_column($column2);
        $widgets->{'main'}->get_widget('treeAvailable')
          ->append_column($column3);
        $column1->signal_connect("clicked", "change_sort_order", 0);
        $column2->signal_connect("clicked", "change_sort_order", 1);
        $column3->signal_connect("clicked", "change_sort_order", 2);
    }
    my $songname = $widgets->{'main'}->get_widget('entrySearch')->get_text();

    if ($config->{'SpecialSong'} ne "") {
        my $query =
          "SELECT id,title,songnum,book FROM lyricMain WHERE title LIKE \"%"
          . $config->{'SpecialSong'} . "%\"";
        qdebug($query);
        $sth = $lyricDbh->prepare($query)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);
        $row = $sth->fetchrow_hashref();
        my $iter = $store->append;
        $store->set(
            $iter,             0, $row->{'title'}, 1, $row->{'book'}, 2,
            $row->{'songnum'}, 3, $row->{'id'}
        );
    }

    $query = "SELECT id,title,songnum,book FROM lyricMain WHERE id > 1";

    # Add search term if applicable
    #if ($keywords) {
    #	$query .= " AND keywords LIKE \"%" . $keywords . "%\"";
    #} elsif ($songname) {
    if ($songname =~ /^\d+$/) {
        $query .= " AND songnum=" . $songname;
    } else {
        $songname =~ s/[\s,]/%/g;
        $songname = Encode::encode("iso-8859-1", $songname);
        $query .= " AND title LIKE \"%" . $songname . "%\"";
    }

    #}
    $query .= " ORDER BY " . $globals->{'sortby'};
    qdebug($query);
    $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);

    while ($row = $sth->fetchrow_hashref()) {
        my $iter = $store->append;
        $store->set(
            $iter,             0, $row->{'title'}, 1, $row->{'book'}, 2,
            $row->{'songnum'}, 3, $row->{'id'}
        );
    }

    if (($songname ne "") && ($config->{'DatabaseType'} eq "mysql")) {
        $query =
"SELECT id,title,songnum,book,SUBSTRING_INDEX(lyrics,'\n',1) as line FROM page,lyricMain WHERE pagenum=1 AND SUBSTRING_INDEX(lyrics,'\n',1) LIKE \"%"
          . $songname
          . "%\" AND page.songid=lyricMain.id AND title != SUBSTRING_INDEX(lyrics,'\n',1) ORDER BY "
          . $globals->{'sortby'};
        qdebug($query);
        $sth = $lyricDbh->prepare($query)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);

        while ($row = $sth->fetchrow_hashref()) {
            my $iter = $store->append;
            $store->set($iter, 0, $row->{'line'} . " (" . $row->{'title'} . ")",
                1, $row->{'book'}, 2, $row->{'songnum'}, 3, $row->{'id'});
        }
    }

    $widgets->{'main'}->get_widget('treeAvailable')->set_model($store);
    $widgets->{'main'}->get_widget('treeAvailable')
      ->set_headers_clickable(TRUE);
    debug("Avail updated");
    return FALSE;
}

#***

#****m* lyricue/popup_avail_menu
# NAME
#   popup_avail_menu --
# SYNOPSIS
#   popup_avail_menu ($widget, $event)
# FUNCTION
#   popup a menu when available songs right-clicked
# INPUTS
#   $widget - Calling widget
#   $event - calling event
# OUTPUT
#   Displays the right-click menu
# SOURCE
#
sub popup_avail_menu {
    my ($widget, $event) = @_;
    debug("Button clicked on available songs list");
    if ($event->button == 3) {
        my $path = $widget->get_path_at_pos($event->x, $event->y);
        $widget->get_selection->select_path($path);
        debug($path . " path");
        my @items = ();
    
        if ($globals->{'access'} =~ /e/) {
            push @items,
              [
                Encode::decode("utf-8", gettext("/Edit Song")),
                undef, 'edit_song', 1, '', ''
              ];
        }
        if ($globals->{'access'} =~ /d/) {
            push @items,
              [
                Encode::decode("utf-8", gettext("/Delete Song")),
                undef, 'delete_song', 1, '', ''
              ];
        }
        push @items,
          (
            [
                Encode::decode("utf-8", gettext("/Refresh List")),
                undef, 'update_available', 1, '', ''
            ],
            [
                Encode::decode("utf-8", gettext("/Order - Songbook -> No.")),
                undef, 'change_sort_order', 4, '', ''
            ],
            [
                Encode::decode("utf-8", gettext("/Order - Songbook -> Name.")),
                undef,
                'change_sort_order',
                5,
                '',
                ''
            ]
          );
        if ($globals->{'access'} =~ /p/) {
            push @items,
              (
                [
                    Encode::decode("utf-8", gettext("/Add to Playlist")),
                    undef, 'add_to_playlist', 1, '', ''
                ]
              );
        }
        my $factory =
          Gtk2::ItemFactory->new('Gtk2::Menu', "<availpopup>", undef);
        $factory->create_items(1, @items);
        $factory->popup($event->x_root, $event->y_root, $event->button,
            $event->time);
        return (TRUE);
    } elsif ($event->button == 1 && $event->type eq '2button-press') {
        debug("Add song");
        add_to_playlist();
        return (TRUE);
    }

    # Tell calling code that we have not handled this event; pass it on.
    return (FALSE);
}

#***

#****m* lyricue/popup_play_menu
# NAME
#   popup_play_menu --
# SYNOPSIS
#   popup_play_menu ()
# FUNCTION
#   popup a menu when playlist item right-clicked
# INPUTS
# OUTPUT
#   Displays the right-click menu
# SOURCE
#
sub popup_play_menu {
    my ($event) = @_;
    debug("Button clicked on playlist window");
    my $playlist =
      $widgets->{'main'}->get_widget('labelCurrentPlaylist')->{user_data};
    my $query =
"SELECT title, data FROM playlist, playlists WHERE playlist.data = playlists.id AND playlist = "
      . $playlist
      . " AND type = 'sub'";
    qdebug($query);
    my $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    my $rv = $sth->execute
      || display_fatal($errorcodes->{'sqlexecute'}, $!);
    my (@list);

    while ($row = $sth->fetchrow_hashref()) {
        my ($text);
        $text->{'data'}  = $row->{'data'};
        $text->{'title'} = $row->{'title'};
        push @list, $text;
        my @childid = find_more_children($row->{'data'});
        foreach (@childid) {
            debug("Child sublist found. ID: " . $_);
            my $query2 = "SELECT title FROM playlists WHERE id=" . $_;
            qdebug($query2);
            my $sth2 = $lyricDbh->prepare($query2)
              || display_fatal($errorcodes->{'sqlprepare'}, $!);
            my $rv2 = $sth2->execute
              || display_fatal($errorcodes->{'sqlexecute'}, $!);
            my $row2 = $sth2->fetchrow_hashref();
            my ($text);
            $text->{'data'}  = $row2->{'data'};
            $text->{'title'} = $row2->{'title'};
            push @list, $text;
        }
    }

    my @items = (
        [
            Encode::decode("utf-8", gettext("/Duplicate Item")), undef,
            'copy_item', 1
        ],
        [
            Encode::decode("utf-8", gettext("/Remove from Playlist")), undef,
            'remove_from_playlist', 1
        ],
        [
            Encode::decode("utf-8", gettext("/Refresh Playlist")), undef,
            'update_playlist', 1
        ],
        [Encode::decode("utf-8", gettext("/Add Verse")), undef, 'add_verse', 1],
        [
            Encode::decode("utf-8", gettext("/Invert Line Display")), undef,
            'invert_lines', 1
        ],
        [
            Encode::decode("utf-8", gettext("/Loop this playlist item")),
            undef, 'begin_loop', 1
        ],
        [
            Encode::decode("utf-8", gettext("/Associate background")), undef,
            'prepare_for_association', 1
        ],
        [
            Encode::decode("utf-8", gettext("/Dis-associate background")),
            undef, 'disassociate_bg', 1
        ],
        [
            Encode::decode("utf-8", gettext("/Preview this item")), undef,
            'preview_playlist_item', 1
        ],
        [
            Encode::decode("utf-8", gettext("/Move to sublist/Main")),
            undef, 'move_item_to_sublist', $playlist
        ],
    );
    foreach (sort { uc($a->{'title'}) cmp uc($b->{'title'}) } @list) {
        my $item =
          Encode::decode("utf-8", gettext("/Move to sublist/")) . $_->{'title'};
        push @items, [$item, undef, 'move_item_to_sublist', $_->{'data'}],;
    }
    my $factory = Gtk2::ItemFactory->new('Gtk2::Menu', '<playpopup>', undef);
    $factory->create_items(undef, @items);
    $factory->popup($event->x_root, $event->y_root, 0, $event->time);
}

#***

#****m* lyricue/add_song
# NAME
#   add_song --
# SYNOPSIS
#   add_song ()
# FUNCTION
#   Called which add chosen from menu/buttons
# INPUTS
# OUTPUT
#   display add window
# SOURCE
#
sub add_song {
    my $i;
    debug("Add clicked");
    if ($widgets->{'add'} && $widgets->{'add'}->get_widget('windowEditSong')) {
        if (!$widgets->{'add'}->get_widget('windowEditSong')->visible) {
            $widgets->{'add'}->get_widget('windowEditSong')->destroy;
        }
    }
    $widgets->{'add'} =
      Gtk2::GladeXML->new($globals->{'gladefile'}, 'windowEditSong', 'lyricue');
    $widgets->{'add'}->signal_autoconnect_from_package('');
    $widgets->{'add'}->get_widget('buttonEditPreviewServer')
      ->signal_connect("clicked", "preview_page", "MINI");
    $widgets->{'add'}->get_widget('notebookEditPages')->remove_page(0);
    %pageOrder = ();
    add_page();
    $widgets->{'add'}->get_widget('windowEditSong')->{user_data} = 0;
    $widgets->{'add'}->get_widget('windowEditSong')->show_all();
    $widgets->{'add'}->get_widget('buttonEditRemovePage')->set_sensitive(FALSE);
    $widgets->{'add'}->get_widget('remove_page1')->set_sensitive(FALSE);
}

#***

#****m* lyricue/delete_song
# NAME
#   delete_song --
# SYNOPSIS
#   delete_song ()
# FUNCTION
#   Confirm if a song is to be deleted
# INPUTS
#   calls create_dialog_delete to confirm deletion
# SOURCE
#
sub delete_song {
    debug("Delete song selected");
    my $selection =
      $widgets->{'main'}->get_widget('treeAvailable')->get_selection;
    my ($model, $iter) = $selection->get_selected;
    if ($iter) {
        my $songid = $model->get($iter, 3);
        if (!($model->get($iter, 1) eq $config->{'SpecialSong'})) {
            $query =
              "SELECT title,songnum,book,artist FROM lyricMain WHERE id="
              . $songid;
            qdebug($query);
            $sth = $lyricDbh->prepare($query)
              || display_fatal($errorcodes->{'sqlprepare'}, $!);
            $rv = $sth->execute
              || display_fatal($errorcodes->{'sqlexecute'}, $!);
            $row = $sth->fetchrow_hashref();
            my $labelText =
              Encode::decode("utf-8",
                gettext("Are you sure you wish to delete "))
              . $row->{'title'} . "\n";

            if ($row->{'book'} ne "") {
                $labelText .=
                  Encode::decode("utf-8", gettext("from the book \""))
                  . $row->{'book'} . "\"\n";
            }
            $labelText .=
              Encode::decode("utf-8", gettext("which was written by \""))
              . $row->{'artist'} . "\"";
            my $deletexml = Gtk2::GladeXML->new($globals->{'gladefile'},
                'dialogConfirm', 'lyricue');
            $deletexml->signal_autoconnect_from_package('');
            $deletexml->get_widget('labelDelete')->set_text($labelText);
            my $confirm = $deletexml->get_widget('dialogConfirm')->run();
            if ($confirm eq "ok") {
                do_delete_song($songid);
                close_dialog($deletexml->get_widget('dialogConfirm'));
                update_available();
            }
        }
    }
}

#***

#****m* lyricue/do_delete_song
# NAME
#   do_delete_song --
# SYNOPSIS
#   do_delete_song ($songid)
# FUNCTION
#   Do the actual deletion of a song including from the playlist
# INPUTS
#   $songid - Id of song to be deleted
# OUTPUT
#   One less song
# SOURCE
#
sub do_delete_song {
    my ($songid) = @_;
    my ($query, $sth, $rv);
    debug("do delete song");

    $query = "DELETE FROM lyricMain WHERE id=" . $songid;
    qdebug($query);
    $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);

    $query = "DELETE FROM page WHERE songid=" . $songid;
    qdebug($query);
    $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);

    $query =
"SELECT playorder FROM playlist,page WHERE playlist.data=page.pageid AND playlist.type=\"song\" AND page.songid="
      . $songid;
    qdebug($query);
    $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);
    my $disable = 0;
    while (my @row = $sth->fetchrow_array()) {

        my $query2 = "DELETE FROM playlist WHERE playorder=" . $row[0];
        qdebug($query2);
        my $sth2 = $lyricDbh->prepare($query2)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        my $rv2 = $sth2->execute
          || display_fatal($errorcodes->{'sqlexecute'}, $!);
        $disable = 1;
    }
    if ($disable == 1) {

        #Cancel any loop timers
        #Continuing to loop over a modified playlists due to
        #removed items represents a possible crash risk - better safe
        #than sorry.
        #
        reset_timer($globals->{'timer'});

        #
    }

}

#***

#****m* lyricue/do_save_song
# NAME
#   do_save_song --
# SYNOPSIS
#   do_save_song ()
# FUNCTION
#   Save the song to the DB
# INPUTS
# OUTPUT
#   One more song in the DB
# SOURCE
#
sub do_save_song {
    my ($query, $sth, $page, $songid);
    debug("do save song");
    my $newitem = FALSE;

    $songid = $widgets->{'add'}->get_widget('windowEditSong')->{user_data};
    if ($songid != 0) {
        debug("Song number: " . $songid);
        $query = "SELECT pagenum FROM page WHERE songid=" . $songid;
        qdebug($query);
        $sth = $lyricDbh->prepare($query)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);
        my (%dbpages);
        while (my @row = $sth->fetchrow_array) {
            $dbpages{$row[0]} = 1;
            my $tmp;
            my $found = FALSE;
            foreach $tmp (keys %pageOrder) {
                if ($pageOrder{$tmp} == $row[0]) {
                    $found = TRUE;
                }
            }
            if (!$found) {
                debug("Page Delete : " . $row[0] . "|" . $pageOrder{$row[0]});
                $query =
                    "SELECT pageid FROM page WHERE songid=" . $songid
                  . " AND pagenum="
                  . $row[0];
                qdebug($query);
                my $sth2 = $lyricDbh->prepare($query)
                  || display_fatal($errorcodes->{'sqlprepare'}, $!);
                $sth2->execute
                  || display_fatal($errorcodes->{'sqlexecute'}, $!);
                my @tmpid = $sth2->fetchrow_array();

                $query = "DELETE FROM page WHERE pageid=" . $tmpid[0];
                qdebug($query);
                $sth2 = $lyricDbh->prepare($query)
                  || display_fatal($errorcodes->{'sqlprepare'}, $!);
                $sth2->execute
                  || display_fatal($errorcodes->{'sqlexecute'}, $!);

                $query =
                  "DELETE FROM playlist WHERE type='song' AND data="
                  . $tmpid[0];
                qdebug($query);
                $sth2 = $lyricDbh->prepare($query)
                  || display_fatal($errorcodes->{'sqlprepare'}, $!);
                $sth2->execute
                  || display_fatal($errorcodes->{'sqlexecute'}, $!);
            }
        }

        foreach $page (keys(%pageOrder)) {
            if ($dbpages{$pageOrder{$page}}) {
                $query = "UPDATE page SET lyrics="
                  . $lyricDbh->quote(
                    Encode::encode(
                        "iso-8859-1",
                        get_buffer_text($widgets->{'textAPageB'}{$page})
                    )
                  )
                  . " WHERE songid="
                  . $songid
                  . " AND pagenum="
                  . $pageOrder{$page};
                qdebug($query);
                $sth = $lyricDbh->prepare($query)
                  || display_fatal($errorcodes->{'sqlprepare'}, $!);
                debug("Page Edit : " . $page . "|" . $pageOrder{$page});
                $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);
            } else {
                $query =
                  "INSERT INTO page (songid,pagenum,lyrics) VALUES (" . $songid
                  . ", "
                  . $pageOrder{$page} . ", "
                  . $lyricDbh->quote(
                    Encode::encode(
                        "iso-8859-1",
                        get_buffer_text($widgets->{'textAPageB'}{$page})
                    )
                  ) . ")";
                qdebug($query);
                $sth = $lyricDbh->prepare($query)
                  || display_fatal($errorcodes->{'sqlprepare'}, $!);
                debug("Page Add : " . $page . "|" . $pageOrder{$page});
                $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);
            }
        }

        $query = "UPDATE lyricMain SET title="
          . quote(
            Encode::encode(
                "iso-8859-1",
                $widgets->{'add'}->get_widget('entryEditName')->get_text()
            )
          );
        $query .= ", songnum="
          . quote(
            Encode::encode(
                "iso-8859-1",
                $widgets->{'add'}->get_widget('entryEditNumber')->get_text()
            )
          );
        $query .= ", book="
          . quote(
            Encode::encode(
                "iso-8859-1",
                $widgets->{'add'}->get_widget('entryEditBook')->get_text()
            )
          );
        $query .= ", artist="
          . quote(
            Encode::encode(
                "iso-8859-1",
                $widgets->{'add'}->get_widget('entryEditArtist')->get_text()
            )
          );
        $query .= ", copyright="
          . quote(
            Encode::encode(
                "iso-8859-1",
                $widgets->{'add'}->get_widget('entryEditCopyright')->get_text()
            )
          );
        my $noaudit = "";
        if (!$widgets->{'add'}->get_widget('checkEditAudit')->get_active()) {
            $noaudit = "NOAUDIT ";
        }
        $query .= ", keywords="
          . quote(
            Encode::encode(
                "iso-8859-1",
                $noaudit
                  . $widgets->{'add'}->get_widget('entryEditKeywords')
                  ->get_text()
            )
          );
        $query .= " WHERE id=" . $songid;
        qdebug($query);
        $sth = $lyricDbh->prepare($query)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);
    } else {

        # Find next id
        $query = "SELECT MAX(id)+1 FROM lyricMain WHERE id < 2000000";
        qdebug($query);
        $sth = $lyricDbh->prepare($query)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);
        my @row = $sth->fetchrow_array;
        $songid = $row[0];
        debug("Song number: " . $songid);

        # insert into db
        $query =
"INSERT INTO lyricMain ( id, title, songnum, book, artist, keywords,copyright) VALUES ( "
          . $songid . ", ";
        $query .= quote(
            Encode::encode(
                "iso-8859-1",
                $widgets->{'add'}->get_widget('entryEditName')->get_text()
            )
        ) . ",";
        $query .= quote(
            Encode::encode(
                "iso-8859-1",
                $widgets->{'add'}->get_widget('entryEditNumber')->get_text()
            )
        ) . ",";
        $query .= quote(
            Encode::encode(
                "iso-8859-1",
                $widgets->{'add'}->get_widget('entryEditBook')->get_text()
            )
        ) . ",";
        $query .= quote(
            Encode::encode(
                "iso-8859-1",
                $widgets->{'add'}->get_widget('entryEditArtist')->get_text()
            )
        ) . ",";
        $query .= quote(
            Encode::encode(
                "iso-8859-1",
                $widgets->{'add'}->get_widget('entryEditKeywords')->get_text()
            )
        ) . ",";
        $query .= quote(
            Encode::encode(
                "iso-8859-1",
                $widgets->{'add'}->get_widget('entryEditCopyright')->get_text()
            )
        ) . ")";
        qdebug($query);
        $sth = $lyricDbh->prepare($query)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);

        my $pagenum = 0;
        foreach $page (keys(%pageOrder)) {
            $query =
                "INSERT INTO page (songid,pagenum,lyrics) VALUES (" . $songid
              . ", "
              . $pageOrder{$page} . ", "
              . $lyricDbh->quote(
                Encode::encode(
                    "iso-8859-1",
                    get_buffer_text($widgets->{'textAPageB'}{$page})
                )
              ) . ")";
            qdebug($query);
            $sth = $lyricDbh->prepare($query)
              || display_fatal($errorcodes->{'sqlprepare'}, $!);
            debug("Page Add : " . $page . "|" . $pageOrder{$page});
            $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);
        }
    }
    return $songid;
}

#***

#****m* lyricue/save_song
# NAME
#   save_song --
# SYNOPSIS
#   save_song ()
# FUNCTION
#   Save the song, close the add window and update the available songs
# INPUTS
# OUTPUT
#   Calls a few others to do anything
# SOURCE
#
sub save_song {
    debug("save song");
    do_save_song();
    close_add_window();
    update_available();
}

#***

#****m* lyricue/add_page
# NAME
#   add_page --
# SYNOPSIS
#   add_page ()
# FUNCTION
#   Add page clicked
# INPUTS
# OUTPUT
#   Adds a page to the song edit screen
# SOURCE
#
sub add_page {
    debug("Add page clicked");

    # Find free hash number
    my $hashnum = 0;
    while (exists $pageOrder{$hashnum}) {
        $hashnum++;
    }
    debug($hashnum);
    my $pagenum =
      $widgets->{'add'}->get_widget('notebookEditPages')->get_current_page + 1;

    #
    # Construct a GtkScrolledWindow 'scrollAPage'
    $widgets->{'scrollAPage'}{$hashnum} = Gtk2::ScrolledWindow->new;
    $widgets->{'scrollAPage'}{$hashnum}->set_policy('always', 'always');
    $widgets->{'scrollAPage'}{$hashnum}->show;

    #
    # Construct a GtkText 'textAPage'
    $widgets->{'textAPage'}{$hashnum}  = Gtk2::TextView->new;
    $widgets->{'textAPageB'}{$hashnum} = Gtk2::TextBuffer->new(undef);
    $widgets->{'textAPage'}{$hashnum}
      ->set_buffer($widgets->{'textAPageB'}{$hashnum});
    $widgets->{'textAPage'}{$hashnum}->set_editable(TRUE);
    $widgets->{'textAPage'}{$hashnum}->set_cursor_visible(TRUE);
    $widgets->{'scrollAPage'}{$hashnum}->add($widgets->{'textAPage'}{$hashnum});
    $widgets->{'textAPage'}{$hashnum}->show;

    #
    # Construct a GtkLabel 'labelAPage'
    $widgets->{'labelAPage'}{$hashnum} = new Gtk2::Label(('Page 1'));
    $widgets->{'labelAPage'}{$hashnum}->set_justify('center');
    $widgets->{'labelAPage'}{$hashnum}->set_line_wrap(0);
    $widgets->{'labelAPage'}{$hashnum}->show;
    $widgets->{'labelAPage'}{$hashnum}->set_alignment(0.5, 0.5);

    $pageOrder{$hashnum} = $pagenum;
    $widgets->{'add'}->get_widget('notebookEditPages')
      ->insert_page($widgets->{'scrollAPage'}{$hashnum},
        $widgets->{'labelAPage'}{$hashnum}, $pagenum);
    renumber_pages($pagenum, $hashnum);
    $widgets->{'add'}->get_widget('notebookEditPages')
      ->set_current_page($pagenum);
    $widgets->{'add'}->get_widget('notebookEditPages')->show_all();
    $widgets->{'add'}->get_widget('buttonEditRemovePage')->set_sensitive(TRUE);
    $widgets->{'add'}->get_widget('remove_page1')->set_sensitive(TRUE);

    return $hashnum;
}

#***

#****m* lyricue/renumber_pages
# NAME
#   renumber_pages --
# SYNOPSIS
#   renumber_pages ($pagenum, $newitem)
# FUNCTION
#   Re-number the pages in a edited song
# INPUTS
#   $pagenum - The page number added/deleted
#   $newitem - The new item added
# OUTPUT
#   Re-ordered page list
# SOURCE
#
sub renumber_pages {
    my ($pagenum, $newitem) = @_;
    debug("renumber pages");
    my $i;

    # Renumber pages
    foreach $i (keys(%pageOrder)) {
        debug("$i:$pageOrder{$i}|");
        my $page = $pageOrder{$i};
        if ($page == $pagenum) {
            if ($i == $newitem) {
                $pageOrder{$i}++;
            } else {

                # Skip it
            }
        } elsif ($page > $pagenum) {
            $pageOrder{$i}++;
        }
        debug("$i:$pageOrder{$i}");
        $widgets->{'labelAPage'}{$i}
          ->set_text(gettext("Page ") . $pageOrder{$i});
        $widgets->{'labelAPage'}{$i}->show();
    }
}

#***

#****m* lyricue/remove_page
# NAME
#   remove_page --
# SYNOPSIS
#   remove_page ()
# FUNCTION
#   Remove a page from the song being edited
# INPUTS
# OUTPUT
#   One less page
# SOURCE
#
sub remove_page {
    debug("Remove page clicked");

    my $i;
    my $pagenum =
      $widgets->{'add'}->get_widget('notebookEditPages')->get_current_page + 1;
    my %newpageOrder;

    my $count = 0;
    foreach $i (keys(%pageOrder)) {
        $count++;
        debug("-$pagenum:$i:$pageOrder{$i}:");
        if ($pageOrder{$i} < $pagenum) {
            $newpageOrder{$i} = $pageOrder{$i};
            $widgets->{'labelAPage'}{$i}
              ->set_text(gettext("Page ") . $newpageOrder{$i});
            debug("$newpageOrder{$i}");
        } elsif ($pageOrder{$i} == $pagenum) {
            $widgets->{'add'}->get_widget('notebookEditPages')
              ->remove_page($pagenum - 1);
            debug("");
        } elsif ($pageOrder{$i} > $pagenum) {
            $newpageOrder{$i} = $pageOrder{$i} - 1;
            debug("$newpageOrder{$i}");
            $widgets->{'labelAPage'}{$i}
              ->set_text(gettext("Page ") . $newpageOrder{$i});
        }
    }
    %pageOrder = %newpageOrder;
    if ($count <= 2) {
        $widgets->{'add'}->get_widget('buttonEditRemovePage')
          ->set_sensitive(FALSE);
        $widgets->{'add'}->get_widget('remove_page1')->set_sensitive(FALSE);
    }
}

#***

#****m* lyricue/close_add_window
# NAME
#   close_add_window --
# SYNOPSIS
#   close_add_window ()
# FUNCTION
#   Close the add window
# INPUTS
# OUTPUT
#   Add window closed
# SOURCE
#
sub close_add_window {
    debug("Close add window");
    $widgets->{'add'}->get_widget('windowEditSong')->destroy();
    undef $widgets->{'add'};
}

#***

#****m* lyricue/close_dialog
# NAME
#   close_dialog --
# SYNOPSIS
#   close_dialog ($widget)
# FUNCTION
#   Close the calling widgets toplevel window
# INPUTS
#   $widget - Calling widget
# OUTPUT
#   window closed
# SOURCE
#
sub close_dialog {
    my ($widget) = @_;
    debug("Close dialog");
    if ($widget) {
        $widget->get_toplevel->destroy;
    }
}

#***

#****m* lyricue/edit_song
# NAME
#   edit_song --
# SYNOPSIS
#   edit_song ()
# FUNCTION
#   Edit a song
# INPUTS
# OUTPUT
#   Calls create_window_add to edit the chosen song
# SOURCE
#
sub edit_song {
    my $i;
    debug("Edit clicked");
    my $selection =
      $widgets->{'main'}->get_widget('treeAvailable')->get_selection;
    my ($model, $iter) = $selection->get_selected;
    if ($iter) {
        my $songid = $model->get($iter, 3);
        if (   $widgets->{'add'}
            && $widgets->{'add'}->get_widget('windowEditSong'))
        {
            if (!$widgets->{'add'}->get_widget('windowEditSong')->visible) {
                $widgets->{'add'}->get_widget('windowEditSong')->destroy;

                $widgets->{'add'} = Gtk2::GladeXML->new($globals->{'gladefile'},
                    'windowEditSong', 'lyricue');
                $widgets->{'add'}->signal_autoconnect_from_package('');
                $widgets->{'add'}->get_widget('buttonEditPreviewServer')
                  ->signal_connect("clicked", "preview_page", "MINI");
                %pageOrder = ();
                update_songinfo($songid);
            }
        } else {
            $widgets->{'add'} = Gtk2::GladeXML->new($globals->{'gladefile'},
                'windowEditSong', 'lyricue');
            $widgets->{'add'}->signal_autoconnect_from_package('');
            $widgets->{'add'}->get_widget('buttonEditPreviewServer')
              ->signal_connect("clicked", "preview_page", "MINI");
            %pageOrder = ();
            $widgets->{'add'}->get_widget('notebookEditPages')->remove_page(0);
            $widgets->{'add'}->get_widget('windowEditSong')->show();
            update_songinfo($songid);
        }
    }
}

#***

#****m* lyricue/do_add_verse
# NAME
#   do_add_verse --
# SYNOPSIS
#   do_add_verse ($widget)
# FUNCTION
#   Add the chosen reading
# INPUTS
#   $widget - Calling widget
# OUTPUT
#   Verse added to playlist
# SOURCE
#
sub do_add_verse {
    my ($widget) = @_;
    debug("Add the chosen reading");

    debug($globals->{'verses'} . "-" . $globals->{'verseEnd'});

    my $book = $widget->get_toplevel->{user_data};
    $book =~ s/ -.*$//g;
    my $chapter = $widget->get_toplevel->{user_data};
    $chapter =~ s/^.*- //g;
    insert_verse($book, $chapter, $globals->{'verses'}, $globals->{'verseEnd'});
    close_dialog($widget);
}

sub insert_verse {
    my ($book, $chapter, $start, $end) = @_;
    debug("insert verse");
    my $verse =
      $book . ":" . $chapter . ":" . $start . "-" . $chapter . ":" . $end;
    debug($verse);

    my ($sth, $rv, $row, $playorder, $playlist);

    # Find next playlist entry
    my $query = "SELECT MAX(playorder) FROM playlist";
    qdebug($query);
    $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);

    if ($row = $sth->fetchrow_hashref()) {
        if ($row->{'MAX(playorder)'}) {
            $playorder = $row->{'MAX(playorder)'} + 1;
        } else {
            $playorder = 1;
        }
    } else {
        $playorder = 1;
    }

    # Find next playlists entry
    $query = "SELECT MAX(id) FROM playlists";
    qdebug($query);
    $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    $rv       = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);
    $row      = $sth->fetchrow_hashref();
    $playlist = $row->{'MAX(id)'} + 1;

    # Add verse to main playlist
    my $main_playlist =
      $widgets->{'main'}->get_widget('labelCurrentPlaylist')->{user_data};
    $query =
        "INSERT INTO playlist (playorder,playlist,type,data) VALUES ("
      . $playorder . ", "
      . $main_playlist
      . ", \"play\", "
      . $playlist . ")";
    qdebug($query);
    $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);

    $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);
    $playorder++;

    # Add entry to playlists table
    $query =
        "INSERT INTO playlists (id,title) VALUES ("
      . $playlist . ", \""
      . Encode::encode("iso-8859-1", $verse) . "\")";
    qdebug($query);
    $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);

    $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);

    # Get current settings
    update_display("status", "", "");

    my $pagenum = 1;
    my $loop    = TRUE;
    my $minv    = $start;
    while ($loop) {
        my $maxv = get_max_verse($book, $chapter, $minv, $end);
        $query =
            "INSERT INTO playlist (playlist,playorder,type,data) VALUES ("
          . $playlist . ", "
          . $playorder
          . ", \"vers\", \""
          . $minv . "-"
          . $maxv . "\")";
        $playorder++;
        qdebug($query);
        my $sth2 = $lyricDbh->prepare($query)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);

        my $rv2 = $sth2->execute
          || display_fatal($errorcodes->{'sqlexecute'}, $!);
        if ($maxv >= $end) {
            $loop = FALSE;
        }
        $minv = $maxv + 1;
    }

    update_playlist();
}

#***

#****m* lyricue/import_song
# NAME
#   import_song --
# SYNOPSIS
#   import_song ($widget)
# FUNCTION
#   Import a song into the add song dialog
# INPUTS
#   $widget - Calling widget
# OUTPUT
#   A dialog to open song to import
# SOURCE
#
sub import_song {
    debug("import song");
    my $filexml =
      Gtk2::GladeXML->new($globals->{'gladefile'}, 'dialogFile', 'lyricue');
    $filexml->signal_autoconnect_from_package('');
    $filexml->get_widget('buttonFileOK')
      ->signal_connect("clicked", "do_import_song", $filexml);
}

#***

#****m* lyricue/do_import_song
# NAME
#   do_import_song --
# SYNOPSIS
#   do_import_song ($widget, $file_dialog)
# FUNCTION
#   Actually import the song into the add song dialog
# INPUTS
#   $widget - Calling widget
#   $file_dialog - Dialog containing selected filename
# OUTPUT
#   Filled in add song dialog
# SOURCE
#
sub do_import_song {
    my ($widget, $filexml) = @_;
    debug("do import song");
    my $filename   = $filexml->get_widget('dialogFile')->get_filename;
    my $lcfilename = lc($filename);

    if ($lcfilename =~ /txt$/) {
        import_song_text($filename);
    } elsif ($lcfilename =~ /usr$/) {
        import_song_songselect($filename);
    } elsif ($lcfilename =~ /opw$/) {
        import_song_opw($filename);
    }

    close_dialog($widget);
}

sub import_song_text {
    my ($filename) = @_;
    debug("import text song");

    my ($artist, $name, $keywords, $number, $book, $copyright) = "";
    my $hashnum = 0;
    open(SONG, $filename) || return;
    while (<SONG>) {
        if (/^Name:/) {
            if (!$name) {
                chomp;
                $name = $_;
                $name =~ s/^.*://g;
                $name =~ s/^ *//g;
                $widgets->{'add'}->get_widget('entryEditName')->set_text($name);
            }
        } elsif (/^Book:/) {

            if (!$book) {
                chomp;
                $book = $_;
                $book =~ s/^.*://g;
                $book =~ s/^ *//g;
                $widgets->{'add'}->get_widget('entryEditBook')->set_text($book);
            }
        } elsif (/^Number:/) {

            if (!$number) {
                chomp;
                $number = $_;
                $number =~ s/^.*://g;
                $number =~ s/^ *//g;
                $widgets->{'add'}->get_widget('entryEditNumber')
                  ->set_text($number);
            }
        } elsif (/^Artist:/) {
            chomp;
            $_ =~ s/^.*://g;
            $_ =~ s/^ *//g;

            if (!$artist) {
                $artist = $_;
                $widgets->{'add'}->get_widget('entryEditArtist')
                  ->set_text($artist);
            } else {
                $artist = "\n" . $_;
                $widgets->{'add'}->get_widget('entryEditArtist')
                  ->append_text($artist);
            }
        } elsif (/^Keywords:/) {

            if (!$keywords) {
                chomp;
                $keywords = $_;
                $keywords =~ s/^.*://g;
                $keywords =~ s/^ *//g;
                $widgets->{'add'}->get_widget('entryEditKeywords')
                  ->set_text($keywords);
            }
        } elsif (/^Copyright/) {

            if (!$copyright) {
                chomp;
                $copyright = $_;
                $copyright =~ s/^.*://g;
                $copyright =~ s/^ *//g;
                $widgets->{'add'}->get_widget('entryEditCopyright')
                  ->set_text($copyright);
            }
        } elsif (/^--/) {
            debug("add page");
            $hashnum = add_page();
        } else {
            debug("add line");
            my $iter = $widgets->{'textAPageB'}{$hashnum}->get_end_iter();
            $widgets->{'textAPageB'}{$hashnum}->insert($iter, $_);
        }
    }
    close SONG;
}

#***

#****m* lyricue/update_songinfo
# NAME
#   update_songinfo --
# SYNOPSIS
#   update_songinfo ($songid)
# FUNCTION
#   Fill in the add song dialog with the chosen songs details/lyrics
# INPUTS
#   $songid - Id of song to edit
# OUTPUT
#   Filled in add song dialog
# SOURCE
#
sub update_songinfo {
    my ($songid) = @_;
    debug("Edit clicked");
    my ($sth, $query, $hashnum);

    if ($songid) {
        $widgets->{'add'}->get_widget('windowEditSong')->{user_data} = $songid;
        $query =
            "SELECT lyrics FROM page WHERE songid=\"" . $songid
          . "\" ORDER BY pagenum";
        qdebug($query);
        $sth = $lyricDbh->prepare($query)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);

        while ($row = $sth->fetchrow_hashref()) {
            $hashnum = add_page();
            $widgets->{'textAPageB'}{$hashnum}->set_text($row->{'lyrics'});
        }

        $query =
"SELECT title,songnum,book,artist,keywords,copyright from lyricMain WHERE id="
          . $songid;
        qdebug($query);
        $sth = $lyricDbh->prepare($query)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);
        if ($row = $sth->fetchrow_hashref()) {
            $widgets->{'add'}->get_widget('entryEditName')
              ->set_text($row->{'title'});
            $widgets->{'add'}->get_widget('entryEditBook')
              ->set_text($row->{'book'});
            $widgets->{'add'}->get_widget('entryEditNumber')
              ->set_text($row->{'songnum'});
            $widgets->{'add'}->get_widget('entryEditArtist')
              ->set_text($row->{'artist'});
            if ($row->{'keywords'} =~ /^NOAUDIT /) {
                $row->{'keywords'} =~ s/^NOAUDIT //g;
                $widgets->{'add'}->get_widget('checkEditAudit')
                  ->set_active(FALSE);
            } else {
                $widgets->{'add'}->get_widget('checkEditAudit')
                  ->set_active(TRUE);
            }
            $widgets->{'add'}->get_widget('entryEditKeywords')
              ->set_text($row->{'keywords'});
            $widgets->{'add'}->get_widget('entryEditCopyright')
              ->set_text($row->{'copyright'});
        }
        $widgets->{'add'}->get_widget('notebookEditPages')->set_current_page(0);
    }
}

#***

#****m* lyricue/add_to_playlist
# NAME
#   add_to_playlist --
# SYNOPSIS
#   add_to_playlist ($widget)
# FUNCTION
#   Add the chosen songs to the playlist
# INPUTS
#   $widget - Calling widget
# OUTPUT
#   More songs in the playlist
# SOURCE
#
sub add_to_playlist {

    debug("Add to playlist clicked");
    my $selection =
      $widgets->{'main'}->get_widget('treeAvailable')->get_selection;
    my ($model, $iter) = $selection->get_selected;
    if ($iter) {
        add_single_song($model->get($iter, 3));
    }

    update_playlist();
}

#***

#****m* lyricue/add_single_song
# NAME
#   add_single_song --
# SYNOPSIS
#   add_single_song ($availableSelection)
# FUNCTION
#   Add a single song to the playlist
#   Optionally audit this addition
# INPUTS
#   $availableSelection - Song id to add to the playlist
# OUTPUT
#   One more song in the playlist
# SOURCE
#
sub add_single_song {
    my ($availableSelection) = @_;
    debug("add single song");
    my ($playorder);

    my $query = "SELECT MAX(playorder) FROM playlist";
    $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);
    if ($row = $sth->fetchrow_hashref()) {
        if ($row->{'MAX(playorder)'}) {
            $playorder = $row->{'MAX(playorder)'} + 1;
        } else {
            $playorder = 1;
        }
    } else {
        $playorder = 1;
    }

    # Find next playlists entry
    $query = "SELECT MAX(id) FROM playlists";
    qdebug($query);
    $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);
    $row = $sth->fetchrow_hashref();
    my $playlist = $row->{'MAX(id)'} + 1;

    $query =
        "INSERT INTO playlist (playorder, playlist, data,type) VALUES ("
      . $playorder . ","
      . $widgets->{'main'}->get_widget('labelCurrentPlaylist')->{user_data}
      . ","
      . $playlist
      . ",\"play\")";
    qdebug($query);
    $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);

    # Find song info
    my $title = "";
    $query =
"SELECT title,pageid,keywords FROM lyricMain, page WHERE songid=id AND id="
      . $availableSelection
      . " ORDER BY pagenum";
    qdebug($query);
    $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);
    my $audit = TRUE;
    while ($row = $sth->fetchrow_hashref()) {

        # Add the pages
        my $keywords = $row->{'keywords'};
        if ($keywords =~ /^NOAUDIT /) {
            $audit = FALSE;
        }
        $title = $row->{'title'};
        $playorder++;
        my $query2 =
            "INSERT INTO playlist (playorder, playlist, data,type) VALUES ("
          . $playorder . ", "
          . $playlist . ","
          . $row->{'pageid'}
          . ", \"song\")";
        qdebug($query2);
        my $sth2 = $lyricDbh->prepare($query2)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        my $rv2 = $sth2->execute
          || display_fatal($errorcodes->{'sqlexecute'}, $!);
    }

    # Add playlists entry
    $query =
        "INSERT INTO playlists (id,title,ref) VALUES ("
      . $playlist . ",\""
      . $title . "\","
      . $availableSelection . ")";
    $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);

    # audit this addition
    # currently always runs due to problem with passing in widgets
    debug("Audit: $config->{'Audit'}");
    if ($config->{'Audit'} && $audit) {
        $query =
            "INSERT INTO audit (songid,playdate) VALUES("
          . $availableSelection
          . ", NOW())";
        debug("Auditing with: " . $query);
        $sth = $lyricDbh->prepare($query)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);
    }
}

#***

#****m* lyricue/remove_from_playlist
# NAME
#   remove_from_playlist --
# SYNOPSIS
#   remove_from_playlist ()
# FUNCTION
#   Remove selected songs from the playlist
# INPUTS
# OUTPUT
#   Less songs in the playlist
# SOURCE
#
sub remove_from_playlist {
    debug("remove from playlist");

    #Cancel any loop timers
    #Continuing to loop over possibly removed playlist
    #items represents a major crash risk - better safe
    #than sorry.
    #
    reset_timer($globals->{'timer'});

    #

    my $selection =
      $widgets->{'main'}->get_widget('treePlaylist')->get_selection;
    debug("Remove from playlist clicked");
    my ($model, $iter) = $selection->get_selected;
    if ($iter) {
        remove_single_item($model->get($iter, 2));
        update_playlist();
    }
}

#***

#****m* lyricue/remove_single_item
# NAME
#   remove_single_item --
# SYNOPSIS
#   remove_single_item ($item)
# FUNCTION
#   Remove a single song from the playlist
# INPUTS
#   $item - Playlist Id to remove
# OUTPUT
#   One less song on the playlist
# SOURCE
#
sub remove_single_item {
    my ($item) = @_;
    debug("Deleting $item");
    reset_timer($globals->{'timer'});
    my $query = "SELECT type,data  FROM playlist WHERE playorder=" . $item;
    qdebug($query);
    my $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    my $rv  = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);
    my @row = $sth->fetchrow_array;

    if ($row[0] eq "play" | $row[0] eq "sub") {
        $query = "SELECT playorder FROM playlist WHERE playlist=" . $row[1];
        qdebug($query);
        $sth = $lyricDbh->prepare($query)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);
        while (my @row2 = $sth->fetchrow_array) {
            remove_single_item($row2[0]);
        }

        $query = "DELETE FROM playlist WHERE playlist=" . $row[1];
        qdebug($query);
        $sth = $lyricDbh->prepare($query)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);

        $query = "DELETE FROM playlists WHERE id=" . $row[1];
        qdebug($query);
        $sth = $lyricDbh->prepare($query)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);
    }

    $query = "DELETE FROM playlist WHERE playorder=" . $item;
    qdebug($query);
    $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);

    $query = "DELETE FROM associations WHERE playlist=" . $item;
    qdebug($query);
    $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);

}

#***

#****m* lyricue/clear_playlist
# NAME
#   clear_playlist --
# SYNOPSIS
#   clear_playlist ($widget)
# FUNCTION
#   Clear the playlist
# INPUTS
#   $widget - Calling widget
# OUTPUT
#   No songs in playlist
# SOURCE
#
sub clear_playlist {
    my ($widget) = @_;
    debug("Clear playlist");
    reset_timer($globals->{'timer'});
    my $deletexml =
      Gtk2::GladeXML->new($globals->{'gladefile'}, 'dialogConfirm', 'lyricue');
    $deletexml->signal_autoconnect_from_package('');
    my $labelText =
      Encode::decode("utf-8",
        gettext("Are you sure you wish to clear the current playlist?"));
    $deletexml->get_widget('labelDelete')->set_text($labelText);
    $deletexml->get_widget('dialogConfirm')
      ->set_title(Encode::decode("utf-8", gettext("Confirm Clear Playlist")));
    my $confirm = $deletexml->get_widget('dialogConfirm')->run();

    if ($confirm eq "ok") {
        my $main_playlist =
          $widgets->{'main'}->get_widget('labelCurrentPlaylist')->{user_data};

        my $query =
          "SELECT playorder FROM playlist WHERE playlist=" . $main_playlist;
        $sth = $lyricDbh->prepare($query)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);
        while (my @row = $sth->fetchrow_array()) {
            remove_single_item($row[0]);
        }
        update_playlist();
    }
    close_dialog($deletexml->get_widget('dialogConfirm'));
}

#***

#****m* lyricue/display_song
# NAME
#   Display_song --
# SYNOPSIS
#   display_song (undef, $event)
# FUNCTION
#   Display a song if double-clicked, or popup menu if right-clicked
# INPUTS
#   $event - Calling event
# OUTPUT
#   Updated display or right-click menu is popped-up
# SOURCE
#
sub display_song {
    my ($widget, $event) = @_;

    #debug ("event ".$event->key); #Too many calls to this one
    if (defined($event->type)) {
        if (
            ($event->type eq '2button-press')
            || (
                ($event->type eq 'key-press')
                && (   ($event->keyval == $Gtk2::Gdk::Keysyms{Return})
                    || ($event->keyval == $Gtk2::Gdk::Keysyms{Enter}))
            )
          )
        {
            reset_timer($globals->{'timer'});
            my $selection =
              $widgets->{'main'}->get_widget('treePlaylist')->get_selection;
            my ($model, $iter) = $selection->get_selected;
            if ($iter) {
                update_display("display", $model->get($iter, 2), "");

            }
            return TRUE;
        } elsif ($event->type eq 'button-press') {
            my $path = $widget->get_path_at_pos($event->x, $event->y);
            if (defined $path) {
                $widget->get_selection->select_path($path);
            }
            if ($event->button == 3) {
                popup_play_menu($event);
                return TRUE;
            } else {
                if (   $widgets->{'transitions'}
                    && $widgets->{'transitions'}
                    ->get_widget('windowTransitions'))
                {
                    my $selection =
                      $widgets->{'main'}->get_widget('treePlaylist')
                      ->get_selection;
                    my ($model, $iter) = $selection->get_selected;
                    if ($iter) {
                        my $transition = $model->get($iter, 3);

                        # Set the transition values
                        my $old_direction = mod($transition, 32);
                        $transition = $transition >> 5;
                        my $new_direction = mod($transition, 32);
                        my $effect = $transition >> 5;
                        if ($effect == NONE) {
                            $widgets->{'transitions'}
                              ->get_widget('radioTransNone')->set_active(TRUE);
                        }
                        if ($effect == WIPE) {
                            $widgets->{'transitions'}
                              ->get_widget('radioTransWipe')->set_active(TRUE);
                        }
                        if ($effect == SLIDE_TEXT) {
                            $widgets->{'transitions'}
                              ->get_widget('radioTransSlide')->set_active(TRUE);
                        }
                        if ($old_direction & WAIT) {
                            $widgets->{'transitions'}
                              ->get_widget('checkTransOld')->set_active(TRUE);
                            $old_direction = $old_direction - WAIT;
                        }
                        if ($new_direction & WAIT) {
                            $widgets->{'transitions'}
                              ->get_widget('checkTransNew')->set_active(TRUE);
                            $new_direction = $new_direction - WAIT;
                        }
                        my @directions = (
                            "None",         "Up",
                            "Down",         "",
                            "Right",        "Up + Right",
                            "Down + Right", "",
                            "Left",         "Up + Left",
                            "Down + Left",  "",
                            "",             "",
                            "",             ""
                        );
                        $widgets->{'transitions'}->get_widget('entryTransOld')
                          ->set_text($directions[$old_direction]);
                        $widgets->{'transitions'}->get_widget('entryTransNew')
                          ->set_text($directions[$new_direction]);
                    }
                } else {
                    update_quickedit();
                    if ($config->{'DynamicPreview'}) {
                        preview_playlist_item();
                    }
                }
            }
        }
    }
    return FALSE;
}

#***

#****m* lyricue/display_now
# NAME
#   display_now --
# SYNOPSIS
#   display_now ($widget)
# FUNCTION
#   Display now button clicked
# INPUTS
#   $widget - Calling widget
# OUTPUT
#   Display updated with currently selected song
# SOURCE
#
sub display_now {
    my ($widget) = @_;
    debug("Display clicked");

    reset_timer($globals->{'timer'});

    my $selection =
      $widgets->{'main'}->get_widget('treePlaylist')->get_selection;
    my ($model, $iter) = $selection->get_selected;
    if ($iter) {
        update_display("display", $model->get($iter, 2), "");
    }
}

#***

#****m* lyricue/prev_page
# NAME
#   prev_page --
# SYNOPSIS
#   prev_page ()
# FUNCTION
#   Display the previous page in the server
# INPUTS
#   none
# OUTPUT
#   Calls update_display
# SOURCE
#
sub prev_page {
    debug("Prev clicked");
    reset_timer($globals->{'timer'});
    update_display("display", "prev_page", $config->{'LoopText'});
}

#***

#****m* lyricue/next_page
# NAME
#   next_page --
# SYNOPSIS
#   next_page ()
# FUNCTION
#   Display the next page in the server
# INPUTS
#   None
# OUTPUT
#   Calls update_display
# SOURCE
#
sub next_page {
    debug("Next clicked");
    reset_timer($globals->{'timer'});
    update_display("display", "next_page", $config->{'LoopText'});
}

#***

#****m* lyricue/prev_song
# NAME
#   prev_song --
# SYNOPSIS
#   prev_song ()
# FUNCTION
#   Display the previous song in the server
# INPUTS
#   none
# OUTPUT
#   Calls update_display
# SOURCE
#
sub prev_song {
    debug("Prev clicked");
    reset_timer($globals->{'timer'});
    update_display("display", "prev_song", $config->{'LoopText'});
}

#***

#****m* lyricue/next_song
# NAME
#   next_song --
# SYNOPSIS
#   next_song ()
# FUNCTION
#   Display the next song in the server
# INPUTS
#   None
# OUTPUT
#   Calls update_display
# SOURCE
#
sub next_song {
    debug("Next clicked");
    reset_timer($globals->{'timer'});
    update_display("display", "next_song", $config->{'LoopText'});
}

#***

#****m* lyricue/blank_page
# NAME
#   blank_page --
# SYNOPSIS
#   blank_page ()
# FUNCTION
#   Blank the server
# INPUTS
#   None
# OUTPUT
#   Calls update_display
# SOURCE
#
sub blank_page {
    debug("Blank page clicked");
    reset_timer($globals->{'timer'});
    update_display("blank", $config->{'BGImage'}, "");
}

#****m* lyricue/clear_text
# NAME
#   clear_text --
# SYNOPSIS
#   clear_text ()
# FUNCTION
#   Clear the server text
# INPUTS
#   None
# OUTPUT
#   Calls update_display
# SOURCE
#
sub clear_text {
    debug("Clear text clicked");
    reset_timer($globals->{'timer'});
    update_display("blank", "", "");
}

#****m* lyricue/next_point
# NAME
#   next_point --
# SYNOPSIS
#   next_point ()
# FUNCTION
#   Display the next point in the server
# INPUTS
#   None
# OUTPUT
#   Calls update_display
# SOURCE
#
sub next_point {
    debug("Next point clicked");
    reset_timer($globals->{'timer'});
    update_display("next_point", "", "");
}

#***

#****m* lyricue/preview_page
# NAME
#   preview_page --
# SYNOPSIS
#   preview_page ($widget)
# FUNCTION
#   Preview a page in the server
# INPUTS
#   $widget - Calling widget
# OUTPUT
#   Calls update_display
# SOURCE
#
sub preview_page {
    my ($widget, $MODE) = @_;
    debug("preview page");
    reset_timer($globals->{'timer'});

    my $current_page =
      $widgets->{'add'}->get_widget('notebookEditPages')->get_current_page() +
      1;
    my $current_hash = 1;
    debug($current_page . " page ");
    my $label = $widgets->{'labelAPage'};
    foreach (keys %$label) {
        if (  $widgets->{'labelAPage'}{$_}->get_text() eq gettext("Page ")
            . $current_page)
        {
            $current_hash = $_;
        }
    }
    debug($current_hash . " hash");

    #join all the strings together so update display will accept them, removing
    #any newlines or semicolons that would upset the split on the other end.
    my $titledata =
        $widgets->{'add'}->get_widget('entryEditName')->get_text()
      . "#BREAK#"
      . $widgets->{'add'}->get_widget('entryEditArtist')->get_text()
      . "#BREAK#"
      . $widgets->{'add'}->get_widget('entryEditCopyright')->get_text();
    $titledata =~ s/:/#SEMI#/g;
    my $songtext = get_buffer_text($widgets->{'textAPageB'}{$current_hash});
    $songtext =~ s/\n/#BREAK#/g;
    $songtext =~ s/:/#SEMI#/g;
    if ($MODE eq "SERVER") {
        update_display("preview", $titledata, $songtext);
    }
    preview_display("preview", $titledata, $songtext);
}

#***

#****m* lyricue/add_verse
# NAME
#   add_verse --
# SYNOPSIS
#   add_verse ($widget)
# FUNCTION
#   Add Verse clicked
# INPUTS
#   $widget - Calling widget
# OUTPUT
#   Calls create_dialogBook
# SOURCE
#
sub add_verse {
    debug("Add verse clicked");

    my $bookxml =
      Gtk2::GladeXML->new($globals->{'gladefile'}, 'windowBook', 'lyricue');
    $bookxml->signal_autoconnect_from_package('');
    $bookxml->get_widget('windowBook')->show;
}

#***

#****m* lyricue/add_image
# NAME
#   add_image --
# SYNOPSIS
#   add_image ($widget)
# FUNCTION
#   Add image clicked
# INPUTS
#   $widget - Calling widget
# OUTPUT
# SOURCE
#
sub add_image {
    debug("Add image clicked");

    $widgets->{'image'} =
      Gtk2::GladeXML->new($globals->{'gladefile'}, 'dialogImage', 'lyricue');
    $widgets->{'image'}->signal_autoconnect_from_package('');
    $widgets->{'image'}->get_widget('buttonImageAdd')
      ->signal_connect('clicked', 'import_image');
    $widgets->{'image'}->get_widget('dialogImage')->show;

    if ($widgets->{'image'}->get_widget('treeImage')->{user_data}
        && ($widgets->{'image'}->get_widget('treeImage')->{user_data} eq "load")
      )
    {
        $widgets->{'image'}->get_widget('treeImage')->{data} = ();
    } else {
        $widgets->{'image'}->get_widget('treeImage')->{user_data} = "load";
        my $renderer = Gtk2::CellRendererText->new;
        $renderer->set("editable", TRUE);
        $renderer->signal_connect("edited", "rename_media");
        my $column =
          Gtk2::TreeViewColumn->new_with_attributes("Filename", $renderer,
            text => 0);
        $widgets->{'image'}->get_widget('treeImage')->append_column($column);
        $widgets->{'image'}->get_widget('treeImage')
          ->get_selection->set_mode('multiple');
        $widgets->{'image'}->get_widget('buttonImageOK')
          ->signal_connect('clicked', "do_add_image");
    }

    # Fill sublists dropbox
    my $menu = Gtk2::Menu->new();
    $widgets->{'image'}->get_widget('optionImageSublist')->set_menu($menu);
    my $playlist =
      $widgets->{'main'}->get_widget('labelCurrentPlaylist')->{user_data};
    my $query =
"SELECT title, data FROM playlist, playlists WHERE playlist.data = playlists.id AND playlist = "
      . $playlist
      . " AND type = 'sub'";
    qdebug($query);
    my $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    my $rv = $sth->execute
      || display_fatal($errorcodes->{'sqlexecute'}, $!);
    my $item = Gtk2::MenuItem->new_with_label("Main");
    $item->{'user_data'} = $playlist;
    $item->show();
    $menu->append($item);
    my (@list);

    while ($row = $sth->fetchrow_hashref()) {
        my $item = Gtk2::MenuItem->new_with_label($row->{'title'});
        $item->{'user_data'} = $row->{'data'};
        $item->show();
        $menu->append($item);
        my @childid = find_more_children($row->{'data'});
        foreach (@childid) {
            debug("Child sublist found. ID: " . $_);
            my $query2 = "SELECT title FROM playlists WHERE id=" . $_;
            qdebug($query2);
            my $sth2 = $lyricDbh->prepare($query2)
              || display_fatal($errorcodes->{'sqlprepare'}, $!);
            my $rv2 = $sth2->execute
              || display_fatal($errorcodes->{'sqlexecute'}, $!);
            my $row2 = $sth2->fetchrow_hashref();
            my $item = Gtk2::MenuItem->new_with_label($row2->{'title'});
            $item->{'user_data'} = $row2->{'data'};
            $item->show();
            $menu->append($item);
        }
    }
    $widgets->{'image'}->get_widget('optionImageSublist')->set_history(0);

    update_imagedir("img", "");
}

sub update_imagedir {
    my ($type, $category) = @_;
    debug("update image directory");

    my $main = "";
    if ($type eq "bg") {
        $main = $config->{'SpecialBack'};
    } else {
        $main = $config->{'SpecialImage'};
    }
    if ($category eq "") {
        $category = $main;
    }
    debug("changing to $category");

    # display sorted list

    # Update categories list
    $widgets->{'image'}->get_widget('optionImageCategory')->{'user_data'} =
      $type;
    $widgets->{'image'}->get_widget('treeImage')->{'user_data'} = $category;
    my $history =
      $widgets->{'image'}->get_widget('optionImageCategory')->get_history();
    my $menu = Gtk2::Menu->new();
    $widgets->{'image'}->get_widget('optionImageCategory')->set_menu($menu);
    my $item = Gtk2::MenuItem->new_with_label($main);
    $item->show();
    $item->{user_data} = $main;
    $menu->append($item);

    $query =
        "SELECT DISTINCT category FROM media WHERE type=\"" . $type
      . "\" AND category != \""
      . $main
      . "\" ORDER BY category";
    qdebug($query);
    $sth = $mediaDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);

    while ($row = $sth->fetchrow_hashref()) {
        my $item = Gtk2::MenuItem->new_with_label($row->{'category'});
        $item->{user_data} = $row->{'category'};
        $item->show();
        $menu->append($item);

    }
    $widgets->{'image'}->get_widget('optionImageCategory')
      ->set_history($history);

    # update list of available images
    $query =
        "SELECT * FROM media WHERE category LIKE \""
      . $category
      . "\" AND type=\""
      . $type
      . "\" ORDER BY description";
    qdebug($query);
    $sth = $mediaDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);
    my $store = Gtk2::ListStore->new('Glib::String', 'Glib::String');
    my $iter;
    while ($row = $sth->fetchrow_hashref()) {
        $iter = $store->append;
        $store->set($iter, 0, $row->{'description'}, 1, $row->{'id'});
    }
    $widgets->{'image'}->get_widget('treeImage')->set_model($store);
    $widgets->{'image'}->get_widget('entryImageFontColour')
      ->set_text("Default");
    $widgets->{'image'}->get_widget('colorbuttonFontColour')
      ->set_color(Gtk2::Gdk::Color->parse($config->{'Colour'}));
    $widgets->{'image'}->get_widget('entryImageShadowColour')
      ->set_text("Default");
    $widgets->{'image'}->get_widget('colorbuttonShadowColour')
      ->set_color(Gtk2::Gdk::Color->parse($config->{'ShadowColour'}));
}

sub change_image_category {
    debug("change image category");
    my $category =
      $widgets->{'image'}->get_widget('optionImageCategory')
      ->get_menu->get_active->{user_data};
    if (defined $category) {
        if ($widgets->{'image'}->get_widget('treeImage')->{user_data} ne
            $category)
        {
            update_imagedir(
                $widgets->{'image'}->get_widget('optionImageCategory')
                  ->{'user_data'},
                $category
            );
        }
    }
}

#***

#****m* lyricue/update_display
# NAME
#   update_display --
# SYNOPSIS
#   update_display ($command, $primary, $secondary)
# FUNCTION
#   Open a connection the the server and send a command. Status is returned
# INPUTS
#   $command - Command to send
#   $primary - First parameter to send
#   $secondary - Second parameter to send
# OUTPUT
#   Updated display
# SOURCE
#
sub update_display {
    my ($command, $primary, $secondary) = @_;
    debug("update display");
    my $biblechanged = "";

    if ($globals->{'access'} !~ /s/) {

        $command   = "";
        $primary   = "";
        $secondary = "";
    }

    if (!defined($secondary)) {
        $secondary = "";
    }
    if (!defined($primary)) {
        $primary = "";
    }
    $primary   =~ s/:/#SEMI#/g;
    $secondary =~ s/:/#SEMI#/g;
    debug("Command: " . $command . ":" . $primary . ":" . $secondary);
    if (
        my $server = IO::Socket::INET->new(
            Proto    => "tcp",
            PeerAddr => $globals->{'host'},
            PeerPort => $globals->{'server_port'}
        )
      )
    {
        print $server Encode::encode("utf-8",
            $command . ":" . $primary . ":" . $secondary . "\n");
        if (defined(my $status = <$server>)) {
            chomp($status);
            if ($status =~ /^pl:/) {
                my @line = split(/:/, $status);
                $status = gettext("Displaying ");
                if ($line[1] eq "v") {
                    $status = gettext(" verses ");
                } else {
                    $status = gettext(" page ") . $line[3];
                    $widgets->{'main'}->get_widget('treePlaylist')
                      ->select_item($line[2]);
                }
                $widgets->{'main'}->get_widget('statusPlaylist')
                  ->push(1, $status);
            } elsif ($status =~ /^Status,/) {
                my @line = split(/,/, $status);
                foreach (@line) {
                    my @item = split(/:/, $_);
                    if ($item[0] eq "W") {
                        $config->{'Width'} = $item[1];
                    } elsif ($item[0] eq "H") {
                        $config->{'Height'} = $item[1];
                    } elsif ($item[0] eq "F") {
                        $config->{'Main'} = $item[1];
                    } elsif ($item[0] eq "B") {
                        if ($item[1] ne $globals->{'biblename'}) {
                            $biblechanged = $item[1];
                        }
                    }
                }
            } else {
                $widgets->{'main'}->get_widget('statusPlaylist')
                  ->push(1, $status);
            }
            debug($status);
        }
        close($server);
        debug("Sent ");
        if ($biblechanged ne "") {
            if (defined $bibleMenu->{$biblechanged}) {
                $bibleMenu->{$biblechanged}->set_active(TRUE);
            }
        }
    }
}

#***

#   1. Set bg in preview window to current bg in server
#   2. Send command to show playlist item to server
sub preview_playlist_item {
    debug("Previewing a playlist item...");

    my $selection =
      $widgets->{'main'}->get_widget('treePlaylist')->get_selection;
    my ($model, $iter) = $selection->get_selected;
    if ($iter) {

        # set the preview window bg to current bg
        my $current = $widgets->{'buttonCurr'}->{user_data};
        preview_display("backdrop", $current, undef);

        # display item
        preview_display("display", $model->get($iter, 2), undef);
    }
}

#****m* lyricue/preview_display
# NAME
#   preview_display --
# SYNOPSIS
#   preview_display ()
# FUNCTION
#   1. Start new server instance for preview windows (if required)
#   2. Issues commands to the preview server
# INPUTS
# OUTPUT
#   Updated display
# SOURCE
#
sub preview_display {
    my ($command, $primary, $secondary) = @_;
    if ($config->{'DynamicPreview'}) {
        if (!defined($secondary)) {
            $secondary = "";
        }
        if (!defined($primary)) {
            $primary = "";
        }
        debug(
            "Preview command: " . $command . ":" . $primary . ":" . $secondary);

      PUPDATE: {
            if (
                my $server = IO::Socket::INET->new(
                    Proto    => "tcp",
                    PeerAddr => "localhost",
                    PeerPort => $globals->{'preview_port'}
                )
              )
            {
                print $server $command . ":" . $primary . ":"
                  . $secondary . "\n";
                close($server);
                if (FALSE) {
                    my $status = "";

                    # if (defined(my $status = <$server>)) {
                    chomp($status);
                    if ($status =~ /^pl:/) {
                        my @line = split(/:/, $status);
                        $status = gettext("Previewed ");
                        if ($line[1] eq "v") {
                            $status .= gettext(" verses ");
                        } else {
                            $status .= gettext(" page ") . $line[3];
                            $widgets->{'main'}->get_widget('treePlaylist')
                              ->select_item($line[2]);
                        }
                        $widgets->{'main'}->get_widget('statusPlaylist')
                          ->push(1, $status);
                    } else {

                        $widgets->{'main'}->get_widget('statusPlaylist')
                          ->push(1, $status);
                    }
                    debug($status);
                }
                close($server);
            } else {

                #tcp/ip socket failure so server must be not running yet!
                debug("Preview not running?");
            }
        }
    }
}

#***

sub open_dialogColour {
    my ($widget, $event, $item) = @_;
    debug("Opening Colour dialog");
    debug("open_dialogColour: item=" . $item);
    debug("open_dialogColour: widget=" . $widget->get_name());
    my $colourxml =
      Gtk2::GladeXML->new($globals->{'gladefile'}, 'dialogColour', 'lyricue');
    $colourxml->signal_autoconnect_from_package('');
    $colourxml->get_widget('dialogColour')
      ->set_title(Encode::decode("utf-8", gettext("Select ")) . $item
          . Encode::decode("utf-8", gettext(" Colour")));
    $colourxml->get_widget('dialogColour')->{'user_data'} = $item;

    if ($item eq "Font") {
        $colourxml->get_widget('colourSelect')->set_current_color(
            Gtk2::Gdk::Color->parse(
                $widgets->{'prefs'}->get_widget('entryPrefColour')->get_text()
            )
        );
    }
    if ($item eq "Shadow") {
        $colourxml->get_widget('colourSelect')->set_current_color(
            Gtk2::Gdk::Color->parse(
                $widgets->{'prefs'}->get_widget('entryPrefShadowColour')
                  ->get_text()
            )
        );
    }
    if ($item eq "ImageFont") {

#$colourxml->get_widget('colourSelect')->set_current_color(Gtk2::Gdk::Color->parse($widgets->{'image'}->get_widget('entryImageFontColour')->get_text()));
        $colourxml->get_widget('colourSelect')
          ->set_current_color($widget->get_color());
    }
    if ($item eq "ImageShadow") {

#$colourxml->get_widget('colourSelect')->set_current_color(Gtk2::Gdk::Color->parse($widgets->{'image'}->get_widget('entryImageShadowColour')->get_text()));
        $colourxml->get_widget('colourSelect')
          ->set_current_color($widget->get_color());
    }
    $colourxml->get_widget('buttonColourOK')
      ->signal_connect("clicked", "change_font_colour", $colourxml);
    $colourxml->get_widget('buttonColourCancel')
      ->signal_connect("clicked", "close_dialog");
    my $response = $colourxml->get_widget('dialogColour')->run();
}

sub open_dialogFont {
    my ($widget, $event, $font) = @_;
    debug("Opening Font dialog");
    $widgets->{'font'} =
      Gtk2::GladeXML->new($globals->{'gladefile'}, 'dialogFont', 'lyricue');
    $widgets->{'font'}->signal_autoconnect_from_package('');
    my $fontname = $config->{$font};
    $widgets->{'font'}->get_widget('selectFont')->set_font_name($fontname);
    $widgets->{'font'}->get_widget('buttonFontOK')
      ->signal_connect("clicked", "apply_font", $font);
    $widgets->{'font'}->get_widget('dialogFont')->show;
    return TRUE;
}

#****m* lyricue/change_font_colour
# NAME
#   change_font_colour --
# SYNOPSIS
#   change_font_colour ($widget, $colourxml)
# FUNCTION
#   Change the font colour
# INPUTS
#   $widget - Calling widget
#   $colourxml - The Colour dialog
# OUTPUT
#   Updated config file and server
# SOURCE
#
sub change_font_colour {
    my ($widget, $colourxml) = @_;
    debug("Change font colour");
    my $item   = $colourxml->get_widget('dialogColour')->{'user_data'};
    my $colour = $colourxml->get_widget('colourSelect')->get_current_color();
    my $color2 =
        sprintf("#%2.2x", ($colour->red / 256))
      . sprintf("%2.2x", ($colour->green / 256))
      . sprintf("%2.2x", ($colour->blue / 256));

    debug("change_font_colour: item=" . $item);
    if ($item eq "Shadow") {
        $widgets->{'prefs'}->get_widget('entryPrefShadowColour')
          ->set_text($color2);
        $widgets->{'prefs'}->get_widget('entryPrefShadowColour')
          ->set_text($color2);
    } elsif ($item eq "Font") {
        $widgets->{'prefs'}->get_widget('entryPrefColour')->set_text($color2);
    } elsif ($item eq "ImageShadow") {
        $widgets->{'image'}->get_widget('entryImageShadowColour')
          ->set_text($color2);
    } elsif ($item eq "ImageFont") {
        $widgets->{'image'}->get_widget('entryImageFontColour')
          ->modify_bg('active', Gtk2::Gdk::Color->parse($color2));
        $widgets->{'image'}->get_widget('entryImageFontColour')
          ->set_text($color2);
        $widgets->{'image'}->get_widget('colorbuttonFontColour')
          ->set_color(Gtk2::Gdk::Color->parse($color2));
    }
    close_dialog($widget);
}

#***

#****m* lyricue/apply_font
# NAME
#   apply_font --
# SYNOPSIS
#   apply_font ()
# FUNCTION
#   Apply font changes to the config file and server
# INPUTS
# OUTPUT
#   Updated file and server
# SOURCE
#
sub apply_font {
    my ($widget, $type) = @_;
    debug("Applying font: "
          . $widgets->{'font'}->get_widget('selectFont')->get_font_name);
    $widgets->{'prefs'}->get_widget('entryPref' . $type)
      ->set_text($widgets->{'font'}->get_widget('selectFont')->get_font_name);
    close_dialog($widget);
}

#***

#****m* lyricue/create_dialogChapter
# NAME
#   create_dialogChapter --
# SYNOPSIS
#   create_dialogChapter ()
# FUNCTION
#   Create a dialog to choose chapter for verse
# INPUTS
#   $widget - Calling widget
# OUTPUT
#   Dialog of available chapters
# SOURCE
#
sub create_dialogChapter {
    my ($widget) = @_;
    my $book = $widget->get_label;
    debug("Selected " . $book);
    my $cont    = FALSE;
    my $maxchap = 0;
    if ($globals->{'usesword'}) {

        # Find proper book name
        my $command = sprintf(
            "%s -b %s -e UTF8 -k '%s' 1:1",
            $globals->{'diatheke'},
            $globals->{'bibledb'}, $book
        );
        qdebug($command);
        $maxchap = Encode::decode("utf-8", `$command`);
        ($book, undef) = split(/\s\d/, $maxchap, 2);

        $command = sprintf(
            "%s -b %s -e UTF8 -k '%s' | grep '^%s'| tail -2 | head -1",
            $globals->{'diatheke'},
            $globals->{'bibledb'}, $book, $book
        );
        qdebug($command);
        $maxchap = Encode::decode("utf-8", `$command`);
        $maxchap =~ s/^$book ([0-9]*):[0-9].*$/$1/g;
        if ($maxchap) {
            $cont = TRUE;
        }
    } else {
        my $query =
          "SELECT MAX(chapternum) FROM verse WHERE book like \""
          . Encode::encode("iso-8859-1", $book) . "%\"";
        qdebug($query);
        $sth = $bibleDbh->prepare($query)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);
        if (my @row = $sth->fetchrow_array) {
            $cont    = TRUE;
            $maxchap = $row[0];
        }
        $sth->finish;
    }

    if ($cont) {
        close_dialog($widget);
        my $chapxml = Gtk2::GladeXML->new($globals->{'gladefile'},
            'windowChapter', 'lyricue');
        $chapxml->signal_autoconnect_from_package('');

        $chapxml->get_widget('tableChapter')->resize(ceil($maxchap / 8) + 1, 5);
        $chapxml->get_widget('labelChapter')->set_text($book);

        for (my $i = 0 ; $i < ceil($maxchap / 8) ; $i++) {
            foreach (0 .. 7) {
                debug($i . "|" . $_);
                if ((($i * 8) + $_ + 1) <= $maxchap) {
                    my $button = Gtk2::Button->new(($i * 8) + $_ + 1);
                    $button->show;
                    $button->signal_connect("clicked", "create_dialogVerse",
                        $book);
                    $chapxml->get_widget('tableChapter')->attach(
                        $button,  $_, $_ + 1, $i + 1, $i + 2, 'fill',
                        'expand', 0,  0
                    );
                }
            }
        }
        $chapxml->get_widget('tableChapter')->show;
        $chapxml->get_widget('windowChapter')->show;
    }
}

#***

#****m* lyricue/create_dialogVerse
# NAME
#   create_dialogVerse --
# SYNOPSIS
#   create_dialogVerse ($widget, $book)
# FUNCTION
#   Create a dialog to choose verse numbers
# INPUTS
#   $widget - Calling widget
#   $book - Book name
# OUTPUT
#   Dialog of available verses
# SOURCE
#
sub create_dialogVerse {
    my ($widget, $book) = @_;
    debug("Opening Verse dialog");
    my $chapter  = int($widget->get_label);
    my $maxverse = 0;
    if ($globals->{'usesword'}) {
        my $command = sprintf(
            "%s -b %s -e UTF8 -k '%s' %d | grep '^%s' | tail -2 | head -1",
            $globals->{'diatheke'},
            $globals->{'bibledb'}, $book, $chapter, $book
        );
        qdebug($command);
        $maxverse = Encode::decode("utf-8", `$command`);
        $maxverse =~ s/^$book [0-9]*:([0-9]*):.*$/$1/g;
    } else {
        my $query =
            "SELECT MAX(versenum) FROM verse WHERE book=\""
          . Encode::encode("iso-8859-1", $book)
          . "\" AND chapternum="
          . $chapter;
        qdebug($query);
        $sth = $bibleDbh->prepare($query)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);

        if (my @row = $sth->fetchrow_array) {
            $maxverse = $row[0];
        }
    }

    if ($maxverse) {
        close_dialog($widget);
        my $versexml =
          Gtk2::GladeXML->new($globals->{'gladefile'}, 'windowVerse',
            'lyricue');
        $versexml->signal_autoconnect_from_package('');
        $versexml->get_widget('tableVerse')->resize(ceil($maxverse / 5) + 1, 5);
        $versexml->get_widget('labelVerse')->set_text($book . " - " . $chapter);
        $versexml->get_widget('windowVerse')->get_toplevel->{user_data} =
          $book . " - " . $chapter;

        for (my $i = 0 ; $i < ceil($maxverse / 5) ; $i++) {
            foreach (0 .. 4) {
                my $number = ($i * 5) + $_ + 1;
                debug($i . "|" . $_ . "|" . $number);
                if ($number <= $maxverse) {
                    $widgets->{'verseButton'}{$number} =
                      Gtk2::ToggleButton->new($number);
                    $widgets->{'verseButton'}{$number}->show;
                    $versexml->get_widget('tableVerse')
                      ->attach($widgets->{'verseButton'}{$number},
                        $_, $_ + 1, $i + 1, $i + 2, 'fill', 'expand', 0, 0);
                    $widgets->{'verseButton'}{$number}
                      ->signal_connect("event", "changeVerseStatus", $maxverse);
                }
            }
        }
        $globals->{'verseStart'} = 0;
        $globals->{'verseEnd'}   = 0;
        $versexml->get_widget('tableVerse')->show;
        $versexml->get_widget('windowVerse')->show;
    }
    $sth->finish;
}

#***

#****m* lyricue/changeVerseStatus
# NAME
#   changeVerseStatus --
# SYNOPSIS
#   changeVerseStatus ($widget, $section, $row, $col, $rows, $max)
# FUNCTION
#   Make sure you can't choose multiple values for start or finish
# INPUTS
#   $widget - Calling widget
#   $section - start or finish section
#   $row - Chosen row
#   $col - Chosen column
#   $rows - Available rows
#   $max - Maximum value
# OUTPUT
#   Only one value chosen in start or finish area
# SOURCE
#
sub changeVerseStatus {
    my ($widget, $event, $max) = @_;
    debug("Change verse status");

    my $number = $widget->get_label;
    if (defined($event->type)) {
        if ($event->type eq 'button-press') {
            if ($globals->{'verseStart'} == 0) {
                debug("Start " . $globals->{'verseStart'});
                $globals->{'verseStart'} = $number;
                $globals->{'verseEnd'}   = 0;
                my $num;
                foreach $num (1 .. $max) {
                    if ($num == $globals->{'verseStart'}) {
                        $widgets->{'verseButton'}{$num}->set_active(TRUE);
                    } else {
                        $widgets->{'verseButton'}{$num}->set_active(FALSE);
                    }
                }
            } else {
                if ($number < $globals->{'verseStart'}) {
                    $globals->{'verseEnd'} = $globals->{'verseStart'};
                    $globals->{'verses'}   = $number;
                } else {
                    $globals->{'verseEnd'} = $number;
                    $globals->{'verses'}   = $globals->{'verseStart'};
                }
                debug("End " . $globals->{'verseEnd'});
                my $num;
                foreach $num (1 .. $max) {
                    if (   ($num >= $globals->{'verses'})
                        && ($num <= $globals->{'verseEnd'}))
                    {
                        $widgets->{'verseButton'}{$num}->set_active(TRUE);
                    } else {
                        $widgets->{'verseButton'}{$num}->set_active(FALSE);
                    }
                }
                debug(
                    $globals->{'verses'} . "-" . $globals->{'verseEnd'} . "\n");
                $globals->{'verseStart'} = 0;
            }
            return TRUE;
        } elsif ($event->type eq 'enter-notify') {
            if ($globals->{'verseStart'} != 0) {
                my $min = 0;
                if ($number < $globals->{'verseStart'}) {
                    $min    = $number;
                    $number = $globals->{'verseStart'};
                } else {
                    $min = $globals->{'verseStart'};
                }
                my $num;
                foreach $num (1 .. $max) {
                    if (($num >= $min) && ($num <= $number)) {
                        $widgets->{'verseButton'}{$num}->set_active(TRUE);
                    } else {
                        $widgets->{'verseButton'}{$num}->set_active(FALSE);
                    }
                }
            }
            return TRUE;
        }
    }
    return FALSE;
}

#***

#****m* lyricue/change_preview
# NAME
#   change_preview --
# SYNOPSIS
#   change_preview ($widget)
# FUNCTION
#   Change the preview in the image dialog
# INPUTS
#   $widget - Calling widget
# OUTPUT
#   Change previews
# SOURCe
#
sub change_preview {
    debug("Changing Preview");
    my $selection = $widgets->{'image'}->get_widget('treeImage')->get_selection;
    my @selecteditems = $selection->get_selected_rows();
    if ($selecteditems[0]) {

        # Find what has changed
        my $newsel = $selecteditems[0];
        my $iter   =
          $widgets->{'image'}->get_widget('treeImage')
          ->get_model->get_iter($newsel);
        my $id =
          $widgets->{'image'}->get_widget('treeImage')
          ->get_model->get($iter, 1);
        my $tmp = 0;
        foreach $tmp (@selecteditems) {
            my $iter2 =
              $widgets->{'image'}->get_widget('treeImage')
              ->get_model->get_iter($tmp);
            my $id2 =
              $widgets->{'image'}->get_widget('treeImage')
              ->get_model->get($iter2, 1);
            if (!defined $selectedimages{$id2}) {
                $id = $id2;
            }
        }
        %selectedimages = ();
        foreach $tmp (@selecteditems) {
            my $iter2 =
              $widgets->{'image'}->get_widget('treeImage')
              ->get_model->get_iter($tmp);
            my $id2 =
              $widgets->{'image'}->get_widget('treeImage')
              ->get_model->get($iter2, 1);
            $selectedimages{$id2} = TRUE;
        }

        if ($globals->{'bg_previews'}) {
            debug("sub change_preview: create_pixbuf");
            my $scaled = create_pixbuf($id, 480, 360);
            if ($scaled) {
                my $query =
                  "SELECT textcolour, shadowcolour FROM media WHERE id=\"" . $id
                  . "\"";
                qdebug($query);
                my $sth = $mediaDbh->prepare($query)
                  || display_fatal($errorcodes->{'sqlprepare'}, $!);
                my $rv = $sth->execute
                  || display_fatal($errorcodes->{'sqlexecute'}, $!);
                my $row = $sth->fetchrow_hashref();
                if ($row->{'textcolour'} ne "NULL") {
                    $widgets->{'image'}->get_widget('entryImageFontColour')
                      ->set_text($row->{'textcolour'});
                    $widgets->{'image'}->get_widget('colorbuttonFontColour')
                      ->set_color(
                        Gtk2::Gdk::Color->parse($row->{'textcolour'}));
                } else {
                    $widgets->{'image'}->get_widget('entryImageFontColour')
                      ->set_text("Default");
                    $widgets->{'image'}->get_widget('colorbuttonFontColour')
                      ->set_color(Gtk2::Gdk::Color->parse($config->{'Colour'}));
                }
                if ($row->{'shadowcolour'} ne "NULL") {
                    $widgets->{'image'}->get_widget('entryImageShadowColour')
                      ->set_text($row->{'shadowcolour'});
                    $widgets->{'image'}->get_widget('colorbuttonShadowColour')
                      ->set_color(
                        Gtk2::Gdk::Color->parse($row->{'shadowcolour'}));
                } else {
                    $widgets->{'image'}->get_widget('entryImageShadowColour')
                      ->set_text("Default");
                    $widgets->{'image'}->get_widget('colorbuttonShadowColour')
                      ->set_color(
                        Gtk2::Gdk::Color->parse($config->{'ShadowColour'}));
                }
                $widgets->{'image'}->get_widget('imageImage')
                  ->set_from_pixbuf($scaled);
                $widgets->{'image'}->get_widget('imageImage')->{user_data} =
                  $id;
            }
        }
    }
    return TRUE;
}

#***

#****m* lyricue/set_default_backdrop
# NAME
#   set_default_backdrop --
# SYNOPSIS
#   set_default_backdrop ($widget)
# FUNCTION
#   Change the default background in the config file
# INPUTS
#   $widget - Calling widget
# OUTPUT
#   Updated config file
# SOURCE
#
sub set_default_backdrop {
    my ($widget) = @_;
    debug("Set default backdrop");
    my $image = $widgets->{'image'}->get_widget('imageImage')->{user_data};
    if ($image) {
        my $scaled = create_pixbuf($image, 100, 75);
        if ($scaled) {
            $widgets->{'prefs'}->get_widget('imagePrefBG')
              ->set_from_pixbuf($scaled);
            $widgets->{'prefs'}->get_widget('imagePrefBG')->{user_data} =
              $image;
        }
    }
    close_dialog($widget);
}

#***

#****m* lyricue/print_songs
# NAME
#   print_songs --
# SYNOPSIS
#   print_songs ()
# FUNCTION
#   Print a html formatted list of available songs
# INPUTS
#   None
# OUTPUT
#   HTML list of songs to standard out
# SOURCE
#
sub print_songs {
    debug("Print html formatted song list");
    my $lyricDbh =
      db_connect($globals->{'lyricdb'}, $errorcodes->{'lyricdbopen'});
    my $query = "SELECT COUNT(id) FROM lyricMain WHERE id > 0;";
    my $sth   = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    my $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);
    my @totals = $sth->fetchrow_array();

    $query =
"SELECT title,book,songnum,artist FROM lyricMain WHERE id > 0 ORDER BY title;";
    $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);

    print "<HTML>\n<HEAD><TITLE>Lyricue Song List</TITLE></HEAD>\n";
    print "<CENTER><H1>Lyricue Song List</H1></CENTER><BR>\n";
    print "<H2>Total number of songs available : <B>"
      . $totals[0]
      . "</B></H2>\n";
    print "<TABLE WIDTH=100% BORDER=1>\n";
    print
"<TR><TH>Song Name</TH><TH>Song Book</TH><TH>Song Number</TH><TH>Artist</TH></TR>\n";
    my @row;

    while (@row = $sth->fetchrow_array()) {
        print "<TR>";
        foreach (@row) {
            if ($_ eq "") {
                $_ = "&nbsp;";
            }
            print "<TD>" . $_ . "</TD>";
        }
        print "</TR>\n";
    }
    print "</TABLE>\n</BODY>\n</HTML>\n";
    $lyricDbh->disconnect;
    exit;
}

#***

#****m* lyricue/backdrop_clicked
# NAME
#   backdrop_clicked --
# SYNOPSIS
#   backdrop_clicked ($widget)
# FUNCTION
#   A background has been clicked son change the bg in the server and update the two previews on the main window
# INPUTS
#   $widget - Calling widget
# OUTPUT
#   Updated bg
# SOURCE
#
sub backdrop_clicked {
    my ($widget) = @_;
    debug("Backdrop clicked");
    if (@ASSOCIATE) {
        associate_bg($widget->{user_data});
    } else {
        debug("Changing backdrop to " . $widget->{user_data});
        if ($globals->{'bg_previews'}) {
            my $prevFile = $widget->{user_data};
            my $scaled   =
              create_pixbuf($widgets->{'buttonCurr'}->{user_data}, 72, 54);

            if ($scaled) {
                $widgets->{'pixmapPrev'}->set_from_pixbuf($scaled);
                $widgets->{'buttonPrev'}->{user_data} =
                  $widgets->{'buttonCurr'}->{user_data};
            }

            $scaled = create_pixbuf($prevFile, 72, 54);
            if ($scaled) {
                $widgets->{'pixmapCurr'}->set_from_pixbuf($scaled);
                $widgets->{'buttonCurr'}->{user_data} = $prevFile;
            }
            update_display("backdrop", $prevFile, "");
            preview_display("backdrop", $prevFile, "");
        }
    }
}

#***

#****m* lyricue/do_add_image
# NAME
#   do_add_image --
# SYNOPSIS
#   do_add_image ()
# FUNCTION
#   Add an image to the playlist
# INPUTS
# OUTPUT
#   Image added to playlist
# SOURCE
#
sub do_add_image {
    my ($widget) = @_;
    debug("do add image");
    my $playorder = 1;
    my $selection = $widgets->{'image'}->get_widget('treeImage')->get_selection;
    my @list      = $selection->get_selected_rows();
    my $model     = $widgets->{'image'}->get_widget('treeImage')->get_model();
    my $playlist  =
      $widgets->{'image'}->get_widget('optionImageSublist')
      ->get_menu->get_active->{'user_data'};

    $query = "SELECT MAX(playorder) FROM playlist";
    qdebug($query);
    $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);

    if ($row = $sth->fetchrow_hashref()) {
        if ($row->{'MAX(playorder)'}) {
            $playorder = $row->{'MAX(playorder)'} + 1;
        }
    }

    foreach (@list) {
        my $image = $model->get($model->get_iter($_), 1);
        debug($image);
        $query =
            "INSERT INTO playlist (playorder, playlist, type, data) VALUES ("
          . $playorder . ", "
          . $playlist
          . ", \"imag\", \""
          . $image . "\")";
        qdebug($query);
        $sth = $lyricDbh->prepare($query)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);
        $playorder++;
    }

    close_dialog($widget);
    update_playlist();
}

#***

sub show_about {
    debug("Showing About dialog");
    my $xml =
      Gtk2::GladeXML->new($globals->{'gladefile'}, 'windowAbout', 'lyricue');
    $xml->signal_autoconnect_from_package('');
    $xml->get_widget('windowAbout')->show();
}

#***

#****m* lyricue/choose_playlist
# NAME
#   choose_playlist --
# SYNOPSIS
#   choose_playlist ( )
# FUNCTION
#   Open a dialog to choose your main playlist
# INPUTS
# OUTPUT
#   Search dialog
# SOURCE
#

sub choose_playlist {
    debug("Choose playlist");
    $widgets->{'choose'} = Gtk2::GladeXML->new($globals->{'gladefile'},
        'windowChoosePlay', 'lyricue');
    $widgets->{'choose'}->signal_autoconnect_from_package('');
    $widgets->{'choose'}->get_widget('windowChoosePlay')->show;
    update_cplayclist();
}

#***

sub create_dialog_prefs {
    debug("Creating preferences dialog");
    $config = load_config();
    $widgets->{'prefs'} =
      Gtk2::GladeXML->new($globals->{'gladefile'}, 'dialogPrefs', 'lyricue');
    $widgets->{'prefs'}->signal_autoconnect_from_package('');
    $widgets->{'prefs'}->get_widget('spinPrefHeight')
      ->set_value($config->{'Height'});
    $widgets->{'prefs'}->get_widget('spinPrefWidth')
      ->set_value($config->{'Width'});
    $widgets->{'prefs'}->get_widget('spinPrefOverscanV')
      ->set_value($config->{'OverscanV'});
    $widgets->{'prefs'}->get_widget('spinPrefOverscanH')
      ->set_value($config->{'OverscanH'});
    $widgets->{'prefs'}->get_widget('checkPrefXinerama')
      ->set_active($config->{'Xinerama'});
    $widgets->{'prefs'}->get_widget('checkPrefCentreY')
      ->set_active($config->{'CentreY'});
    $widgets->{'prefs'}->get_widget('checkPrefCentreX')
      ->set_active($config->{'CentreX'});

    if (defined($config->{'ServerType'})) {
        if ($config->{'ServerType'} eq "simple") {
            $widgets->{'prefs'}->get_widget('radioPrefServerFast')
              ->set_active(TRUE);
        } elsif ($config->{'ServerType'} eq "trans") {
            $widgets->{'prefs'}->get_widget('radioPrefServerTrans')
              ->set_active(TRUE);
        } else {
            $widgets->{'prefs'}->get_widget('radioPrefServerCanvas')
              ->set_active(TRUE);
        }
    } else {
        if ($config->{'FastServer'}) {
            $widgets->{'prefs'}->get_widget('radioPrefServerFast')
              ->set_active(TRUE);
        } else {
            $widgets->{'prefs'}->get_widget('radioPrefServerCanvas')
              ->set_active(TRUE);
        }
    }
    $widgets->{'prefs'}->get_widget('entryPrefHeader')
      ->set_text($config->{'Header'});
    $widgets->{'prefs'}->get_widget('entryPrefMain')
      ->set_text($config->{'Main'});
    $widgets->{'prefs'}->get_widget('entryPrefFooter')
      ->set_text($config->{'Footer'});
    $widgets->{'prefs'}->get_widget('entryPrefColour')
      ->set_text($config->{'Colour'});
    $widgets->{'prefs'}->get_widget('entryPrefShadowColour')
      ->set_text($config->{'ShadowColour'});
    $widgets->{'prefs'}->get_widget('spinPrefShadow')
      ->set_value($config->{'ShadowSize'});
    $widgets->{'prefs'}->get_widget('checkPrefLoop')
      ->set_active($config->{'Loop'});
    $widgets->{'prefs'}->get_widget('checkPrefAudit')
      ->set_active($config->{'Audit'});
    $widgets->{'prefs'}->get_widget('checkPrefView')
      ->set_active($config->{'DynamicPreview'});
    $widgets->{'prefs'}->get_widget('checkPrefMiniview')
      ->set_active($config->{'Miniview'});
    my $scaled = create_pixbuf($config->{'BGImage'}, 72, 54);

    if ($scaled) {
        $widgets->{'prefs'}->get_widget('imagePrefBG')
          ->set_from_pixbuf($scaled);
        $widgets->{'prefs'}->get_widget('imagePrefBG')->{user_data} =
          $config->{'BGImage'};
    }

    my (@list);
    my $bibles = $config->{'Bibles'};
    foreach (sort keys %$bibles) {
        my $bible = $_ . ":" . $config->{'Bibles'}->{$_};
        push @list, $bible;
    }

    $widgets->{'prefs'}->get_widget('comboPrefBible')
      ->set_popdown_strings(@list);
    $widgets->{'prefs'}->get_widget('entryPrefBible')
      ->set_text($config->{'DefBible'});
    $widgets->{'prefs'}->get_widget('entryPrefSpecialSong')
      ->set_text($config->{'SpecialSong'});
    $widgets->{'prefs'}->get_widget('entryPrefSpecialImage')
      ->set_text($config->{'SpecialImage'});
    $widgets->{'prefs'}->get_widget('entryPrefSpecialBack')
      ->set_text($config->{'SpecialBack'});

    if ($config->{'DatabaseType'} eq "mysql") {
        $widgets->{'prefs'}->get_widget('radioPrefDBMysql')->set_active(TRUE);
    } else {
        $widgets->{'prefs'}->get_widget('radioPrefDBSqlite')->set_active(TRUE);
    }
    $widgets->{'prefs'}->get_widget('entryPrefHeader')
      ->signal_connect("button_press_event", "open_dialogFont", "Header");
    $widgets->{'prefs'}->get_widget('entryPrefMain')
      ->signal_connect("button_press_event", "open_dialogFont", "Main");
    $widgets->{'prefs'}->get_widget('entryPrefFooter')
      ->signal_connect("button_press_event", "open_dialogFont", "Footer");
    $widgets->{'prefs'}->get_widget('entryPrefColour')
      ->signal_connect("button_press_event", "open_dialogColour", "Font");
    $widgets->{'prefs'}->get_widget('entryPrefShadowColour')
      ->signal_connect("button_press_event", "open_dialogColour", "Shadow");
}

#****m* lyricue/execute_app
# NAME
#   execute_app --
# SYNOPSIS
#   execute_app ($widget, $app)
# FUNCTION
#   Run and application on the second head
# INPUTS
#   $widget - Calling widget
#   $app - Application to run
# OUTPUT
#   Application loaded on second head
# SOURCE
#
sub execute_app {
    my ($widget, $app) = @_;
    my $command = $config->{'App'}[$app];
    $command =~ s/^.*;//g;
    debug("Executing app: " . $command);
    if ($config->{'Xinerama'}) {
        system($command. " -geometry +" . $config->{'Width'} . "+0 &");
    } else {
        system("DISPLAY=:0.1 " . $command . " &");
    }
}

#***

#****m* lyricue/quote
# NAME
#   quote --
# SYNOPSIS
#   quote ()
# FUNCTION
#   Quote a string for use in db queries
# INPUTS
#   @_ - String to be quoted
# OUTPUT
#   Quoted string
# SOURCE
#
sub quote {
    return $lyricDbh->quote(@_);
}

#***

#****m* lyricue/honourise_song_lyrics
# NAME
#   honourise_song_lyrics --
# SYNOPSIS
#   honourise_song_lyrics ($widget)
# FUNCTION
#   Change lyrics to 'honourise' them by changing 'jesus' to 'Jesus' etc
# INPUTS
#   $widget - Calling widget
# OUTPUT
#   Better lyrics
# SOURCE
#
sub honourise_song_lyrics {
    my ($widget) = @_;
    my ($tmppage, $page, $numchars, $key);
    my %hash;

    debug("Start of honourising lyrics...");

    foreach $page (keys(%pageOrder)) {
        $tmppage = get_buffer_text($widgets->{'textAPageB'}{$page});
        debug(  "Page contents of page " . $page
              . " before honourisation:\n"
              . $tmppage);

        # this hash is adapted from bible parsing perl script
        # it will correct american spelling in any songs entered
        # and will perform the honourise process -
        # capitalising holy names and correcting errors like
        # "Sons of god" to "sons of God"
        %hash = (
            "honor"      => "honour",
            "color"      => "colour",
            "labor"      => "labour",
            "neighbor"   => "neighbour",
            "center"     => "centre",
            "dialog"     => "dialogue",
            "gray"       => "grey",
            "humor"      => "humour",
            "harbor"     => "harbour",
            "thru"       => "through",
            "plow"       => "plough",
            "favor"      => "favour",
            "son"        => "Son",
            "reaSon"     => "reason",
            "Sons"       => "sons",
            "god"        => "God",
            "spirit"     => "Spirit",
            "jesus"      => "Jesus",
            "lord"       => "Lord",
            "emmanuel"   => "Emmanuel",
            "immanuel"   => "Immanuel",
            "jehovah"    => "Jehovah",
            "he"         => "He",
            "him"        => "Him",
            "his"        => "His",
            "History"    => "history",
            "messiah"    => "Messiah",
            "father"     => "Father",
            "king"       => "King",
            "christ"     => "Christ",
            "great i am" => "Great I Am",
            "hosanna"    => "Hosanna",
            "yahweh"     => "Yahweh"
        );

        foreach $key (keys %hash) {
            $tmppage =~ s/^$key([ \n])/$hash{$key}$1/g;
            $tmppage =~ s/([ \n])$key$/$1$hash{$key}/g;
            $tmppage =~ s/([ \n])$key([ \n])/$1$hash{$key}$2/g;
        }
        debug(  "Page contents of page " . $page
              . " after honourisation:\n"
              . $tmppage);
        $widgets->{'textAPageB'}{$page}->set_text($tmppage);

    }

    debug("End of honourising lyrics...");
}

#***

#****m* lyricue/select_bible_db
# NAME
#   select_bible_db --
# SYNOPSIS
#   select_bible_db ($bible_database)
# FUNCTION
#   Change the bible db used by the server
# INPUTS
#   $bible_database - DB name to change it to
# OUTPUT
#   Verses displayed in chosen bible
# SOURCE
#
sub select_bible_db {
    my ($widget, $bibledb) = @_;

    if ($widget && ($widget->get_active)) {
        debug("Changing bible to $bibledb");
        my @line = split(/;/, $bibledb, 2);
        do_change_bible($line[0], $line[1]);
    }
}

sub do_change_bible {
    my ($type, $bibledb) = @_;
    debug("Change bible");
    $globals->{'bibledb'} = $bibledb;
    if (!$globals->{'usesword'}) {
        $bibleDbh->disconnect;
    }
    if ($type eq "db") {
        $globals->{'usesword'} = FALSE;
        $bibleDbh =
          db_connect($globals->{'bibledb'},
            $errorcodes->{'bibledbopen'} . $globals->{'bibledb'});
    } else {
        $globals->{'usesword'} = TRUE;
    }
    update_display("change_to_db", $bibledb, $type);
}

#***

#****m* lyricue/invert_lines
# NAME
#   invert_lines --
# SYNOPSIS
#   invert_lines ()
# FUNCTION
#   Change value so that playlist items text shows the last line of each page rather than the first
# INPUTS
# OUTPUT
#   Calls update_playlist
# SOURCE
#
sub invert_lines {
    debug("Invert line display");
    $globals->{'invert'} = !$globals->{'invert'};
    update_playlist();
}

#***

#****m* lyricue/export_song
# NAME
#   export_song --
# SYNOPSIS
#   export_song ($widget)
# FUNCTION
#   Show a dialog to choose where to save an exported song to
# INPUTS
#   $widget - Calling widget
# OUTPUT
#   File dialog displayed
# SOURCE
#
sub export_song {
    debug("Export song");
    my $filexml =
      Gtk2::GladeXML->new($globals->{'gladefile'}, 'dialogFile', 'lyricue');
    $filexml->signal_autoconnect_from_package('');
    $filexml->get_widget('buttonFileOK')
      ->signal_connect("clicked", "complete_export_song", $filexml);
}

#***

#****m* lyricue/complete_export_song
# NAME
#   complete_export_song --
# SYNOPSIS
#   complete_export_song ($widget, $filexml)
# FUNCTION
#   Export a song to the chosen filename
# INPUTS
#   $widget - Calling widget
#   $file_dialog - File dialog
# OUTPUT
#   File containing song
# SOURCE
#
sub complete_export_song {
    my ($widget, $filexml) = @_;
    my ($tmppage, $page, $numchars, $key);
    debug("Exporting song");
    my $export = $filexml->get_widget('dialogFile')->get_filename;
    close_dialog($widget);

    my $name     = $widgets->{'add'}->get_widget('entryEditName')->get_text();
    my $number   = $widgets->{'add'}->get_widget('entryEditNumber')->get_text();
    my $book     = $widgets->{'add'}->get_widget('entryEditBook')->get_text();
    my $artist   = $widgets->{'add'}->get_widget('entryEditArtist')->get_text();
    my $keywords =
      $widgets->{'add'}->get_widget('entryEditKeywords')->get_text();
    my $copyright =
      $widgets->{'add'}->get_widget('entryEditCopyright')->get_text();

    debug(" to file: " . $export);
    open(EXPORT, ">$export") || return 0;
    print(EXPORT "Name: " . $name . "\n");
    print(EXPORT "Number: " . $number . "\n");
    print(EXPORT "Book: " . $book . "\n");
    print(EXPORT "Artist: " . $artist . "\n");
    print(EXPORT "Keywords: " . $keywords . "\n");
    print(EXPORT "Copyright: " . $copyright . "\n");

    # Find maximum number of pages
    my $maxpages = 1;
    while (exists $pageOrder{$maxpages}) {
        $maxpages++;
    }

    #decrement maxpages so export works properly
    $maxpages = $maxpages - 1;

    foreach $page (sort { $pageOrder{$a} cmp $pageOrder{$b} } keys %pageOrder) {
        my $tmppage = get_buffer_text($widgets->{'textAPageB'}{$page});

        if ($maxpages > 0) {
            print(EXPORT $tmppage . "\n---\n");
        } else {
            print(EXPORT $tmppage);
        }
        $maxpages--;
    }

    close EXPORT;

}

#***

#****m* lyricue/do_adv_search
# NAME
#   do_adv_search --
# SYNOPSIS
#   do_adv_search ($widget)
# FUNCTION
#   Do an advanced search where it searchs lyrics instead of song titles
# INPUTS
#   $widget - Calling widget
# OUTPUT
#   List of suitable songs
# SOURCE
#
sub do_adv_search {
    my $search_text =
      $widgets->{'search'}->get_widget('entrySearchSongs')->get_text();
    if ($search_text ne "") {
        debug("Searching for " . $search_text);
        my $store = $widgets->{'search'}->get_widget('treeSearch')->get_model();
        if ($store) {
            $store->clear;
        } else {
            $store = Gtk2::ListStore->new(
                'Glib::String', 'Glib::String',
                'Glib::String', 'Glib::String'
            );
            $widgets->{'search'}->get_widget('treeSearch')->set_model($store);
            my $column =
              Gtk2::TreeViewColumn->new_with_attributes(
                Encode::decode("utf-8", gettext("Title")),
                Gtk2::CellRendererText->new, text => 0);
            $widgets->{'search'}->get_widget('treeSearch')
              ->append_column($column);
            $column =
              Gtk2::TreeViewColumn->new_with_attributes(
                Encode::decode("utf-8", gettext("Book")),
                Gtk2::CellRendererText->new, text => 1);
            $widgets->{'search'}->get_widget('treeSearch')
              ->append_column($column);
            $column =
              Gtk2::TreeViewColumn->new_with_attributes(
                Encode::decode("utf-8", gettext("Song No")),
                Gtk2::CellRendererText->new, text => 2);
            $widgets->{'search'}->get_widget('treeSearch')
              ->append_column($column);
        }
        my $query =
"SELECT id,title,songnum,book,count(id) as count FROM lyricMain,page WHERE lyricMain.id=page.songid AND page.lyrics LIKE \"%"
          . $search_text
          . "%\" GROUP BY id ORDER BY count DESC";
        qdebug($query);
        $sth = $lyricDbh->prepare($query)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);
        while ($row = $sth->fetchrow_hashref()) {
            my $iter = $store->append;
            $store->set(
                $iter,          0, $row->{'title'},   1,
                $row->{'book'}, 2, $row->{'songnum'}, 3,
                $row->{'id'}
            );
        }
    }
}

#***

#****m* lyricue/update_adv_search
# NAME
#   update_adv_search --
# SYNOPSIS
#   update_adv_search ($widget)
# FUNCTION
#   Update list of matching songs
# INPUTS
#   $widget - Calling widget
# OUTPUT
#   Updated dialog
# SOURCE
#
sub update_adv_search {
    my ($widget) = @_;
    my $selection =
      $widgets->{'search'}->get_widget('treeSearch')->get_selection;
    my ($model, $iter) = $selection->get_selected;
    if ($iter) {
        my $songid = $model->get($iter, 3);
        debug("Songid \"" . $songid . "\" selected");
        my $query =
            "SELECT lyrics FROM page WHERE songid=" . $songid
          . " ORDER BY pagenum";
        $sth = $lyricDbh->prepare($query)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);
        my $lyrics = "";

        while ($row = $sth->fetchrow_hashref()) {
            $lyrics .= $row->{'lyrics'} . "\n\n";
        }
        $widgets->{'search'}->get_widget('textSearch')
          ->get_buffer->set_text($lyrics);
    }
}

#***

#****m* lyricue/add_presentation
# NAME
#   add_presentation --
# SYNOPSIS
#   add_presentation ($widget)
# FUNCTION
#   Add a presentation to the playlist
# INPUTS
#   $widget - Calling widget
# OUTPUT
#   Presentation added to playlist
# SOURCE
#
sub add_presentation {
    my ($widget) = @_;
    debug("Add presentation selected");

    my $filexml =
      Gtk2::GladeXML->new($globals->{'gladefile'}, 'dialogFile', 'lyricue');
    $filexml->signal_autoconnect_from_package('');
    $filexml->get_widget('buttonFileOK')
      ->signal_connect("clicked", "present_ok", $filexml);
}

#***

#****m* lyricue/present_ok
# NAME
#   present_ok --
# SYNOPSIS
#   present_ok ($widget, $filexml)
# FUNCTION
#   Presentation selected so add it to the playlist
# INPUTS
#   $widget - Calling widget
#   $filexml - File selection dialog
# OUTPUT
#   Presentation added to playlist
# SOURCE
#
sub present_ok {
    my ($widget, $filexml) = @_;
    debug("Presentation Selected");
    debug("present_ok not done yet");
    close_dialog($widget);
}

#***

#****m* lyricue/move_item
# NAME
#   move_item
# SYNOPSIS
#   move_item ($direction)
# FUNCTION
#   Move item in the playlist up/down
# INPUTS
#   $direction - Direction to move item
# OUTPUT
#   Re-ordered playlist
# SOURCE
#
sub move_item {
    my ($direction) = @_;
    my $selection =
      $widgets->{'main'}->get_widget('treePlaylist')->get_selection;
    my ($model, $iter) = $selection->get_selected;
    if ($iter) {
        debug("Moving item in " . $direction . " direction");
        my $query = "";
        my $item  = $model->get($iter, 2);
        my $model = $widgets->{'main'}->get_widget('treePlaylist')->get_model();
        my $path  = $model->get_path($iter);
        my $pathstring  = $path->to_string();
        my $startstring = $pathstring;
        $startstring =~ s/^(.*:)(.*?)$/$1/g;
        my $endstring = $2;

        if (!defined($2)) {
            $endstring   = $startstring;
            $startstring = "";
        }
        if ($direction > 0) {
            $query =
"SELECT MIN(a.playorder) FROM playlist AS a, playlist AS b WHERE a.playlist=b.playlist AND a.playorder > "
              . $item
              . " AND b.playorder="
              . $item;
            $endstring++;
        } else {
            $query =
"SELECT MAX(a.playorder) FROM playlist AS a, playlist AS b WHERE a.playlist=b.playlist AND a.playorder < "
              . $item
              . " AND b.playorder="
              . $item;
            $endstring--;
        }
        qdebug($query);
        my $sth = $lyricDbh->prepare($query)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        my $rv = $sth->execute
          || display_fatal($errorcodes->{'sqlexecute'}, $!);
        my @row = $sth->fetchrow_array;

        if ($row[0]) {
            renumber_item($item,   -1);
            renumber_item($row[0], $item);
            renumber_item(-1,      $row[0]);
            $pathstring = $startstring . $endstring;
            my $path2      = Gtk2::TreePath->new_from_string($pathstring);
            my $path_state =
              $widgets->{'main'}->get_widget('treePlaylist')
              ->row_expanded($path);
            my $path2_state =
              $widgets->{'main'}->get_widget('treePlaylist')
              ->row_expanded($path2);
            if ($path_state) {
                $widgets->{'main'}->get_widget('treePlaylist')
                  ->expand_row($path2, FALSE);
            } else {
                $widgets->{'main'}->get_widget('treePlaylist')
                  ->collapse_row($path2);
            }
            if ($path2_state) {
                $widgets->{'main'}->get_widget('treePlaylist')
                  ->expand_row($path, FALSE);
            } else {
                $widgets->{'main'}->get_widget('treePlaylist')
                  ->collapse_row($path);
            }
            update_playlist($row[0]);
        }
    }
}

sub move_item_up {
    move_item(-1);
}

sub move_item_down {
    move_item(1);
}

#***

#****m* lyricue/renumber_item
# NAME
#   renumber_item
# SYNOPSIS
#   renumber_item ($before,$after)
# FUNCTION
#   Renumber an item in the playlist
# INPUTS
#   $before - playlist id to change from
#   $after - playlist id to change to
# OUTPUT
#   Renumbered verse in playlist/lyrics
# SOURCE
#
sub renumber_item {
    my ($before, $after) = @_;
    debug("Renumber item");
    my $query = "SELECT * FROM playlist WHERE playorder=" . $before;
    qdebug($query);
    $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    $rv  = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);
    $row = $sth->fetchrow_hashref();

    $query =
      "UPDATE playlist SET playorder=" . $after . " WHERE playorder=" . $before;
    $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);

    $query =
        "UPDATE associations SET playlist=" . $after
      . " WHERE playlist="
      . $before;
    $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);
}

#***

#****m* lyricue/add_copyright_preset
# NAME
#   add_copyright_preset
# SYNOPSIS
#   add_copyright_preset ($widget, $preset)
# FUNCTION
#   Add preset copyright text from menu to text entry
# INPUTS
#   $widget - Calling widget
#   $preset - preset number to add
# OUTPUT
#   Preset in copyright entry
# SOURCE
#
sub add_copyright_preset {
    debug("Add preset");
    my ($widget, $preset) = @_;
    $widgets->{'add'}->get_widget('entryEditCopyright')
      ->set_text("Preset:" . $preset);
}

#***

#****m* lyricue/debug
# NAME
#   debug -- output debugging text
# SYNOPSIS
#   debug ($text)
# FUNCTION
#   Print $text if $globals->{'debugging'} is set
# INPUTS
#   $text - the text to output
# OUTPUT
#   Text to STDOUT or nothing
# SOURCE
#
sub debug {
    if ($globals->{'debugging'}) {
        my $text = shift;
        chomp($text);
        if ($text) {
            my ($sec, $min, $hour, undef) = localtime(time);
            print STDERR $hour . ":" . $min . ":" . $sec . "|INTERFACE: ";
            print STDERR $text . "\n";
        }
    }
    return TRUE;
}

sub qdebug {
    if ($globals->{'debugging'} == 2) {
        debug(@_);
    }
}

#***

#****m* lyricue/select_playlist
# NAME
#   select_playlist --
# SYNOPSIS
#   select_playlist ()
# FUNCTION
#   Select the main playlist
# INPUTS
# OUTPUT
#   Closed
# SOURCE

sub select_playlist {
    my ($widget) = @_;
    debug("Selecting a main playlist");

    my $query =
      "SELECT id FROM playlists WHERE title LIKE \""
      . $widgets->{'choose'}->get_widget('entryChoosePlay')->get_text() . "\"";
    my $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    my $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);
    my @row = $sth->fetchrow_array();
    if ($row[0]) {
        $widgets->{'main'}->get_widget('labelCurrentPlaylist')
          ->set_text(
            $widgets->{'choose'}->get_widget('entryChoosePlay')->get_text);
        $widgets->{'main'}->get_widget('labelCurrentPlaylist')->{user_data} =
          $row[0];
        update_playlist();
        close_dialog($widget);
    }
}

sub select_playlist_click {
    my ($widget, $event) = @_;
    if ($event->type eq '2button-press') {
        select_playlist($widget);
    }
}

#***

#****m* lyricue/new_playlist
# NAME
#   new_playlist --
# SYNOPSIS
#   new_playlist ($widget)
# FUNCTION
#   Create a new playlist
# INPUTS
#   $widget - Calling widget
# OUTPUT
#   Closed
# SOURCE

sub new_playlist {
    my ($widget) = @_;
    debug("Creating a new playlist");
    if ($widgets->{'choose'}->get_widget('entryChoosePlay')->get_text() ne "") {
        my $query = "SELECT MAX(id)+1 FROM playlists";
        my $sth   = $lyricDbh->prepare($query)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        my $rv = $sth->execute
          || display_fatal($errorcodes->{'sqlexecute'}, $!);
        my @row = $sth->fetchrow_array();
        $query =
            "INSERT INTO playlists (id,title) VALUES ("
          . $row[0] . ", \""
          . $widgets->{'choose'}->get_widget('entryChoosePlay')->get_text()
          . "\")";
        $sth = $lyricDbh->prepare($query)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);
        select_playlist($widget);
    }
}

#***

#****m* lyricue/rename_playlist
# NAME
#   rename_playlist --
# SYNOPSIS
#   rename_playlist ($widget)
# FUNCTION
#   Rename a playlist
# INPUTS
#   $widget - Calling widget
# OUTPUT
#   Closed
# SOURCE
sub rename_playlist {
    my ($widget) = @_;
    debug("Renaming playlist");
    if ($widgets->{'choose'}->get_widget('entryChoosePlay')->get_text() ne "") {
        my $query =
            "SELECT id FROM playlists WHERE title=\""
          . $widgets->{'choose'}->get_widget('entryChoosePlay')->get_text()
          . "\"";
        my $sth = $lyricDbh->prepare($query)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        my $rv = $sth->execute
          || display_fatal($errorcodes->{'sqlexecute'}, $!);
        my @row = $sth->fetchrow_array();
        if ($row[0]) {
            my $renamexml = Gtk2::GladeXML->new($globals->{'gladefile'},
                'dialogPromptEntry', 'lyricue');
            $renamexml->signal_autoconnect_from_package('');
            my $labelText =
              Encode::decode("utf-8", gettext("Renaming "))
              . $widgets->{'choose'}->get_widget('entryChoosePlay')->get_text();
            $renamexml->get_widget('dialogPromptEntry')->set_title($labelText);
            $renamexml->get_widget('labelPromptE')->set_text($labelText);
            $renamexml->get_widget('labelPromptE')->{user_data} = $row[0];
            $renamexml->get_widget('entryPromptE')
              ->set_text(
                $widgets->{'choose'}->get_widget('entryChoosePlay')->get_text()
              );
            $renamexml->get_widget('buttonPromptEOK')
              ->signal_connect("clicked", "do_rename_playlist", $renamexml);
        }
    }
}

#***

#****m* lyricue/do_rename_playlist
# NAME
#   do_rename_playlist --
# SYNOPSIS
#   do_rename_playlist ($widget, $renamexml)
# FUNCTION
#   Rename a playlist
# INPUTS
#   $widget - Calling widget
#   $renamexml - Dialog widgets
# OUTPUT
#   Closed
# SOURCE
sub do_rename_playlist {
    my ($widget, $renamexml) = @_;
    debug("Do rename playlist");
    my $query =
        "UPDATE playlists SET title=\""
      . $renamexml->get_widget('entryPromptE')->get_text
      . "\" WHERE id="
      . $renamexml->get_widget('labelPromptE')->{user_data};
    qdebug($query);
    my $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    my $rv = $sth->execute
      || display_fatal($errorcodes->{'sqlexecute'}, $!);
    update_cplayclist();
    close_dialog($widget);
}

#***

#****m* lyricue/delete_playlist
# NAME
#   delete_playlist --
# SYNOPSIS
#   delete_playlist ($widget)
# FUNCTION
#   Delete a playlist
# INPUTS
#   $widget - Calling widget
#   $widget - Global widgets
# OUTPUT
#   Closed
# SOURCE
sub delete_playlist {
    my ($widget) = @_;
    debug("Deleting a playlist");

    my $query =
      "SELECT id FROM playlists WHERE title=\""
      . $widgets->{'choose'}->get_widget('entryChoosePlay')->get_text() . "\"";
    my $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    my $rv = $sth->execute
      || display_fatal($errorcodes->{'sqlexecute'}, $!);
    my @row = $sth->fetchrow_array();

    $query = "DELETE FROM playlists WHERE id=" . $row[0];
    $sth   = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);

    $query =
      "DELETE FROM playlist WHERE data=" . $row[0] . " AND type=\"play\"";
    $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);

    $query = "SELECT playorder FROM playlist WHERE playlist=" . $row[0];
    $sth   = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);
    while (@row = $sth->fetchrow_array()) {
        remove_single_item($row[0]);
    }

    update_cplayclist();
}

#***

#****m* lyricue/update_cplay
# NAME
#   update_cplay --
# SYNOPSIS
#   update_cplay ($selection)
# FUNCTION
#   Update the text box with the chosen playlist
# INPUTS
#   $selection - the selection
# OUTPUT
#   Closed
# SOURCE

sub update_cplay {
    my ($selection) = @_;
    debug("Selected a playlist");

    my ($model, $iter) = $selection->get_selected;
    if ($iter) {
        $widgets->{'choose'}->get_widget('entryChoosePlay')
          ->set_text($model->get($iter, 0));
    }
}

#***

#****m* lyricue/update_cplayclist
# NAME
#   update_cplayclist --
# SYNOPSIS
#   update_cplayclist ($widget)
# FUNCTION
#   Update the clist
# INPUTS
#   $widget - The clist to update
# OUTPUT
#   Closed
# SOURCE
sub update_cplayclist {
    my ($selection, $renderer, $column);
    debug("Update Choose playlist");
    my $store = $widgets->{'choose'}->get_widget('treeChoosePlay')->get_model();
    if ($store) {
        $store->clear;
    } else {
        $store = Gtk2::ListStore->new('Glib::String');
        $widgets->{'choose'}->get_widget('treeChoosePlay')->set_model($store);
        $renderer  = Gtk2::CellRendererText->new;
        $selection =
          $widgets->{'choose'}->get_widget('treeChoosePlay')->get_selection;
        $selection->signal_connect(changed => \&update_cplay);
        $column =
          Gtk2::TreeViewColumn->new_with_attributes("Playlist", $renderer,
            text => 0);
        $widgets->{'choose'}->get_widget('treeChoosePlay')
          ->append_column($column);
    }

    my $query =
"SELECT title FROM playlists LEFT JOIN playlist ON playlist.data=playlists.id AND playlist.data NOT LIKE '%-%' WHERE data IS NULL AND playlists.id > 0 ORDER BY id";
    qdebug($query);
    my $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    my $rv = $sth->execute
      || display_fatal($errorcodes->{'sqlexecute'}, $!);
    my @row;
    while (@row = $sth->fetchrow_array()) {
        my $iter = $store->append;
        $store->set($iter, 0, $row[0]);
    }
}

#***

#****m* lyricue/copy_item
# NAME
#   copy_item --
# SYNOPSIS
#   copy_item ()
# FUNCTION
#   Copy the selected item in the playlist
# INPUTS
# OUTPUT
#   Extra item in the playlist
# SOURCE
#
sub copy_item {
    my $selection =
      $widgets->{'main'}->get_widget('treePlaylist')->get_selection;
    my ($model, $iter) = $selection->get_selected;
    if ($iter) {
        debug("Duplicate item clicked ");

        my $item  = $model->get($iter, 2);
        my $query = "SELECT * FROM playlist WHERE playorder=" . $item;
        my $sth   = $lyricDbh->prepare($query)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        my $rv = $sth->execute
          || display_fatal($errorcodes->{'sqlexecute'}, $!);
        my $row = $sth->fetchrow_hashref();

        if ($row->{'type'} ne "play") {
            my $query = "SELECT MAX(playorder) FROM playlist";
            my $sth   = $lyricDbh->prepare($query)
              || display_fatal($errorcodes->{'sqlprepare'}, $!);
            my $rv = $sth->execute
              || display_fatal($errorcodes->{'sqlexecute'}, $!);
            my @row2 = $sth->fetchrow_array();
            $row2[0]++;

            $query =
                "INSERT INTO playlist (playorder,playlist,data,type) VALUES ("
              . $row2[0] . ", "
              . $row->{'playlist'} . ", \""
              . $row->{'data'}
              . "\", \""
              . $row->{'type'} . "\")";
            qdebug($query);
            $sth = $lyricDbh->prepare($query)
              || display_fatal($errorcodes->{'sqlprepare'}, $!);
            $rv = $sth->execute
              || display_fatal($errorcodes->{'sqlexecute'}, $!);
        }

        update_playlist();
    }
}

#***

#****m* lyricue/begin_loop
# NAME
#   begin_loop --
# SYNOPSIS
#   begin_loop ()
# FUNCTION
#   Initialise the automatic page looping
# INPUTS
# OUTPUT
#   Gtk timer set
# SOURCE
#
sub begin_loop {
    debug("Initialising a playlist item loop");

    my $selection =
      $widgets->{'main'}->get_widget('treePlaylist')->get_selection;
    my ($model, $iter) = $selection->get_selected;
    if ($iter) {
        my $loopxml =
          Gtk2::GladeXML->new($globals->{'gladefile'}, 'dialogLoop', 'lyricue');
        $loopxml->signal_autoconnect_from_package('');
        $loopxml->get_widget('dialogLoop')->{user_data} = $model->get($iter, 2);
        $loopxml->get_widget('buttonLoopOK')
          ->signal_connect('clicked', "establish_timer", $loopxml);
    }
}

#***

sub establish_timer {
    my ($widget, $loopxml) = @_;

    reset_timer($globals->{'timer'});

    my $seconds = $loopxml->get_widget('spinLoopSeconds')->get_value_as_int();
    my $milliseconds =
      $loopxml->get_widget('spinLoopMilliseconds')->get_value_as_int();

    debug("Seconds = " . $seconds . " ||| Milliseconds = " . $milliseconds);

    my $interval = ($seconds * 1000) + $milliseconds;
    if ($interval < 50) {
        $interval = 50;
        debug("Interval too small - defaulting to 50 milliseconds");
    }

#Find the id of the list we will be looping (could be a playlist, sublist or song)
    my $query =
      "SELECT data FROM playlist WHERE playorder ="
      . $loopxml->get_widget('dialogLoop')->{user_data};
    qdebug($query);
    my $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    my $rv = $sth->execute
      || display_fatal($errorcodes->{'sqlexecute'}, $!);
    my @row    = $sth->fetchrow_array();
    my $parent = $row[0];

    update_display("display", $loopxml->get_widget('dialogLoop')->{user_data},
        "");
    update_display("loopparent", $parent, "");

    my @data = ("display", "next_page", "loop");

    $globals->{'timer'} = Glib::Timeout->add($interval, \&update_loop, \@data);
    debug("Set loop advance timer to " . $interval . " milliseconds.");
    close_dialog($widget);
}

sub update_loop {
    my ($data) = @_;
    debug("Loop triggered");
    update_display($$data[0], $$data[1], $$data[2]);
    return TRUE;
}

#***

sub load_config {
    my ($conf);
    debug("Loading Preferences");
    my $bibleCount = 0;
    my $appCount   = 0;
    open(CONFIG, $globals->{'configfile'})
      || display_fatal($errorcodes->{'fileopenread'} . $globals->{'configfile'},
        $!);
    $conf->{'Width'}  = 0;
    $conf->{'Height'} = 0;
    while (<CONFIG>) {
        chomp;
        my @line = split(/=/);
        $line[0] =~ s/ *$//g;
        $line[1] =~ s/ *$//g;
        if (!$line[1]) {
            $line[1] = "";
        }
        $line[1] =~ s/^ *//g;
        if ($line[0] eq "App") {
            $conf->{'App'}[$appCount++] = $line[1];
        } else {
            $conf->{$line[0]} = $line[1];
        }
    }
    close CONFIG;
    $conf->{'BibleCount'} = $bibleCount;
    $conf->{'AppCount'}   = $appCount;
    $conf->{'Bibles'}     = get_bibles();
    if (!defined $conf->{'HighlightColour'}) {
        $conf->{'HighlightColour'} = "yellow";
    }
    if (defined $globals->{'force_sqlite'} && ($globals->{'force_sqlite'})) {
        $conf->{'DatabaseType'} = "SQLite";
    }
    if (!defined $conf->{'DatabaseType'}) {
        $conf->{'DatabaseType'} = "mysql";
    }
    $conf->{'LoopText'} = "";
    if (defined $conf->{'Loop'}) {
        if ($conf->{'Loop'} eq "1") {
            $conf->{'LoopText'} = "loop";
        }
    }

    return $conf;
}

#***

sub save_state {
    my $config = "";
    $config .=
      "FrameLeft = "
      . $widgets->{'main'}->get_widget('hpanedMainLeft')->get_position() . "\n";
    $config .=
        "FrameRight = "
      . $widgets->{'main'}->get_widget('hpanedMainRight')->get_position()
      . "\n";
    my ($width, $height) =
      $widgets->{'main'}->get_widget('windowMain')->get_size();
    $config .=
        "FrameMain = "
      . ($height - $widgets->{'main'}->get_widget('vpanedMain')->get_position())
      . "\n";
    return $config;
}

sub save_and_close_prefs {
    debug("Save and close preferences");
    save_preferences();
    $globals->{'configured'} = TRUE;
    close_dialog($widgets->{'prefs'}->get_widget('dialogPrefs'));
}

sub save_preferences {
    debug("Saving preferences");
    my $db_changed = FALSE;
    unlink $globals->{'configfile'} . ".bak";
    rename($globals->{'configfile'}, $globals->{'configfile'} . ".bak");
    open(CONFIG, ">$globals->{'configfile'}")
      || display_fatal($errorcodes->{'fileopenwrite'}, $!);
    print CONFIG "Main = "
      . $widgets->{'prefs'}->get_widget('entryPrefMain')->get_text() . "\n";
    print CONFIG "Header = "
      . $widgets->{'prefs'}->get_widget('entryPrefHeader')->get_text() . "\n";
    print CONFIG "Footer = "
      . $widgets->{'prefs'}->get_widget('entryPrefFooter')->get_text() . "\n";
    print CONFIG "Colour = "
      . $widgets->{'prefs'}->get_widget('entryPrefColour')->get_text() . "\n";
    print CONFIG "ShadowColour = "
      . $widgets->{'prefs'}->get_widget('entryPrefShadowColour')->get_text()
      . "\n";
    print CONFIG "ShadowSize = "
      . $widgets->{'prefs'}->get_widget('spinPrefShadow')->get_value() . "\n";
    print CONFIG "Height = "
      . $widgets->{'prefs'}->get_widget('spinPrefHeight')->get_value() . "\n";
    print CONFIG "Width = "
      . $widgets->{'prefs'}->get_widget('spinPrefWidth')->get_value() . "\n";
    print CONFIG "OverscanH = "
      . $widgets->{'prefs'}->get_widget('spinPrefOverscanH')->get_value()
      . "\n";
    print CONFIG "OverscanV = "
      . $widgets->{'prefs'}->get_widget('spinPrefOverscanV')->get_value()
      . "\n";
    print CONFIG "Loop = "
      . $widgets->{'prefs'}->get_widget('checkPrefLoop')->get_active() . "\n";
    print CONFIG "Audit = "
      . $widgets->{'prefs'}->get_widget('checkPrefAudit')->get_active() . "\n";
    print CONFIG "DynamicPreview = "
      . $widgets->{'prefs'}->get_widget('checkPrefView')->get_active() . "\n";
    print CONFIG "Miniview = "
      . $widgets->{'prefs'}->get_widget('checkPrefMiniview')->get_active()
      . "\n";
    print CONFIG "Xinerama = "
      . $widgets->{'prefs'}->get_widget('checkPrefXinerama')->get_active()
      . "\n";
    print CONFIG "CentreY = "
      . $widgets->{'prefs'}->get_widget('checkPrefCentreY')->get_active()
      . "\n";
    print CONFIG "CentreX = "
      . $widgets->{'prefs'}->get_widget('checkPrefCentreX')->get_active()
      . "\n";

    if ($widgets->{'prefs'}->get_widget('radioPrefServerTrans')->get_active()) {
        print CONFIG "ServerType = trans\n";
    } elsif (
        $widgets->{'prefs'}->get_widget('radioPrefServerFast')->get_active())
    {
        print CONFIG "ServerType = simple\n";
    } else {
        print CONFIG "ServerType = canvas\n";
    }
    print CONFIG "BGImage = "
      . $widgets->{'prefs'}->get_widget('imagePrefBG')->{user_data} . "\n";
    print CONFIG "SpecialSong = "
      . $widgets->{'prefs'}->get_widget('entryPrefSpecialSong')->get_text()
      . "\n";
    print CONFIG "SpecialImage = "
      . $widgets->{'prefs'}->get_widget('entryPrefSpecialImage')->get_text()
      . "\n";
    print CONFIG "SpecialBack = "
      . $widgets->{'prefs'}->get_widget('entryPrefSpecialBack')->get_text()
      . "\n";

    if ($widgets->{'prefs'}->get_widget('radioPrefDBMysql')->get_active()) {
        print CONFIG "DatabaseType = mysql\n";
        if ($config->{'DatabaseType'} ne "mysql") {
            $db_changed = TRUE;
        }
    } else {
        if ($config->{'DatabaseType'} ne "SQLite") {
            $db_changed = TRUE;
        }
        print CONFIG "DatabaseType = SQLite\n";
    }

    print CONFIG "DefBible = "
      . $widgets->{'prefs'}->get_widget('entryPrefBible')->get_text() . "\n";
    print CONFIG save_state();
    foreach (0 .. $config->{'AppCount'} - 1) {
        print CONFIG "App = " . $config->{'App'}[$_] . "\n";
    }
    foreach (keys(%$config)) {
        if (/^Preset/) {
            print CONFIG $_ . " = " . $config->{$_} . "\n";
        }
    }
    close CONFIG;
    $config = load_config();
    if ($db_changed) {
        db_restart();
    }
    init_preview();
    init_miniview();

    preview_display("reconfig", "", "");
    update_display("reconfig", "", "");
    preview_display("display", "current", "");
    update_display("display", "current", "");
}

#***

# Add the filename to the playlist
sub file_ok_sel {
    my ($widget, $filexml) = @_;
    debug("Add filename to playlist");
    my $file = $filexml->get_widget('dialogFile')->get_filename;
    if (($file ne "") && (-r $file)) {
        my $playorder = 1;
        my $query     = "SELECT MAX(playorder) FROM playlist";
        qdebug($query);
        $sth = $lyricDbh->prepare($query)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        $rv = $sth->execute
          || display_fatal($errorcodes->{'sqlexecute'}, $!);
        if ($row = $sth->fetchrow_hashref()) {
            if ($row->{'MAX(playorder)'}) {
                $playorder = $row->{'MAX(playorder)'} + 1;
            }
        }

        my $main_playlist =
          $widgets->{'main'}->get_widget('labelCurrentPlaylist')->{user_data};
        $query =
            "INSERT INTO playlist (playorder, playlist, type, data) VALUES ("
          . $playorder . ", "
          . $main_playlist
          . ", \"file\", \""
          . $file . "\")";
        qdebug($query);
        $sth = $lyricDbh->prepare($query)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        $rv = $sth->execute
          || display_fatal($errorcodes->{'sqlexecute'}, $!);
        update_playlist();
        close_dialog($widget);
    }
}

#***

#****m* lyricue/create_sublist
# NAME
#   create_sublist --
# SYNOPSIS
#   create_sublist ()
# FUNCTION
#   Display the 'new sublist' dialog
# INPUTS
# OUTPUT
#   Dialog
# SOURCE
#
sub create_sublist {
    my ($widget) = @_;
    debug("Create sublist");
    create_dialogSublist($widget);
}

#***

#****m* lyricue/new_sublist
# NAME
#   new_sublist --
# SYNOPSIS
#   new_sublist ()
# FUNCTION
#   Create sublist entry in database
# INPUTS
# OUTPUT
#   Closes sublist dialog
#   Adds sublist to current playlist
#   Calls update_playlist to refresh playlist
# SOURCE
#
sub new_sublist {
    my ($widget, $sublistxml) = @_;
    debug("new sublist");
    my ($row, $sth, $rv, $parentid);

    #Determine the id of the main playlist
    $parentid =
      $widgets->{'main'}->get_widget('labelCurrentPlaylist')->{user_data};

    #Find out the id for the playlists table
    my $playlistsid = 1;
    $query = "SELECT MAX(id) FROM playlists";
    $sth   = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);
    if ($row = $sth->fetchrow_hashref()) {
        if ($row->{'MAX(id)'}) {
            $playlistsid = $row->{'MAX(id)'} + 1;
        }
    }

    #Find out the playorder for the playlist table
    my $playorder = 1;
    $query = "SELECT MAX(playorder) FROM playlist";
    $sth   = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);
    if ($row = $sth->fetchrow_hashref()) {
        if ($row->{'MAX(playorder)'}) {
            $playorder = $row->{'MAX(playorder)'} + 1;
        }
    }

    #create sublist playlist
    $query =
        "INSERT INTO playlists (id, title, ref) VALUES ("
      . $playlistsid . " ,'"
      . $sublistxml->get_widget('entryPromptE')->get_text() . "','')";
    $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);

    #link sublist to main playlist
    $query =
      "INSERT INTO playlist (playorder,playlist,type,data,transition) VALUES ("
      . $playorder . ", "
      . $parentid
      . ", \"sub\","
      . $playlistsid . " ,0)";
    qdebug($query);
    $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);

    close_dialog($widget);
    update_playlist();
}

#***

#****m* lyricue/move_item_to_sublist
# NAME
#   move_item_to_sublist --
# SYNOPSIS
#   move_item_to_sublist ($item, $sublistid)
# FUNCTION
#   Move a playlist item to become a child of a sublist
# INPUTS
#   $item - playorder of the item to be moved
#   $sublistid - id of the sublist the item is to be a child of
# OUTPUT
#   Alters database entry for playlist item to make sublist its parent
#   Calls update_playlist to refresh playlist
# SOURCE
#
sub move_item_to_sublist {
    my ($widget, $sublistid) = @_;
    debug("move item to sublist");
    my $selection =
      $widgets->{'main'}->get_widget('treePlaylist')->get_selection;
    my ($model, $iter) = $selection->get_selected;
    if ($iter) {
        my $plitem = $model->get($iter, 2);
        debug("Moving item " . $plitem . " to playlist " . $sublistid);

        my $query =
            "UPDATE playlist SET playlist = "
          . $sublistid
          . " WHERE playorder = "
          . $plitem;
        qdebug($query);
        my $sth = $lyricDbh->prepare($query)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        my $rv = $sth->execute
          || display_fatal($errorcodes->{'sqlexecute'}, $!);

        update_playlist();
    }
}

#***

#****m* lyricue/find_more_children
# NAME
#   find_more_children --
# SYNOPSIS
#   find_more_children ($parentid)
# FUNCTION
#   Find all the children of a given playlist item
# INPUTS
#   $parentid - The playlist id of the 'parent'
# OUTPUT
#   Returns an array of playlist ids representing children of the parent
# SOURCE
#
sub find_more_children {
    my ($parentid) = @_;
    debug("Looking for children");
    my @kiddies;

    my $query =
        "SELECT data FROM playlist WHERE playlist = "
      . $parentid
      . " AND type = 'sub'";

    qdebug($query);
    my $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    my $rv = $sth->execute
      || display_fatal($errorcodes->{'sqlexecute'}, $!);
    while ($row = $sth->fetchrow_hashref()) {
        @kiddies = (@kiddies, $row->{'data'});
        @kiddies = (@kiddies, find_more_children($row->{'data'}));
    }

    return @kiddies;
}

#***

#****m* lyricue/associate_bg
# NAME
#   associate_bg --
# SYNOPSIS
#   associate_bg ($imagefile)
# FUNCTION
#   To associate the an background image with a playlist item
# INPUTS
#   $imagefile - file path & name of image to associate with item
# OUTPUT
#   Stores image name, playlist order pair in db 'associations' table
# SOURCE
#
sub associate_bg {
    my ($imagefile) = @_;

    debug("Associating bg " . $imagefile . " with " . $ASSOCIATE[0]);

    my $query = "DELETE FROM associations WHERE playlist=" . $ASSOCIATE[0];
    my $sth   = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);

    $query =
"INSERT INTO associations (id,playlist,imagename, absoluteparent) VALUES(0, "
      . $ASSOCIATE[0] . ", '"
      . $imagefile . "',"
      . $ASSOCIATE[1] . ")";
    $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);

    update_playlist();
    @ASSOCIATE = ();
}

#***

#****m* lyricue/prepare_for_association
# NAME
#   prepare_for_association  --
# SYNOPSIS
#   prepare_for_association  ()
# FUNCTION
#   Set up the ASSOCIATE array
# INPUTS
# OUTPUT
#   Updated ASSOCIATE array
# SOURCE
#
sub prepare_for_association {
    my ($widget) = @_;
    debug("prepare for association");
    my $selection =
      $widgets->{'main'}->get_widget('treePlaylist')->get_selection;
    my ($model, $iter) = $selection->get_selected;
    if ($iter) {

        # Change ASSOCIATE array to contain playorder of list item,
        # main playlist id
        @ASSOCIATE = (
            $model->get($iter, 2),
            $widgets->{'main'}->get_widget('labelCurrentPlaylist')->{user_data}
        );
        debug("Waiting for an image click in order to associate");
    }

}

#***

#****m* lyricue/disassociate_bg
# NAME
#   disassociate_bg --
# SYNOPSIS
#   disassociate_bg ()
# FUNCTION
#   To disssociate the current background image from a playlist item
# INPUTS
# OUTPUT
#   If an association record exists in db, it is removed.
# SOURCE
#
sub disassociate_bg {
    my ($widget) = @_;

    my $selection =
      $widgets->{'main'}->get_widget('treePlaylist')->get_selection;
    my ($model, $iter) = $selection->get_selected;
    if ($iter) {
        debug("Disassociating image from " . $model->get($iter, 2));
        my $query =
          "DELETE FROM associations WHERE playlist=" . $model->get($iter, 2);

        my $sth = $lyricDbh->prepare($query)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);

        update_playlist();
    }
}

#***

#****m* lyricue/clear_associations
# NAME
#   clear_associations --
# SYNOPSIS
#   clear_associations ()
# FUNCTION
#   Clears all image-playlist item associations linked to current playlist
# INPUTS
# OUTPUT
#   Clears the associations table in lyricDb
# SOURCE
#
sub clear_associations {
    my ($widget) = @_;
    debug("Disassociating images from all playlist items");
    my $parentid =
      $widgets->{'main'}->get_widget('labelCurrentPlaylist')->{user_data};

    my $query = "DELETE FROM associations WHERE absoluteparent=" . $parentid;
    qdebug($query);
    my $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);

    update_playlist();
}

#***

sub init_mainWindow {
    debug("Initializing main window");

    # Load bg thumbnails
    my $scaled = create_pixbuf($config->{'BGImage'}, 72, 52);
    if ($scaled) {
        my $pixmap = Gtk2::Image->new_from_pixbuf($scaled);
        $widgets->{'pixmapCurr'} = $pixmap;
        $widgets->{'buttonCurr'} = new Gtk2::Button();
        $widgets->{'buttonCurr'}->add($widgets->{'pixmapCurr'});
        $widgets->{'buttonCurr'}->{user_data} = $config->{'BGImage'};
        $widgets->{'main'}->get_widget('vboxCurrentBG')
          ->pack_start($widgets->{'buttonCurr'}, FALSE, FALSE, 0);
        $widgets->{'pixmapCurr'}->show();
        $widgets->{'buttonCurr'}->show();
        $widgets->{'buttonCurr'}->signal_connect("clicked", "backdrop_clicked");
    }
    $scaled = create_pixbuf($config->{'BGImage'}, 72, 52);
    if ($scaled) {
        my $pixmap = Gtk2::Image->new_from_pixbuf($scaled);
        $widgets->{'pixmapPrev'} = $pixmap;
        $widgets->{'buttonPrev'} = new Gtk2::Button();
        $widgets->{'buttonPrev'}->add($widgets->{'pixmapPrev'});
        $widgets->{'buttonPrev'}->{user_data} = $config->{'BGImage'};
        $widgets->{'main'}->get_widget('vboxPrevBG')
          ->pack_start($widgets->{'buttonPrev'}, FALSE, FALSE, 0);
        $widgets->{'pixmapPrev'}->show();
        $widgets->{'buttonPrev'}->show();
        $widgets->{'buttonPrev'}->signal_connect("clicked", "backdrop_clicked");
    }

    # Update list of background directories
    bgdir_list();

    # Setup Available list and blank playlist
    $widgets->{'main'}->get_widget('treeAvailable')->set_enable_search(TRUE);
    $widgets->{'main'}->get_widget('treeAvailable')->set_search_column(0);
    update_available();
    $widgets->{'main'}->get_widget('labelCurrentPlaylist')->{user_data} = 0;

    # Update the bible menu
    my $menutop = Gtk2::Menu->new();
    my $group   = -1;
    my $bibles  = $config->{'Bibles'};
    my ($defbible, undef) = split(/\:/, $config->{'DefBible'}, 2);
    foreach (sort keys %$bibles) {
        my @bible = split(/;/, $config->{'Bibles'}->{$_}, 2);

        $bibleMenu->{$_} =
          Gtk2::RadioMenuItem->new_with_label($group, $bible[1]);
        if ($group == -1) {
            $group = $bibleMenu->{$_}->get_group;
        }
        $bibleMenu->{$_}
          ->signal_connect("toggled", "select_bible_db", $bible[0] . ";" . $_);
        if (defined($defbible) && ($_ eq $defbible)) {
            $bibleMenu->{$_}->set_active(TRUE);
        }
        $bibleMenu->{$_}->show;
        $menutop->append($bibleMenu->{$_});
    }
    $widgets->{'main'}->get_widget('bible1')->set_submenu($menutop);

    # Update the application menu
    my $menutop2 = Gtk2::Menu->new();
    my $group2   = 0;
    my @appMenu  = ();
    foreach (0 .. $config->{'AppCount'} - 1) {
        my $app = $config->{'App'}[$_];
        $app =~ s/^(.*);.*$/$1/g;

        $appMenu[$_] = Gtk2::MenuItem->new_with_label($app);
        $appMenu[$_]->signal_connect("activate", "execute_app", $_);
        $appMenu[$_]->show;
        $menutop2->append($appMenu[$_]);
    }
    $widgets->{'main'}->get_widget('applications1')->set_submenu($menutop2);

    my @target_table =
      ({'target' => "STRING", 'flags' => 'same-app', 'info' => 0},);
    $widgets->{'main'}->get_widget('treeAvailable')
      ->drag_source_set(['button1_mask'], ['copy'], @target_table);
    $widgets->{'main'}->get_widget('treePlaylist')
      ->drag_dest_set('all', ['copy'], @target_table);

    # BORKED
    #$widgets->{'main'}->get_widget('treePlaylist')->set_reorderable(TRUE);

    init_preview();
    init_miniview();

    # Show the window finally
    $widgets->{'main'}->get_widget('windowMain')->show;
    if (!(defined($globals->{'run_windowed'}) && ($globals->{'run_windowed'})))
    {
        $widgets->{'main'}->get_widget('windowMain')->maximize;
    }

    do_pending();

    # Adjust frame sizes
    my ($width, $height) =
      $widgets->{'main'}->get_widget('windowMain')->get_size();
    if ((defined $config->{'FrameRight'}) && ($config->{'FrameRight'} != 0)) {
        $widgets->{'main'}->get_widget('hpanedMainLeft')
          ->set_position($config->{'FrameLeft'});
        $widgets->{'main'}->get_widget('hpanedMainRight')
          ->set_position($config->{'FrameRight'});
    } else {
        if ($globals->{'access'} =~ /p/) {
            $widgets->{'main'}->get_widget('hpanedMainLeft')
              ->set_position($width / 5 * 2);
            $widgets->{'main'}->get_widget('hpanedMainRight')
              ->set_position($width / 5 * 2);
        }
    }
    if ((defined $config->{'FrameMain'}) && ($config->{'FrameMain'} != 0)) {
        $widgets->{'main'}->get_widget('vpanedMain')
          ->set_position($height - $config->{'FrameMain'});
    } else {
        $widgets->{'main'}->get_widget('vpanedMain')
          ->set_position($height - 125);
    }

    $globals->{'hand_cursor'} = Gtk2::Gdk::Cursor->new('hand2');
    $globals->{'text_cursor'} = Gtk2::Gdk::Cursor->new('xterm');
}

sub bgdir_list {
    debug("Loading directory list");
    my $counter = 0;
    debug("Adding Main image dir button");
    my @categories = ();
    if ($config->{'SpecialBack'} ne "") {
        push @categories, $config->{'SpecialBack'};
    }
    my $query =
      "SELECT DISTINCT category FROM media WHERE type=\"bg\" AND category !=\""
      . $config->{'SpecialBack'}
      . "\" ORDER BY category";
    my $sth = $mediaDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    my $rv = $sth->execute
      || display_fatal($errorcodes->{'sqlexecute'}, $!);
    while ($row = $sth->fetchrow_hashref()) {
        push @categories, $row->{'category'};
    }

    my $cat = "";
    foreach $cat (@categories) {
        debug("Adding image dir button for " . $cat);
        $widgets->{'dirbuttons'}[$counter] = new Gtk2::Button($cat);
        $widgets->{'main'}->get_widget('vboxBackDir')
          ->add($widgets->{'dirbuttons'}[$counter]);
        $widgets->{'dirbuttons'}[$counter]
          ->signal_connect("clicked", \&bgdir_change, $cat);
        $widgets->{'dirbuttons'}[$counter]->show;
        $counter++;
    }
}

#****m* lyricue/bgdir_change
# NAME
#   bgdir_change --
# SYNOPSIS
#   bgdir_change ($directory)
# FUNCTION
#   Clears background image preview buttions
#   Calls bgdir_load to create new buttons
# INPUTS
#   $category - background image category to load images from
# OUTPUT
#   Updated background preview buttons
# SOURCE
#
sub bgdir_change {
    my ($widget, $category) = @_;
    debug(  "Changing background source category to "
          . $category
          . "\nDestroying old images");
    my $count = 0;
    if ($widgets->{'buttons'}) {
        while (defined $widgets->{'buttons'}[$count]
            && $widgets->{'buttons'}[$count] != NULL)
        {
            $widgets->{'buttons'}[$count]->destroy();
            $widgets->{'pixmaps'}[$count]->destroy();
            $count++;
        }
    }
    bgdir_load($category);
    debug("Background category change complete");
}

#***

#****m* lyricue/bgdir_load
# NAME
#   bgdir_load --
# SYNOPSIS
#   bgdir_load ($category)
# FUNCTION
#   Create new preview pixmaps and buttons
# INPUTS
#   $category - background image category to load images from
# OUTPUT
#   Updated background preview buttons
# SOURCE
#
sub bgdir_load {
    my ($category) = @_;

    # update list of available backdrops
    debug("Loading new preview images from: " . $category);

    # Disable dir buttons
    $widgets->{'main'}->get_widget('vboxBackDir')->set_sensitive(FALSE);
    $widgets->{'main'}->get_widget('hboxBackImage')->hide();

    my $query =
        "SELECT id FROM media WHERE category=\""
      . $category
      . "\" AND type=\"bg\" ORDER BY description";
    my $sth = $mediaDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    my $rv = $sth->execute
      || display_fatal($errorcodes->{'sqlexecute'}, $!);
    my $counter = 0;
    while ($row = $sth->fetchrow_hashref()) {

        do_pending();
        my $scaled = create_pixbuf($row->{'id'}, 64, 48);
        if ($scaled) {
            my $pixmap = Gtk2::Image->new_from_pixbuf($scaled);

            $widgets->{'pixmaps'}[$counter] = $pixmap;
            $widgets->{'buttons'}[$counter] = new Gtk2::Button();
            $widgets->{'buttons'}[$counter]
              ->add($widgets->{'pixmaps'}[$counter]);
            $widgets->{'buttons'}[$counter]->{user_data} = ($row->{'id'});
            $widgets->{'pixmaps'}[$counter]->show();
            $widgets->{'buttons'}[$counter]->show();
            $widgets->{'buttons'}[$counter]
              ->signal_connect("clicked", "backdrop_clicked", \$widgets);
            $widgets->{'main'}->get_widget('hboxBackImage')
              ->pack_start($widgets->{'buttons'}[$counter], FALSE, FALSE, 0);

            $counter++;
        }
    }
    $widgets->{'main'}->get_widget('hboxBackImage')->show();

    # Enable dir buttons
    $widgets->{'main'}->get_widget('vboxBackDir')->set_sensitive(TRUE);

}

#***

#****m* lyricue/spell_check
# NAME
#   spell_check --
# SYNOPSIS
#   spell_check ()
# FUNCTION
#   Use aspell to check spellling for a song
# INPUTS
#   $oldwidgets
# OUTPUT
#   Dialog listing spelling errors on each page
# SOURCE
#
sub spell_check {
    my ($widget) = @_;
    my ($page);
    if ($globals->{'spell'}) {
        debug("Running spell checker");

        foreach $page (
            sort { $pageOrder{$a} cmp $pageOrder{$b} }
            keys %pageOrder
          )
        {
            if (!defined $widgets->{'spellAPage'}{$page}) {
                $widgets->{'spellAPage'}{$page} =
                  Gtk2::Spell->new($widgets->{'textAPage'}{$page});
            }

        }
    }
}

#***

#****m* lyricue/create_search
# NAME
#   create_search --
# SYNOPSIS
#   create_search ($topwidgets)
# FUNCTION
#   Create the dialog for advanced search
# INPUTS
#   $topwidgets - Widgets of main window
# OUTPUT
#   Search dialog
# SOURCE
#
sub create_search {
    debug("Advanced search dialog opened");
    $widgets->{'search'} =
      Gtk2::GladeXML->new($globals->{'gladefile'}, 'dialogSearch', 'lyricue');
    $widgets->{'search'}->signal_autoconnect_from_package('');
    my @target_table = ({'target' => "STRING", 'flags' => [], 'info' => 0},);
    $widgets->{'search'}->get_widget('treeSearch')
      ->drag_source_set(['button1_mask'], ['copy'], @target_table);
    $widgets->{'main'}->get_widget('treePlaylist')
      ->drag_dest_set('all', ['copy'], @target_table);
    my $search_text = $widgets->{'main'}->get_widget('entrySearch')->get_text();
    $widgets->{'search'}->get_widget('entrySearchSongs')
      ->set_text($search_text);
    do_adv_search();
}

#***

sub add_file {
    debug("Open a file dialog");
    my $filexml =
      Gtk2::GladeXML->new($globals->{'gladefile'}, 'dialogFile', 'lyricue');
    $filexml->signal_autoconnect_from_package('');
    $filexml->get_widget('buttonFileOK')
      ->signal_connect("clicked", "file_ok_sel", $filexml);
}

sub add_background {
    debug("add background");
    my ($widget) = @_;
    change_bgimage($widget);
}

sub change_bgimage {
    my ($widget) = @_;
    my $bgimage = "";
    debug("Change backdrop dialog opened");
    $widgets->{'image'} =
      Gtk2::GladeXML->new($globals->{'gladefile'}, 'dialogImage', 'lyricue');
    $widgets->{'image'}->signal_autoconnect_from_package('');
    $widgets->{'image'}->get_widget('dialogImage')
      ->set_title(Encode::decode("utf-8", gettext("Choose a Background")));
    $widgets->{'image'}->get_widget('buttonImageAdd')
      ->signal_connect('clicked', 'import_background');
    $widgets->{'image'}->get_widget('hboxImageSublist')->hide;
    $widgets->{'image'}->get_widget('dialogImage')->show;

    if ($widgets->{'image'}->get_widget('treeImage')->{user_data}
        && ($widgets->{'image'}->get_widget('treeImage')->{user_data} eq "load")
      )
    {
        $widgets->{'image'}->get_widget('treeImage')->{data} = ();
    } else {
        if ($widget->get_name() eq "buttonPrefBackground") {
            $bgimage =
              $widgets->{'prefs'}->get_widget('imagePrefBG')->{user_data};
            $widgets->{'image'}->get_widget('buttonImageOK')
              ->set_label(gettext("Set as Default"));
            $widgets->{'image'}->get_widget('buttonImageOK')
              ->signal_connect('clicked', "set_default_backdrop");
            $widgets->{'image'}->get_widget('buttonImageAdd')->hide();
            $widgets->{'image'}->get_widget('buttonImageDelete')->hide();
            $widgets->{'image'}->get_widget('buttonImageChange')->hide();
        } else {
            $widgets->{'image'}->get_widget('buttonImageCancel')->hide();
            $widgets->{'image'}->get_widget('buttonImageOK')
              ->signal_connect('clicked', "close_dialog");
            $widgets->{'image'}->get_widget('hboxImageColour')->show();
            $widgets->{'image'}->get_widget('entryImageFontColour')
              ->signal_connect("button_press_event", "open_dialogColour",
                "ImageFont");
            $widgets->{'image'}->get_widget('colorbuttonFontColour')
              ->signal_connect("button_press_event", "open_dialogColour",
                "ImageFont");
            $widgets->{'image'}->get_widget('entryImageFontColour')
              ->signal_connect("changed", "change_colour_media");
            $widgets->{'image'}->get_widget('entryImageShadowColour')
              ->signal_connect("button_press_event", "open_dialogColour",
                "ImageShadow");
            $widgets->{'image'}->get_widget('colorbuttonShadowColour')
              ->signal_connect("button_press_event", "open_dialogColour",
                "ImageShadow");
            $widgets->{'image'}->get_widget('entryImageShadowColour')
              ->signal_connect("changed", "change_colour_media");
        }
        $widgets->{'image'}->get_widget('treeImage')->{user_data} = "load";
        my $renderer = Gtk2::CellRendererText->new;
        $renderer->set("editable", TRUE);
        $renderer->signal_connect("edited", "rename_media");
        my $column =
          Gtk2::TreeViewColumn->new_with_attributes("Filename", $renderer,
            text => 0);
        $widgets->{'image'}->get_widget('treeImage')->append_column($column);
    }
    update_imagedir("bg", "");
    return TRUE;
}

#****m* lyricue/create_dialogSublist
# NAME
#   create_dialogSublist --
# SYNOPSIS
#   create_dialogSublist ($widgets)
# FUNCTION
#   Create the dialog for adding a sublist
# INPUTS
#   $widgets - Widgets of main window
# OUTPUT
#   Dialog
# SOURCE
#
sub create_dialogSublist {
    debug("create sublist dialog");
    my $sublistxml = Gtk2::GladeXML->new($globals->{'gladefile'},
        'dialogPromptEntry', 'lyricue');
    $sublistxml->signal_autoconnect_from_package('');
    $sublistxml->get_widget('dialogPromptEntry')
      ->set_title(Encode::decode("utf-8", gettext("Create new sublist")));
    $sublistxml->get_widget('labelPromptE')->set_text("Name of sublist");
    $sublistxml->get_widget('buttonPromptEOK')
      ->signal_connect("clicked", "new_sublist", $sublistxml);
}

#***

sub get_buffer_text {
    my ($widget) = @_;
    return $widget->get_text($widget->get_bounds, FALSE);
}

sub on_treeAvailable_drag_data_get {
    my ($widget, $context, $data, $info, $time) = @_;
    debug("Dragged from available songs");
    my $selection = $widget->get_selection;
    my ($model, $iter) = $selection->get_selected;
    if ($iter) {
        $data->set($data->target, 8, $model->get($iter, 3));
    }
}

sub on_treeSearch_drag_data_get {
    my ($widget, $context, $data, $info, $time) = @_;
    debug("Dragged from advanced search");
    my $selection = $widget->get_selection;
    my ($model, $iter) = $selection->get_selected;
    if ($iter) {
        $data->set($data->target, 8, $model->get($iter, 3));
    }
}

sub on_treePlaylist_drag_data_received {
    my ($widget, $context, $x, $y, $data, $info, $time) = @_;
    debug("Dropped on playlist");

    if (($data->length >= 0) && ($data->format == 8)) {
        debug("Recieved " . $data->data);
        add_single_song($data->data);
        update_playlist();
        $context->finish(1, 0, $time);
        return;
    }
    $context->finish(0, 0, $time);
}

sub display_fatal {
    my ($message, $error) = @_;
    print STDERR "\n\n-------------\n";
    print STDERR "FATAL ERROR!!\n";
    print STDERR "-------------\n";
    print STDERR "Error description\n";
    print STDERR $message . "\n";
    print STDERR "------------------\n";
    my $errorxml =
      Gtk2::GladeXML->new($globals->{'gladefile'}, 'dialogError', 'lyricue');
    $errorxml->signal_autoconnect_from_package('');
    $errorxml->get_widget('labelError')->set_text($message);
    my $confirm = $errorxml->get_widget('dialogError')->run();
    close_dialog($errorxml->get_widget('dialogError'));
    print STDERR "Full error message\n";
    print STDERR "------------------\n";
    die($error);
}

sub select_transitions {
    debug("Selecting transitions");
    $widgets->{'transitions'} = Gtk2::GladeXML->new($globals->{'gladefile'},
        'windowTransitions', 'lyricue');
    $widgets->{'transitions'}->signal_autoconnect_from_package('');
    $widgets->{'transitions'}->get_widget('windowTransitions')->show();
}

sub apply_transition {
    debug("Applying transition");
    my $selection =
      $widgets->{'main'}->get_widget('treePlaylist')->get_selection;
    my ($model, $iter) = $selection->get_selected;
    if ($iter) {
        my $trans_type = NORMAL;
        if ($widgets->{'transitions'}->get_widget('radioTransWipe')
            ->get_active())
        {
            $trans_type = WIPE;
        } elsif ($widgets->{'transitions'}->get_widget('radioTransSlide')
            ->get_active())
        {
            $trans_type = SLIDE_TEXT;
        }

        # Set new direction
        $trans_type = $trans_type << 5;
        $_ = $widgets->{'transitions'}->get_widget('entryTransNew')->get_text();
        if (/Up/) {
            $trans_type = $trans_type + UP;
        } elsif (/Down/) {
            $trans_type = $trans_type + DOWN;
        }

        if (/Left/) {
            $trans_type = $trans_type + LEFT;
        } elsif (/Right/) {
            $trans_type = $trans_type + RIGHT;
        }
        if ($widgets->{'transitions'}->get_widget('checkTransNew')->get_active()
          )
        {
            $trans_type = $trans_type + WAIT;
        }

        # Set old direction
        $trans_type = $trans_type << 5;
        $_ = $widgets->{'transitions'}->get_widget('entryTransOld')->get_text();
        if (/Up/) {
            $trans_type = $trans_type + UP;
        } elsif (/Down/) {
            $trans_type = $trans_type + DOWN;
        }

        if (/Left/) {
            $trans_type = $trans_type + LEFT;
        } elsif (/Right/) {
            $trans_type = $trans_type + RIGHT;
        }
        if ($widgets->{'transitions'}->get_widget('checkTransOld')->get_active()
          )
        {
            $trans_type = $trans_type + WAIT;
        }

        my $query =
          "SELECT type,data FROM playlist WHERE playorder="
          . $model->get($iter, 2);
        qdebug($query);
        my $sth = $lyricDbh->prepare($query)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        my $rv = $sth->execute
          || display_fatal($errorcodes->{'sqlexecute'}, $!);
        my @row = $sth->fetchrow_array();
        if ($row[0] eq "play") {
            $query =
                "UPDATE playlist SET transition="
              . $trans_type
              . " WHERE playorder="
              . $model->get($iter, 2)
              . " OR playlist="
              . $row[1];
        } else {
            $query =
                "UPDATE playlist SET transition="
              . $trans_type
              . " WHERE playorder="
              . $model->get($iter, 2);
        }
        qdebug($query);
        $sth = $lyricDbh->prepare($query)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        $rv = $sth->execute
          || display_fatal($errorcodes->{'sqlexecute'}, $!);
    }

}

sub mod {
    my ($inp, $div) = @_;
    my $rem = (($inp / $div) - (int($inp / $div))) * $div;
    return $rem;
}

sub user_admin {
    debug("User access administration");
    $widgets->{'admin'} =
      Gtk2::GladeXML->new($globals->{'gladefile'}, 'dialogAdmin', 'lyricue');
    $widgets->{'admin'}->signal_autoconnect_from_package('');
    eval { open(ACCESS, $globals->{'accessfile'}); };
    if ($@) {
        debug("No existing access.conf");
    } else {
        my $users = 0;
        my (@username, @useraccess);
        while (<ACCESS>) {
            ($username[$users], $useraccess[$users]) = split(/=/, $_, 2);
            $username[$users]   =~ s/ *$//g;
            $useraccess[$users] =~ s/ *$//g;
            $users++;
        }
        close ACCESS;
        $widgets->{'admin'}->get_widget('tableAccess')->resize($users, 6);
        foreach my $count (0 .. ($users - 1)) {
            debug("Users: " . $username[$count]);
            $widgets->{'adminUsername'}[$count] =
              Gtk2::Label->new($username[$count]);
            $widgets->{'admin'}->get_widget('tableAccess')
              ->attach($widgets->{'adminUsername'}[$count],
                0, 1, $count + 2, $count + 3, 'fill', 'expand', 0, 0);

            $widgets->{'adminPlaylist'}[$count] = Gtk2::CheckButton->new();
            $widgets->{'admin'}->get_widget('tableAccess')
              ->attach($widgets->{'adminPlaylist'}[$count],
                1, 2, $count + 2, $count + 3, 'fill', 'expand', 0, 0);
            $widgets->{'adminEdit'}[$count] = Gtk2::CheckButton->new();
            $widgets->{'admin'}->get_widget('tableAccess')
              ->attach($widgets->{'adminEdit'}[$count],
                2, 3, $count + 2, $count + 3, 'fill', 'expand', 0, 0);
            $widgets->{'adminDelete'}[$count] = Gtk2::CheckButton->new();
            $widgets->{'admin'}->get_widget('tableAccess')
              ->attach($widgets->{'adminDelete'}[$count],
                3, 4, $count + 2, $count + 3, 'fill', 'expand', 0, 0);
            $widgets->{'adminDisplay'}[$count] = Gtk2::CheckButton->new();
            $widgets->{'admin'}->get_widget('tableAccess')
              ->attach($widgets->{'adminDisplay'}[$count],
                4, 5, $count + 2, $count + 3, 'fill', 'expand', 0, 0);
            $widgets->{'adminAdmin'}[$count] = Gtk2::CheckButton->new();
            $widgets->{'admin'}->get_widget('tableAccess')
              ->attach($widgets->{'adminAdmin'}[$count],
                5, 6, $count + 2, $count + 3, 'fill', 'expand', 0, 0);
            $_ = $useraccess[$count];

            if (/p/) {
                $widgets->{'adminPlaylist'}[$count]->set_active(TRUE);
            }
            if (/e/) { $widgets->{'adminEdit'}[$count]->set_active(TRUE); }
            if (/d/) {
                $widgets->{'adminDelete'}[$count]->set_active(TRUE);
            }
            if (/s/) {
                $widgets->{'adminDisplay'}[$count]->set_active(TRUE);
            }
            if (/a/) { $widgets->{'adminAdmin'}[$count]->set_active(TRUE); }
        }
        $widgets->{'admin'}->get_widget('tableAccess')->show_all();
    }
    my $confirm = $widgets->{'admin'}->get_widget('dialogAdmin')->run();
    if ($confirm eq "ok") {
        open(ACCESS, ">" . $globals->{'accessfile'})
          || display_fatal(
            $errorcodes->{'fileopenwrite'} . $globals->{'accessfile'}, $!);
        my $users =
          ($widgets->{'admin'}->get_widget('tableAccess')->get('n-rows') - 2);
        foreach my $count (0 .. ($users - 1)) {
            my $out = $widgets->{'adminUsername'}[$count]->get_text() . " = ";
            if ($widgets->{'adminPlaylist'}[$count]->get_active()) {
                $out .= "p";
            }
            if ($widgets->{'adminEdit'}[$count]->get_active()) {
                $out .= "e";
            }
            if ($widgets->{'adminDelete'}[$count]->get_active()) {
                $out .= "d";
            }
            if ($widgets->{'adminDisplay'}[$count]->get_active()) {
                $out .= "s";
            }
            if ($widgets->{'adminAdmin'}[$count]->get_active()) {
                $out .= "a";
            }
            print ACCESS $out . "\n";
        }
        close ACCESS;
    }
    $widgets->{'admin'}->get_widget('dialogAdmin')->destroy();
    $globals->{'access'} = load_access();

}

sub load_access {
    debug("Loading access settings");
    my $access = "";

    # Load the access settings (ignore if using sqlite)
    if (($config->{'DatabaseType'} eq "SQLite") || ($^O eq 'MSWin32')) {
        $access = "spade";
    } else {
        open(ACCESS, $globals->{'accessfile'})
          || display_fatal(
            $errorcodes->{'fileopenread'} . $globals->{'accessfile'}, $!);
        my $username = getpwuid($<);
        while (<ACCESS>) {
            chomp;
            if (/^$username/) {
                $access = $_;
                $access =~ s/^.*= *//g;
            }
        }
        close ACCESS;
    }
    debug("Access set at " . $access);

    $_ = $access;
    my @remove_items = ();
    my @show_items   = ();
    if (!/e/) {
        push @remove_items, @edit_items;
    } else {
        push @show_items, @edit_items;
    }

    if (!/d/) {
        push @remove_items, @delete_items;
    } else {
        push @show_items, @delete_items;
    }

    if (!/s/) {
        push @remove_items, @display_items;
    } else {
        push @show_items, @display_items;
    }

    if (!/p/) {
        push @remove_items, @playlist_items;
    } else {
        push @show_items, @playlist_items;
    }

    if (!/a/) {
        push @remove_items, @admin_items;
    } else {
        push @show_items, @admin_items;
    }
    if (!-w $globals->{'accessfile'}) {
        push @remove_items, "user_administration1";
    }

    foreach my $item (@show_items) {
        debug("Showing $item");
        if (defined $widgets->{'main'}->get_widget($item)) {
            $widgets->{'main'}->get_widget($item)->show();
        }
    }

    foreach my $item (@remove_items) {
        debug("Hiding $item");
        if (defined $widgets->{'main'}->get_widget($item)) {
            $widgets->{'main'}->get_widget($item)->hide();
        }
    }
    return $access;
}

sub add_user {
    debug("Adding a user");
    my $userxml = Gtk2::GladeXML->new($globals->{'gladefile'},
        'dialogPromptEntry', 'lyricue');
    $userxml->signal_autoconnect_from_package('');
    $userxml->get_widget('dialogPromptEntry')
      ->set_title(Encode::decode("utf-8", gettext("Add a user")));
    $userxml->get_widget('labelPromptE')->set_text("Enter username");
    my $confirm = $userxml->get_widget('dialogPromptEntry')->run();
    if ($confirm eq "ok") {
        my $users =
          ($widgets->{'admin'}->get_widget('tableAccess')->get('n-rows') - 2);
        debug("Users :" . $users);
        $widgets->{'admin'}->get_widget('tableAccess')->resize($users + 3, 6);
        $widgets->{'adminUsername'}[$users] =
          Gtk2::Label->new($userxml->get_widget('entryPromptE')->get_text());
        $widgets->{'admin'}->get_widget('tableAccess')
          ->attach($widgets->{'adminUsername'}[$users],
            0, 1, $users + 2, $users + 3, 'fill', 'expand', 0, 0);
        $widgets->{'adminPlaylist'}[$users] = Gtk2::CheckButton->new();
        $widgets->{'admin'}->get_widget('tableAccess')
          ->attach($widgets->{'adminPlaylist'}[$users],
            1, 2, $users + 2, $users + 3, 'fill', 'expand', 0, 0);
        $widgets->{'adminEdit'}[$users] = Gtk2::CheckButton->new();
        $widgets->{'admin'}->get_widget('tableAccess')
          ->attach($widgets->{'adminEdit'}[$users],
            2, 3, $users + 2, $users + 3, 'fill', 'expand', 0, 0);
        $widgets->{'adminDelete'}[$users] = Gtk2::CheckButton->new();
        $widgets->{'admin'}->get_widget('tableAccess')
          ->attach($widgets->{'adminDelete'}[$users],
            3, 4, $users + 2, $users + 3, 'fill', 'expand', 0, 0);
        $widgets->{'adminDisplay'}[$users] = Gtk2::CheckButton->new();
        $widgets->{'admin'}->get_widget('tableAccess')
          ->attach($widgets->{'adminDisplay'}[$users],
            4, 5, $users + 2, $users + 3, 'fill', 'expand', 0, 0);
        $widgets->{'adminAdmin'}[$users] = Gtk2::CheckButton->new();
        $widgets->{'admin'}->get_widget('tableAccess')
          ->attach($widgets->{'adminAdmin'}[$users],
            5, 6, $users + 2, $users + 3, 'fill', 'expand', 0, 0);
    }
    $widgets->{'admin'}->get_widget('tableAccess')->show_all();
    close_dialog($userxml->get_widget('dialogPromptEntry'));
}

sub search_changed {
    debug("Search changed");
    reset_timer($globals->{'update_timer'});
    $globals->{'update_timer'} = Glib::Timeout->add(500, \&update_available);
}

sub init_preview {
    debug("init preview");

    # Start/stop preview as needed
    if ($config->{'DynamicPreview'}) {
        if (!defined($widgets->{'preview'})) {
            $widgets->{'main'}->get_widget('framePreview')->show();
            $widgets->{'preview'} = Gtk2::Socket->new;
            $widgets->{'preview'}->show;
            $widgets->{'preview'}->set_size_request(280, 210);
            $widgets->{'main'}->get_widget('framePreview')
              ->set_size_request(280, 210);
            $widgets->{'main'}->get_widget('framePreview')
              ->add($widgets->{'preview'});

            debug(
                sprintf(
                    "lyricue_server -r %s -m %d -p %d",
                    $globals->{'mysqlhost'}, $widgets->{'preview'}->get_id,
                    $globals->{'preview_port'}
                )
            );

            $globals->{'preview_pid'} = fork;
            if ($globals->{'preview_pid'} < 0) {
                display_fatal(
                    "Unable to start the lyricue server as a preview window",
                    $!);
            }
            if ($globals->{'preview_pid'} == 0) {
                my $debug_cmd = "";
                if ($globals->{'debugging'}) {
                    $debug_cmd = "-d";
                }
                exec(
                    sprintf(
                        "lyricue_server -r %s -m %d -p %d\n",
                        $globals->{'mysqlhost'},
                        $widgets->{'preview'}->get_id,
                        $globals->{'preview_port'}
                    )
                );
            }
            $widgets->{'preview'}->signal_connect(
                'plug-removed' => sub {
                    debug("Lyricue preview died..restarting\n");
                    $widgets->{'main'}->get_widget('framePreview')
                      ->remove($widgets->{'preview'});
                    init_preview();
                    1;
                }
            );
        }
    } else {
        if ($widgets->{'preview'}) {
            $widgets->{'preview'}->destroy;
            undef $widgets->{'preview'};
            kill 9, $globals->{'preview_pid'};
        }
        $widgets->{'main'}->get_widget('framePreview')->hide();
    }
}

sub init_miniview {
    debug("init miniview");

    # Start/stop miniview as needed
    if ($config->{'Miniview'}) {
        if (!defined($widgets->{'miniview'})) {
            update_display("status", "previewon", "");
            $widgets->{'main'}->get_widget('frameCurrent')->show();
            $widgets->{'miniview'} = Gtk2::Socket->new;
            $widgets->{'miniview'}->show;
            $widgets->{'miniview'}->set_size_request(280, 210);
            $widgets->{'main'}->get_widget('frameCurrent')
              ->add($widgets->{'miniview'});
            $widgets->{'main'}->get_widget('frameCurrent')
              ->set_size_request(-1, -1);
            debug(
                sprintf(
                    "lyricue_server -r %s -m %d -p %d\n",
                    $globals->{'mysqlhost'},
                    $widgets->{'miniview'}->get_id,
                    $globals->{'miniview_port'}
                )
            );

            $globals->{'miniview_pid'} = fork;
            if ($globals->{'miniview_pid'} < 0) {
                display_fatal(
                    "Unable to start the lyricue server as a preview window",
                    $!);
            }
            if ($globals->{'miniview_pid'} == 0) {
                exec(
                    sprintf(
                        "lyricue_server -r %s -m %d -p %d\n",
                        $globals->{'mysqlhost'}, $widgets->{'miniview'}->get_id,
                        $globals->{'miniview_port'}
                    )
                );
            }

            $widgets->{'miniview'}->signal_connect(
                'plug-removed' => sub {
                    debug("Lyricue miniview died..restarting");
                    $widgets->{'main'}->get_widget('frameCurrent')
                      ->remove($widgets->{'miniview'});
                    init_miniview();
                    1;
                }
            );
        }
    } else {
        if ($widgets->{'miniview'}) {
            $widgets->{'miniview'}->destroy;
            undef $widgets->{'miniview'};
            kill 9, $globals->{'miniview_pid'};
        }
        update_display("status", "previewoff", "");
        $widgets->{'main'}->get_widget('frameCurrent')->hide();
    }
}

sub quick_save {
    debug("Quick Save");
    my $buffer    = $widgets->{'main'}->get_widget('textQuick')->get_buffer();
    my $songtext  = $buffer->get_text($buffer->get_bounds, FALSE);
    my $playorder = $widgets->{'main'}->get_widget('textQuick')->{user_data};
    my $query = "SELECT data,type FROM playlist WHERE playorder=" . $playorder;
    qdebug($query);
    $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    $rv = $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);
    my @row = $sth->fetchrow_array();

    if ($row[1] eq "play") {
        $query =
          "SELECT data,type FROM playlist WHERE playlist=5 ORDER BY playorder";
        qdebug($query);
        $sth = $lyricDbh->prepare($query)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        $rv = $sth->execute
          || display_fatal($errorcodes->{'sqlexecute'}, $!);
        @row = $sth->fetchrow_array();
    }

    if ($row[1] eq "song") {
        if (!defined $row[0]) { $row[0] = ""; }

        $query =
            "UPDATE page SET lyrics="
          . $lyricDbh->quote(Encode::encode("iso-8859-1", $songtext))
          . " WHERE pageid="
          . $row[0];
        qdebug($query);
        $sth = $lyricDbh->prepare($query)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        $sth->execute || display_fatal($errorcodes->{'sqlexecute'}, $!);
    }
}

# Just put the text in the quickview section onto the screen
sub quick_show {
    debug("Quickshow textarea");
    my $buffer = $widgets->{'main'}->get_widget('textQuick')->get_buffer();
    my $songtext = $buffer->get_text($buffer->get_bounds, FALSE);
    $songtext =~ s/\n/#BREAK#/g;
    $songtext =~ s/:/#SEMI#/g;
    update_display("preview", "ignore", $songtext);
}

# Called when the preview windows are resized
sub resize_preview {
    my ($widget, $event) = @_;

   #print $event."\n";
   #debug ("Resizing previews");
   #my $pos = $widgets->{'main'}->get_widget('hpanedMainRight')->get_position();
   #my ($width,$height) = $widgets->{'preview'}->get_size();
   #print ("$width * $height\n");
    return FALSE;
}

# Return a Gdk::Pixbuf of the given media at the given res
sub create_pixbuf {
    my ($id, $width, $height) = @_;
    my ($pixbuf);
    my $query =
      "SELECT format, description, data FROM media WHERE id=\"" . $id . "\"";
    qdebug($query);
    my $sth = $mediaDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    my $rv = $sth->execute
      || display_fatal($errorcodes->{'sqlexecute'}, $!);
    my $row = $sth->fetchrow_hashref();
    if ($row->{'format'} eq "bg") {
        my @xpm = ("1 1 1 1", "  c " . $row->{'description'}, " ");
        $pixbuf = Gtk2::Gdk::Pixbuf->new_from_xpm_data(@xpm);
    } else {
        my $pixbuf_loader = Gtk2::Gdk::PixbufLoader->new();
        eval { $pixbuf_loader->write($row->{'data'}); };
        eval { $pixbuf_loader->close() };
        if ($@) {
            my @xpm = ("1 1 1 1", "  c " . $row->{'description'}, " ");
            $pixbuf = Gtk2::Gdk::Pixbuf->new_from_xpm_data(@xpm);
        } else {
            $pixbuf = $pixbuf_loader->get_pixbuf();
        }
    }

    if ($width == 0) {
        return $pixbuf;
    } else {
        return $pixbuf->scale_simple($width, $height, 'nearest');
    }
}

sub import_image {
    import_media("img");
}

sub import_background {
    import_media("bg");
}

sub import_media {
    my ($type) = @_;
    debug("Import media " . $type);
    my $filexml =
      Gtk2::GladeXML->new($globals->{'gladefile'}, 'dialogFile', 'lyricue');
    $filexml->signal_autoconnect_from_package('');
    $filexml->get_widget('buttonFileOK')
      ->signal_connect("clicked", "select_category", $filexml);
    $filexml->get_widget('dialogFile')->{user_data} = $type;
    $filexml->get_widget('dialogFile')->set_select_multiple(TRUE);
}

sub select_category {
    my ($widget, $filexml) = @_;
    debug("select category");
    my $hashnum   = 0;
    my @filenames = $filexml->get_widget('dialogFile')->get_selections;
    debug(@filenames);
    my $type      = $filexml->get_widget('dialogFile')->{user_data};
    my $choosexml =
      Gtk2::GladeXML->new($globals->{'gladefile'}, 'windowChoosePlay');
    $choosexml->get_widget('windowChoosePlay')
      ->set_title(Encode::decode("utf-8", gettext("Select Category")));
    $choosexml->get_widget('buttonLoad')->set_label("Select");
    $choosexml->get_widget('buttonDelete')->hide();
    $choosexml->get_widget('buttonRename')->hide();
    $choosexml->get_widget('buttonLoad')
      ->signal_connect("clicked", "do_import_media", $choosexml);
    $choosexml->get_widget('buttonNew')
      ->signal_connect("clicked", "do_import_media", $choosexml);
    $choosexml->get_widget('buttonCancel')
      ->signal_connect("clicked", "close_dialog");
    $choosexml->get_widget('windowChoosePlay')->show;
    $choosexml->get_widget('windowChoosePlay')->{'user_data'} = \@filenames;
    $choosexml->get_widget('treeChoosePlay')->{'user_data'}   = $type;
    my $store = $choosexml->get_widget('treeChoosePlay')->get_model();

    if ($store) {
        $store->clear;
    } else {
        $store = Gtk2::ListStore->new('Glib::String');
        $choosexml->get_widget('treeChoosePlay')->set_model($store);
        my $renderer  = Gtk2::CellRendererText->new;
        my $selection = $choosexml->get_widget('treeChoosePlay')->get_selection;
        $selection->signal_connect("changed", "update_category", $choosexml);
        my $column =
          Gtk2::TreeViewColumn->new_with_attributes("Category", $renderer,
            text => 0);
        $choosexml->get_widget('treeChoosePlay')->append_column($column);
    }

    my $query =
        "SELECT DISTINCT category FROM media WHERE type=\"" . $type
      . "\" ORDER BY category";
    qdebug($query);
    my $sth = $mediaDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    my $rv = $sth->execute
      || display_fatal($errorcodes->{'sqlexecute'}, $!);
    my @row;
    while (@row = $sth->fetchrow_array()) {
        my $iter = $store->append;
        $store->set($iter, 0, $row[0]);
    }
    $choosexml->get_widget('windowChoosePlay')
      ->set_transient_for($widgets->{'image'}->get_widget('dialogImage'));

    close_dialog($widget);
}

sub do_import_media {
    my ($widget, $choosexml) = @_;
    debug("Importing media");
    if ($choosexml->get_widget('entryChoosePlay')->get_text() ne "") {
        my $type      = $choosexml->get_widget('treeChoosePlay')->{'user_data'};
        my $category  = $choosexml->get_widget('entryChoosePlay')->get_text();
        my $filenames =
          $choosexml->get_widget('windowChoosePlay')->{'user_data'};
        my $filename = "";
        foreach $filename (@$filenames) {
            my $format = $filename;
            $format =~ s/^.*\.//g;
            my $description = $filename;
            $description =~ s/^.*\///g;
            $description =~ s/\..*?$//g;
            my $owner = getpwuid($<);
            my @date  = localtime(time);
            my $time  = sprintf(
                "%04d-%02d-%02d %02d:%02d:%02d",
                $date[5] + 1900,
                $date[4], $date[3], $date[2], $date[1], $date[0]
            );
            debug("Category: $category\nFilename: $filename");
            open(MEDIA, $filename);
            my $data = "";

            while (<MEDIA>) {
                $data .= $_;
            }
            close MEDIA;
            debug("Length: " . length($data));
            my $sth =
              $mediaDbh->prepare(
q{INSERT INTO media(category, subcategory, type, format, insertedby, insertdate, description, data) VALUES (?,?,?,?,?,?,?,?)}
              );
            my $rv =
              $sth->execute($category, "", $type, $format, $owner, $time,
                $description, $data);
        }
    }
    close_dialog($widget);
    my $category =
      $widgets->{'image'}->get_widget('optionImageCategory')
      ->get_menu->get_active->{user_data};
    update_imagedir(
        $widgets->{'image'}->get_widget('optionImageCategory')->{'user_data'},
        $category);
}

sub update_category {
    my ($selection, $choosexml) = @_;
    debug("Selected a category");

    my ($model, $iter) = $selection->get_selected;
    if ($iter) {
        $choosexml->get_widget('entryChoosePlay')
          ->set_text($model->get($iter, 0));
    }
}

sub update_quickedit {
    debug("Updating Quick edit");
    my $selection =
      $widgets->{'main'}->get_widget('treePlaylist')->get_selection;
    my ($model, $iter) = $selection->get_selected;
    if ($iter) {
        my $quicktext = "";

        my $plnumber = $model->get($iter, 2);
        my $loop     = 0;

        # Find the items lyrics for Quick Editing
        while ($loop == 0) {
            my $query =
              "SELECT type,data,playlist FROM playlist WHERE playorder="
              . $plnumber;
            qdebug($query);
            $sth = $lyricDbh->prepare($query)
              || display_fatal($errorcodes->{'sqlprepare'}, $!);
            $rv = $sth->execute
              || display_fatal($errorcodes->{'sqlexecute'}, $!);
            my @row = $sth->fetchrow_array();
            if ($row[0] eq "song") {
                $query =
"SELECT lyrics FROM playlist as pl, page as pa WHERE pl.data=pa.pageid AND pl.playorder="
                  . $plnumber;
                qdebug($query);
                $sth = $lyricDbh->prepare($query)
                  || display_fatal($errorcodes->{'sqlprepare'}, $!);
                $rv = $sth->execute
                  || display_fatal($errorcodes->{'sqlexecute'}, $!);
                my @row2 = $sth->fetchrow_array();
                if (!defined $row2[0]) { $row2[0] = ""; }
                $quicktext = $row2[0];
                $loop      = 1;
                $widgets->{'main'}->get_widget('textQuick')
                  ->set_wrap_mode('none');
            } elsif ($row[0] eq "play") {
                $query =
                    "SELECT playorder FROM playlist WHERE playlist="
                  . $row[1]
                  . " ORDER BY playorder";
                qdebug($query);
                $sth = $lyricDbh->prepare($query)
                  || display_fatal($errorcodes->{'sqlprepare'}, $!);
                $rv = $sth->execute
                  || display_fatal($errorcodes->{'sqlexecute'}, $!);
                my @row = $sth->fetchrow_array();
                $plnumber = $row[0];
                $loop     = 0;
            } elsif ($row[0] eq "vers") {
                my ($startv, $endv) = split(/-/, $row[1], 2);
                $query = "SELECT title FROM playlists WHERE id=" . $row[2];
                qdebug($query);
                $sth = $lyricDbh->prepare($query)
                  || display_fatal($errorcodes->{'sqlprepare'}, $!);
                $rv = $sth->execute
                  || display_fatal($errorcodes->{'sqlexecute'}, $!);
                my @row = $sth->fetchrow_array();
                my @line = split(/:/, $row[0]);
                if ($globals->{'usesword'}) {
                    my $command = sprintf(
                        "%s -b %s -e UTF8 -k '%s' %d:%d-%d:%d |tr \\\\n \' \'",
                        $globals->{'diatheke'},
                        $globals->{'bibledb'}, $line[0], $line[1], $startv,
                        $line[1], $endv, $line[0]
                    );
                    debug($command);
                    my $command_out = Encode::decode("utf-8", `$command`);
                    ($line[0], undef) = split(/\s\d/, $command_out, 2);
                    my @command_lines = split(/$line[0] /, $command_out);
                    foreach (@command_lines) {
                        chomp;
                        my $line2 = $_;
                        $line2 =~ s/^$line[0] //g;
                        if ($line2 ne "") {
                            $quicktext .= $line2 . "\n";
                        }
                    }
                } else {
                    $query =
"SELECT chapternum,versenum,verse FROM verse WHERE book=\""
                      . $line[0]
                      . "\" AND chapternum="
                      . $line[1]
                      . " AND versenum>="
                      . $startv
                      . " AND versenum <= "
                      . $endv;
                    qdebug($query);
                    $sth = $bibleDbh->prepare($query)
                      || display_fatal($errorcodes->{'sqlprepare'}, $!);
                    $rv = $sth->execute
                      || display_fatal($errorcodes->{'sqlexecute'}, $!);

                    while (@row = $sth->fetchrow_array()) {
                        $quicktext .=
                          $row[0] . ":" . $row[1] . "   " . $row[2] . "\n";
                    }
                }
                $loop = 1;
                $widgets->{'main'}->get_widget('textQuick')
                  ->set_wrap_mode('word');
            } elsif ($row[0] eq "imag") {
                $quicktext = "Image";
                $loop      = 1;
            } elsif ($row[0] eq "back") {
                $quicktext = "Background";
                $loop      = 1;
            } else {
                $loop = 1;
            }
        }

        $widgets->{'main'}->get_widget('textQuick')
          ->get_buffer->set_text($quicktext);
        $widgets->{'main'}->get_widget('textQuick')->{user_data} =
          $model->get($iter, 2);
        $sth->finish;
    }
}

sub media_move {
    debug("Moving media");
    my $selection = $widgets->{'image'}->get_widget('treeImage')->get_selection;
    my @selecteditems = $selection->get_selected_rows();
    my @id            = ();
    my $type          =
      $widgets->{'image'}->get_widget('optionImageCategory')->{'user_data'};
    if ($selecteditems[0]) {
        foreach (@selecteditems) {
            my $iter =
              $widgets->{'image'}->get_widget('treeImage')
              ->get_model->get_iter($_);
            push @id, $widgets->{'image'}->get_widget('treeImage')
              ->get_model->get($iter, 1);
        }
    }
    if (@id) {
        my $choosexml =
          Gtk2::GladeXML->new($globals->{'gladefile'}, 'windowChoosePlay');
        $choosexml->get_widget('windowChoosePlay')
          ->set_title(Encode::decode("utf-8", gettext("Select Category")));
        $choosexml->get_widget('buttonLoad')->set_label("Select");
        $choosexml->get_widget('buttonDelete')->hide();
        $choosexml->get_widget('buttonRename')->hide();
        $choosexml->get_widget('buttonLoad')
          ->signal_connect("clicked", "do_move_media", $choosexml);
        $choosexml->get_widget('buttonNew')
          ->signal_connect("clicked", "do_move_media", $choosexml);
        $choosexml->get_widget('buttonCancel')
          ->signal_connect("clicked", "close_dialog");
        $choosexml->get_widget('windowChoosePlay')->show;
        $choosexml->get_widget('treeChoosePlay')->{'user_data'}   = $type;
        $choosexml->get_widget('windowChoosePlay')->{'user_data'} = \@id;
        my $store = $choosexml->get_widget('treeChoosePlay')->get_model();

        if ($store) {
            $store->clear;
        } else {
            $store = Gtk2::ListStore->new('Glib::String');
            $choosexml->get_widget('treeChoosePlay')->set_model($store);
            my $renderer  = Gtk2::CellRendererText->new;
            my $selection =
              $choosexml->get_widget('treeChoosePlay')->get_selection;
            $selection->signal_connect("changed", "update_category",
                $choosexml);
            my $column =
              Gtk2::TreeViewColumn->new_with_attributes("Category", $renderer,
                text => 0);
            $choosexml->get_widget('treeChoosePlay')->append_column($column);
        }

        my $query =
            "SELECT DISTINCT category FROM media WHERE type=\"" . $type
          . "\" ORDER BY category";
        my $sth = $mediaDbh->prepare($query)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        my $rv = $sth->execute
          || display_fatal($errorcodes->{'sqlexecute'}, $!);
        my @row;
        while (@row = $sth->fetchrow_array()) {
            my $iter = $store->append;
            $store->set($iter, 0, $row[0]);
        }
    }
}

sub do_move_media {
    my ($widget, $choosexml) = @_;
    debug("Moving media");
    if ($choosexml->get_widget('entryChoosePlay')->get_text() ne "") {
        my $type     = $choosexml->get_widget('treeChoosePlay')->{'user_data'};
        my $category = $choosexml->get_widget('entryChoosePlay')->get_text();
        my $id = $choosexml->get_widget('windowChoosePlay')->{'user_data'};
        foreach (@$id) {
            my $query =
                "UPDATE media SET category=\""
              . $category
              . "\" WHERE id=\""
              . $_ . "\"";
            qdebug($query);
            $sth = $mediaDbh->prepare($query)
              || display_fatal($errorcodes->{'sqlprepare'}, $!);
            $rv = $sth->execute
              || display_fatal($errorcodes->{'sqlexecute'}, $!);
        }
    }
    close_dialog($widget);
    my $category =
      $widgets->{'image'}->get_widget('optionImageCategory')
      ->get_menu->get_active->{user_data};
    update_imagedir(
        $widgets->{'image'}->get_widget('optionImageCategory')->{'user_data'},
        $category);
}

sub media_delete {
    debug("Deleting media");
    my $selection = $widgets->{'image'}->get_widget('treeImage')->get_selection;
    my @selecteditems = $selection->get_selected_rows();
    my @id            = ();
    if ($selecteditems[0]) {
        my $title = "";
        foreach (@selecteditems) {
            my $iter =
              $widgets->{'image'}->get_widget('treeImage')
              ->get_model->get_iter($_);
            push @id, $widgets->{'image'}->get_widget('treeImage')
              ->get_model->get($iter, 1);
            $title .= "\""
              . $widgets->{'image'}->get_widget('treeImage')
              ->get_model->get($iter, 0) . "\", ";
        }
        if ($title ne "") {
            $title =~ s/, $//;
            debug("Deleting $title");
            my $labelText =
              Encode::decode("utf-8",
                gettext("Are you sure you wish to delete "))
              . $title . "\n";
            my $deletexml = Gtk2::GladeXML->new($globals->{'gladefile'},
                'dialogConfirm', 'lyricue');
            $deletexml->signal_autoconnect_from_package('');
            $deletexml->get_widget('labelDelete')->set_text($labelText);
            $deletexml->get_widget('dialogConfirm')
              ->set_title(
                Encode::decode("utf-8", gettext("Confirm Delete Image")));
            my $confirm = $deletexml->get_widget('dialogConfirm')->run();

            if ($confirm eq "ok") {

                foreach (@id) {
                    my $query = "DELETE FROM media WHERE id=" . $_;
                    qdebug($query);
                    my $sth = $mediaDbh->prepare($query)
                      || display_fatal($errorcodes->{'sqlprepare'}, $!);
                    my $rv = $sth->execute
                      || display_fatal($errorcodes->{'sqlexecute'}, $!);

                    close_dialog($deletexml->get_widget('dialogConfirm'));
                    my $category =
                      $widgets->{'image'}->get_widget('optionImageCategory')
                      ->get_menu->get_active->{user_data};
                    update_imagedir(
                        $widgets->{'image'}->get_widget('optionImageCategory')
                          ->{'user_data'},
                        $category
                    );
                }
            }
        }
    }

}

sub rename_media {
    my ($widget, $id, $newname) = @_;
    debug("rename media");
    my $oldname = $widget->get('text');
    if ($oldname ne $newname) {
        my $iter =
          $widgets->{'image'}->get_widget('treeImage')
          ->get_model->get_iter_from_string($id);
        $id =
          $widgets->{'image'}->get_widget('treeImage')
          ->get_model->get($iter, 1);
        debug("Renaming media from $oldname to $newname");
        my $query = "UPDATE media SET description=\"$newname\" WHERE id=$id";
        qdebug($query);
        my $sth = $mediaDbh->prepare($query)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        my $rv = $sth->execute
          || display_fatal($errorcodes->{'sqlexecute'}, $!);
        update_imagedir(
            $widgets->{'image'}->get_widget('optionImageCategory')
              ->{'user_data'},
            $widgets->{'image'}->get_widget('optionImageCategory')
              ->get_menu->get_active->{user_data}
        );
    }
}

sub change_colour_media {
    my ($widget, $id, $newcolour) = @_;
    debug("change colour media");
    my $oldcolour = $widget->get('text');
    if (($newcolour) && ($oldcolour ne $newcolour)) {
        debug(  "Changing text colour for media from "
              . $oldcolour . " to "
              . $newcolour);
        my $iter =
          $widgets->{'image'}->get_widget('treeImage')
          ->get_model->get_iter_from_string($id);
        $id =
          $widgets->{'image'}->get_widget('treeImage')
          ->get_model->get($iter, 1);

  #        my $query = "UPDATE media SET description=\"$newname\" WHERE id=$id";
  #        qdebug($query);
  #        my $sth = $mediaDbh->prepare($query)
  #          || display_fatal($errorcodes->{'sqlprepare'}, $!);
  #        my $rv = $sth->execute
  #          || display_fatal($errorcodes->{'sqlexecute'}, $!);
  #        update_imagedir(
  #            $widgets->{'image'}->get_widget('optionImageCategory')
  #              ->{'user_data'},
  #            $widgets->{'image'}->get_widget('optionImageCategory')
  #              ->get_menu->get_active->{user_data}
  #        );
    }
}

sub restore_db {
    debug("Restoring DB");

    # Get filename of DB
    my $fileDialog =
      Gtk2::GladeXML->new($globals->{'gladefile'}, 'dialogFile', 'lyricue');
    $fileDialog->get_widget('dialogFile')
      ->set_title(
        Encode::decode("utf-8", gettext("Select Database Backup file")));
    my $response = $fileDialog->get_widget('dialogFile')->run();
    if ($response eq "ok") {
        my $filename = $fileDialog->get_widget('dialogFile')->get_filename;
        close_dialog($fileDialog->get_widget('dialogFile'));

        my $confirmDialog = Gtk2::GladeXML->new($globals->{'gladefile'},
            'dialogConfirm', 'lyricue');
        $confirmDialog->signal_autoconnect_from_package('');
        $confirmDialog->get_widget('dialogConfirm')
          ->set_title(
            Encode::decode("utf-8", gettext("Confirm Restore Database")));
        $confirmDialog->get_widget('labelDelete')
          ->set_text(
"WARNING: Restoring this database will overwrite your current database"
          );
        my $confirm = $confirmDialog->get_widget('dialogConfirm')->run();
        if ($confirm eq "ok") {

            debug("ok");
            open(INPUT, "gzip -dc " . $filename . "|");
            my $table = "";
            my $db    = "";
            my $dbh   = "";
            while (<INPUT>) {
                chomp;
                if (/^USE/) {
                    $db = $_;
                    $db =~ s/^USE (.*);.*$/$1/g;
                    debug($db . " - database");
                    if ($db eq "lyricDb") {
                        $dbh = $lyricDbh;
                    } elsif ($db eq "mediaDb") {
                        $dbh = $mediaDbh;
                    } else {
                        $dbh = "";
                    }
                } elsif (/^INSERT INTO/) {
                    if ($dbh ne "") {
                        my $insert   = $_;
                        my $tmptable = $insert;
                        $tmptable =~ s/^INSERT INTO `(.*?)`.*$/$1/g;
                        if ($tmptable ne $table) {
                            $table = $tmptable;
                            my $query = "DELETE FROM " . $table;
                            my $sth   = $dbh->prepare($query)
                              || display_fatal($errorcodes->{'sqlprepare'}, $!);
                            my $rv = $sth->execute
                              || display_fatal($errorcodes->{'sqlexecute'}, $!);
                        }
                        my $sth = $dbh->prepare($insert)
                          || display_fatal($errorcodes->{'sqlprepare'}, $!);
                        my $rv = $sth->execute
                          || display_fatal($errorcodes->{'sqlexecute'}, $!);

                    }
                }
            }
        }
        close_dialog($confirmDialog->get_widget('dialogConfirm'));
    } else {
        close_dialog($fileDialog->get_widget('dialogFile'));
    }
}

sub backup_db {
    debug("Backup DB selected");
    $widgets->{'backupdb'} =
      Gtk2::GladeXML->new($globals->{'gladefile'}, 'dialogDatabase', 'lyricue');
    $widgets->{'backupdb'}->signal_autoconnect_from_package('');
    $widgets->{'backupdb'}->get_widget('entryPrefDBFilename')
      ->set_text($ENV{"HOME"} . "/Lyricue_DB.gz");
    my $response = $widgets->{'backupdb'}->get_widget('dialogDatabase')->run();
    if ($response eq "ok") {
        my $filename =
          $widgets->{'backupdb'}->get_widget('entryPrefDBFilename')->get_text();
        if (!($filename =~ /\.gz$/)) {
            $filename .= ".gz";
        }
        my $databases = "";
        if ($widgets->{'backupdb'}->get_widget('checkLyricdb')->get_active()) {
            $databases .= "lyricDb ";
        }
        if ($widgets->{'backupdb'}->get_widget('checkMediadb')->get_active()) {
            $databases .= "mediaDb ";
        }

        # This will need to be written to support sqlite
        #foreach (split(/ /,$databases)){
        #my $db= db_connect($_,"");
        #my @tables=$db->tables;
        #foreach (@tables) {
        #}
        #}
        my $command =
"mysqldump --no-create-db --no-create-info --user=lyric --password='' --databases "
          . $databases
          . " | gzip -c > "
          . $filename;
        system $command;
        if ($? == -1) {
            debug("Failed to execute: " . $command);
        } else {
            debug("Backed up to " . $filename);
        }
    }
    close_dialog($widgets->{'backupdb'}->get_widget('dialogDatabase'));
}

sub backup_db_browse {
    debug("Browsing for BackupDB filename");
    my $fileDialog =
      Gtk2::GladeXML->new($globals->{'gladefile'}, 'dialogFile', 'lyricue');
    $fileDialog->get_widget('dialogFile')
      ->set_filename(
        $widgets->{'backupdb'}->get_widget('entryPrefDBFilename')->get_text());
    my $response = $fileDialog->get_widget('dialogFile')->run();
    if ($response eq "ok") {
        my $filename = $fileDialog->get_widget('dialogFile')->get_filename;
        if (!($filename =~ /\.gz$/)) {
            $filename .= ".gz";
        }
        $widgets->{'backupdb'}->get_widget('entryPrefDBFilename')
          ->set_text($filename);
    }
    close_dialog($fileDialog->get_widget('dialogFile'));
}

sub get_bibles {
    my $bibles;
    debug("Getting available bibles");

  if (!$^O eq 'MSWin32') {

    # Bibles provided by Sword libraries
    open(SWORD, $globals->{'diatheke'} . " -b system -k modulelist|");
    while (<SWORD>) {
        if (/^Biblical Texts:/) {
            while (<SWORD>) {
                if (/^Commentaries:/) {
                    while (<SWORD>) { }
                } else {
                    chomp;
                    my @bible = split(/:/, $_, 2);
                    $bible[0] =~ s/\s+$//;
                    $bible[1] =~ s/^\s+//;
                    $bibles->{$bible[0]} = "sword;" . $bible[1];
                }
            }
        }
    }
    close SWORD;
  }
    # Bible databases found
    my $dbs = $globals->{'db_available_db'};
    foreach (keys %$dbs) {
        my $dbname = $_;
        my $db     = db_connect($dbname, "");
        my @tables = $db->tables;
        my $table;
        foreach (@tables) {
            $_ =~ s/^`(.*)`$/$1/g;
            $_ =~ s/^"(.*)"$/$1/g;
            if (/^verse$/) {
                my $query = "SELECT * FROM verse WHERE book=\"Bible\";";
                qdebug($query);
                my $sth = $db->prepare($query)
                  || display_fatal($errorcodes->{'sqlprepare'}, $!);
                my $rv = $sth->execute
                  || display_fatal($errorcodes->{'sqlexecute'}, $!);
                my $row = $sth->fetchrow_hashref();
                if (defined $row->{'verse'}) {
                    $bibles->{$dbname} = "db;" . $row->{'verse'};
                }
                $sth->finish;
            }
        }
        $db->disconnect();
    }

    return $bibles;
}

sub navigator_changed {
    debug("Navigator changed");
    reset_timer($globals->{'nav_update_timer'});
    $globals->{'nav_update_timer'} =
      Glib::Timeout->add(500, \&navigator_update);
}

sub navigator_update {
    debug("Updating navigator");
    reset_timer($globals->{'nav_update_timer'});
    my $buffer = Gtk2::TextBuffer->new();
    $buffer->set_text("");
    my $iter  = $buffer->get_iter_at_offset(0);
    my $verse = $widgets->{'main'}->get_widget('entryNavVerse')->get_text();
    $verse =~ s/ (\D)/_$1/;
    my ($book, $chapter, $startverse, $endverse) =
      split(/[ :\-,]/, $verse, 4);
    $book =~ s/_/ /g;

    if ((!defined $chapter) or ($chapter eq "") or ($chapter =~ /\D/)) {
        $chapter = 1;
    }
    if (   (!defined $startverse)
        or ($startverse eq "")
        or ($startverse =~ /\D/))
    {
        $startverse = 1;
    }
    if (   (!defined $endverse)
        or ($endverse eq "")
        or ($endverse =~ /\D/)
        or ($endverse < $startverse))
    {
        $endverse = $startverse;
    }
    debug(  "Book " . $book
          . " chapter "
          . $chapter
          . " verses "
          . $startverse . "-"
          . $endverse);
    if ($globals->{'usesword'}) {
        my $command = sprintf(
            "%s -b %s -e UTF8 -k '%s' %d:%d-%d |tr \\\\n \' \'",
            $globals->{'diatheke'},
            $globals->{'bibledb'}, $book, $chapter, $startverse, $endverse,
            $book
        );
        qdebug($command);
        $book = ucfirst($book . "[:alpha:]*");
        my $command_out = Encode::decode("utf-8", `$command`);
        ($book, undef) = split(/\s\d/, $command_out, 2);
        my @command_lines = split(/$book /, $command_out);
        my $mark = 0;
        foreach (@command_lines) {
            chomp;
            my $line2 = $_;
            if ($line2 ne "") {
                $buffer->create_mark($mark, $iter, TRUE);
                insert_link($buffer, $iter, $mark, $line2 . "\n");
                $mark++;
            }
        }
        $buffer->create_mark($mark, $iter, TRUE);

        # remove the trailing bible name
        #$quicktext =~ s/\($globals->{'bibledb'}\)/\n/g;
    } else {
        $query = "SELECT book FROM verse WHERE book LIKE \"" . $book . "%\"";
        qdebug($query);
        $sth = $bibleDbh->prepare($query)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);
        $rv = $sth->execute
          || display_fatal($errorcodes->{'sqlexecute'}, $!);
        my @bookrow = $sth->fetchrow_array();
        if ((defined $bookrow[0]) && ($book ne "")) {
            $book  = $bookrow[0];
            $query =
"SELECT book,chapternum,versenum,verse FROM  verse WHERE book LIKE \""
              . $book
              . "%\" AND chapternum="
              . $chapter
              . " AND versenum>="
              . $startverse
              . " AND versenum <= "
              . $endverse
              . " ORDER BY versenum";
            qdebug($query);
            $sth = $bibleDbh->prepare($query)
              || display_fatal($errorcodes->{'sqlprepare'}, $!);
            $rv = $sth->execute
              || display_fatal($errorcodes->{'sqlexecute'}, $!);
            my $mark = 0;
            while (my @row = $sth->fetchrow_array()) {
                my $verse = $row[1] . ":" . $row[2] . " " . $row[3] . "\n";
                $buffer->create_mark($mark, $iter, TRUE);
                insert_link($buffer, $iter, $mark, $verse);
                $mark++;
            }
            $buffer->create_mark($mark, $iter, TRUE);
        }
    }

    $widgets->{'main'}->get_widget('entryNavVerse')->{user_data} =
      $book . " " . $chapter . ":" . $startverse . "-" . $endverse;
    my $oldtext =
      get_buffer_text(
        $widgets->{'main'}->get_widget('textNavView')->get_buffer());
    my $quicktext = get_buffer_text($buffer);
    if ($oldtext ne $quicktext) {
        $widgets->{'main'}->get_widget('textNavView')->set_buffer($buffer);
        if ($widgets->{'main'}->get_widget('checkNavAuto')->get_active()) {
            navigator_show();
        }
    }
}

sub navigator_enter {
    navigator_update();
    navigator_show();
}

sub navigator_show {
    debug("Bible navigator showing on server");
    my $buffer = $widgets->{'main'}->get_widget('textNavView')->get_buffer();
    my $songtext = $buffer->get_text($buffer->get_bounds, FALSE);
    $songtext =~ s/\n/#BREAK#/g;
    $songtext =~ s/:/#SEMI#/g;

    my $verse =
      $widgets->{'main'}->get_widget('entryNavVerse')->{user_data}
      . "#BREAK##BREAK##BREAK#wrap";

    update_display("preview", $verse, $songtext);
}

sub navigator_add {
    debug("Adding verses from Bible navigator to playlist");
    my $verse = $widgets->{'main'}->get_widget('entryNavVerse')->{user_data};
    $verse =~ s/ (\D)/_$1/;
    my ($book, $chapter, $startverse, $endverse) =
      split(/[ :\-,]/, $verse, 4);
    $book =~ s/_/ /g;
    insert_verse($book, $chapter, $startverse, $endverse);
}

sub navigator_next {
    debug("Showing next verse");
    my $verse = $widgets->{'main'}->get_widget('entryNavVerse')->{user_data};
    $verse =~ s/ (\D)/_$1/;
    my ($book, $chapter, $startverse, $endverse) =
      split(/[ :\-,]/, $verse, 4);
    $book =~ s/_/ /g;
    my $loop = TRUE;
    $endverse++;
    while ($loop) {
        my $maxv = get_max_verse($book, $chapter, $startverse, $endverse);
        if ($maxv < $endverse) {
            $startverse++;
        } else {
            $loop = FALSE;
        }
    }
    $verse = $book . " " . $chapter . ":" . $startverse . "-" . $endverse;
    $widgets->{'main'}->get_widget('entryNavVerse')->set_text($verse);
    navigator_update();
    navigator_show();
}

sub navigator_prev {
    debug("Showing next verse");
    my $verse = $widgets->{'main'}->get_widget('entryNavVerse')->{user_data};
    $verse =~ s/ (\D)/_$1/;
    my ($book, $chapter, $startverse, $endverse) =
      split(/[ :\-,]/, $verse, 4);
    $book =~ s/_/ /g;
    my $loop = TRUE;
    $startverse--;
    $endverse = get_max_verse($book, $chapter, $startverse, $endverse);
    $verse = $book . " " . $chapter . ":" . $startverse . "-" . $endverse;
    $widgets->{'main'}->get_widget('entryNavVerse')->set_text($verse);
    navigator_update();
    navigator_show();
}

sub reset_timer {
    my ($timer) = @_;
    if ($timer) {
        debug("Cancelling timer");
        Glib::Source->remove($timer);
        $timer = FALSE;
    }
}

sub do_pending {
    while (Gtk2->events_pending) {
        Gtk2->main_iteration;
    }
}

sub get_max_verse {
    my ($book, $chapter, $startverse, $endverse) = @_;

    if ($config->{'Main'} eq "") {
        $config = load_config();
    }
    if ($config->{'Width'} == 0) {
        $config->{'Width'}  = 800;
        $config->{'Height'} = 600;
    }

    my $pageHeight = 0;

    my $layout =
      $widgets->{'main'}->get_widget('windowMain')->create_pango_layout("");
    $layout->set_wrap('word');
    $layout->set_width($config->{'Width'} * PANGO_SCALE);
    $layout->set_font_description(
        Gtk2::Pango::FontDescription->from_string($config->{'Main'}));
    my $page = "";
    my (@command_out);

    if (!$globals->{'usesword'}) {
        $query =
            "SELECT * FROM  verse WHERE book=\"" . $book
          . "\" AND chapternum="
          . $chapter
          . " AND versenum>="
          . $startverse
          . " AND versenum <= "
          . $endverse
          . " ORDER BY versenum";
        qdebug($query);
        $sth = $bibleDbh->prepare($query)
          || display_fatal($errorcodes->{'sqlprepare'}, $!);

        $rv = $sth->execute
          || display_fatal($errorcodes->{'sqlexecute'}, $!);
    }

    my $loop     = TRUE;
    my $versenum = $startverse;
    while ($loop) {
        my $line = "";
        if ($globals->{'usesword'}) {
            my $command = sprintf(
"%s -b %s -e UTF8 -k '%s' %d:%d-%d | sed -e 's/^%s //g' | head -n -1",
                $globals->{'diatheke'},
                $globals->{'bibledb'}, $book, $chapter, $startverse, $versenum,
                $book
            );
            qdebug($command);
            $line = Encode::decode("utf-8", `$command`);
            $versenum++;
            if ($versenum > $endverse - 1) { $loop = FALSE; }
            chomp($line);
        } else {
            if (!defined $line) { $loop = FALSE; }
            if ($row = $sth->fetchrow_hashref()) {
                $line =
                    $row->{'chapternum'} . ":"
                  . $row->{'versenum'} . "   "
                  . $row->{'verse'};
                $versenum = $row->{'versenum'};
            } else {
                return $versenum;
            }
        }

        $layout->set_text($page . "\n" . $line);
        my ($pageWidth, $pageHeight) = $layout->get_pixel_size;

        if ($pageHeight > $config->{'Height'} - 100) {
            if ($versenum > $startverse) { $versenum--; }
            return $versenum;
        } else {
            $page = $page . "\n" . $line;
        }
    }
    return $versenum;
}

# Inserts a piece of text into the buffer, giving it the usual
# appearance of a hyperlink in a web browser: blue and underlined.
# Additionally, attaches some data on the tag, to make it recognizable
# as a link.
#
sub insert_link {
    my ($buffer, $iter, $mark, $verse) = @_;
    my $tag = $buffer->create_tag(
        undef,
        foreground => "blue",
        underline  => 'single'
    );
    $tag->{markname} = $mark;
    $buffer->create_mark($mark, $iter, TRUE);
    my ($chap, $num, $text) = split(/[:\ ]/, $verse, 3);
    $verse = $chap . ":" . $num;
    $text  = " " . $text;
    $buffer->insert_with_tags($iter, $verse, $tag);
    $buffer->insert($iter, $text);
}

# Looks at all tags covering the position of iter in the text view,
# and if one of them is a link, follow it by showing the page identified
# by the data attached to it.
#
sub follow_if_link {
    my ($text_view, $iter) = @_;
    my $verse = $widgets->{'main'}->get_widget('entryNavVerse')->{user_data};
    $verse =~ s/ (\D)/_$1/;
    my ($book, $chapter, $startverse, $endverse) =
      split(/[ :\-,]/, $verse, 4);
    $book =~ s/_/ /g;
    foreach my $tag ($iter->get_tags) {
        my $buffer    = $text_view->get_buffer();
        my $mark      = $buffer->get_mark($tag->{markname});
        my $startiter = $buffer->get_iter_at_mark($mark);
        my $enditer   =
          $buffer->get_iter_at_mark($buffer->get_mark($tag->{markname} + 1));
        my $quicktext = $buffer->get_text($startiter, $enditer, FALSE);
        if ($verse ne "") {
            debug("Showing $book $verse");
            debug($quicktext);
            last;
        }
    }
}

sub navigator_event_after {
    my ($text_view, $event) = @_;
    debug("Navigator text clicked");
    return FALSE unless $event->type eq 'button-release';
    return FALSE unless $event->button == 1;
    my $buffer = $text_view->get_buffer;

    # we shouldn't follow a link if the user has selected something
    my ($start, $end) = $buffer->get_selection_bounds;
    return FALSE if defined $end and $start->get_offset != $end->get_offset;
    my ($x, $y) =
      $text_view->window_to_buffer_coords('widget', $event->x, $event->y);
    my $iter = $text_view->get_iter_at_location($x, $y);
    follow_if_link($text_view, $iter);
    return FALSE;
}

sub text_set_cursor_if_appropriate {
    my ($text_view, $x, $y) = @_;
    $globals->{'hovering'} = FALSE;
    my $buffer = $text_view->get_buffer;
    my $iter = $text_view->get_iter_at_location($x, $y);
    foreach my $tag ($iter->get_tags) {
        if (defined $tag->{markname}) {
            $globals->{'hovering'} = TRUE;
            last;
        }
    }

    if ($globals->{'hovering'} != $globals->{'hovering_over_link'}) {
        $globals->{'hovering_over_link'} = $globals->{'hovering'};
        $text_view->get_window('text')->set_cursor(
              $globals->{'hovering_over_link'}
            ? $globals->{'hand_cursor'}
            : $globals->{'text_cursor'}
        );
    }
}

# Update the cursor image if the pointer moved.
#
sub text_motion_notify_event {
    my ($text_view, $event) = @_;
    my ($x,         $y)     =
      $text_view->window_to_buffer_coords('widget', $event->x, $event->y);
    text_set_cursor_if_appropriate($text_view, $x, $y);
    $text_view->window->get_pointer;
    return FALSE;
}

# Also update the cursor image if the window becomes visible
# (e.g. when a window covering it got iconified).
#
sub text_visibility_notify_event {
    my ($text_view, $event) = @_;
    my (undef, $wx, $wy, undef) = $text_view->window->get_pointer;
    my ($bx, $by) = $text_view->window_to_buffer_coords('widget', $wx, $wy);
    text_set_cursor_if_appropriate($text_view, $bx, $by);
    return FALSE;
}

sub import_song_from_file {
    my ($filename) = @_;
    import_song_opw($filename);
}

sub import_song_songselect {
    my ($filename) = @_;
    debug("import songselect song");
    open(SONG, $filename) || return;
    my $hashnum = 0;

    while (<SONG>) {
        $_ =~ s/
//g;
        chomp;
        my @line = split(/=/, $_, 2);
        $_ = $line[0];
        if (/Title/) {
            $widgets->{'add'}->get_widget('entryEditName')->set_text($line[1]);
        } elsif (/Author/) {
            $widgets->{'add'}->get_widget('entryEditArtist')
              ->set_text($line[1]);
        } elsif (/Copyright/) {
            $widgets->{'add'}->get_widget('entryEditCopyright')
              ->set_text($line[1]);
        } elsif (/Themes/) {
            $line[1] =~ s/\/t/ /g;
            $widgets->{'add'}->get_widget('entryEditKeywords')
              ->set_text($line[1]);
        } elsif (/Words/) {
            my @words = split(/\/t/, $line[1]);
            foreach (@words) {
                if ($_ ne "") {
                    $_ =~ s/\/n/\n/g;
                    chomp;
                    if ($hashnum == 0) {
                        $widgets->{'textAPageB'}{$hashnum}->set_text($_);
                        $hashnum++;
                    } else {
                        $hashnum = add_page();
                        $widgets->{'textAPageB'}{$hashnum}->set_text($_);
                    }
                }
            }
        }
    }
    close SONG;

}

sub import_song_opw {
    my ($filename) = @_;
    debug("import opw song");
    my $input   = "";
    my $hashnum = 0;

    open(OPW, $filename) || die("Unable to open $filename");
    while (<OPW>) {
        $input .= Encode::decode("cp-1252",$_);
    }
    close OPW;

    $input =~ /bundel>(.*)<\/bundel/;
    $widgets->{'add'}->get_widget('entryEditBook')->set_text($1);

    $input =~ /nummer>(.*)<\/nummer/;
    $widgets->{'add'}->get_widget('entryEditNumber')->set_text($1);

    $input =~ /titel>(.*)<\/titel/;
    $widgets->{'add'}->get_widget('entryEditName')->set_text($1);

    $input =~ /copyrights>(.*)<\/copyrights/;
    $widgets->{'add'}->get_widget('entryEditCopyright')->set_text($1);

    $input =~ /beginregel>(.*)<\/beginregel/;
    $widgets->{'add'}->get_widget('entryEditKeywords')->set_text($1);

    $input =~ /tekst>(.*)<\/tekst/s;
    my $lyrics = $1;
    $lyrics =~ s/
//g;
    my @lyrics = split(/\n \n/, $lyrics);

    foreach (@lyrics) {
        if ($_ ne "") {
            chomp;
            $_ =~ s/ *$//g;
            $_ =~ s/^[0-9]*\. //g;
            if ($hashnum == 0) {
                $widgets->{'textAPageB'}{$hashnum}->set_text($_);
                $hashnum++;
            } else {
                $hashnum = add_page();
                $widgets->{'textAPageB'}{$hashnum}->set_text($_);
            }
        }
    }
}

####
# Install database functions
####
sub db_check_app {
    debug("Checking for database servers");
    if (defined $globals->{'force_sqlite'} && ($globals->{'force_sqlite'})) {
        debug("Forcing usage of SQLite3");
        $config->{'DatabaseType'} = "SQLite";
        return;
    }
    my @ary    = DBI->available_drivers(1);
    my $mysql  = FALSE;
    my $sqlite = FALSE;
    foreach (@ary) {
        if ($_ eq "mysql") {
            $mysql = TRUE;
        } elsif ($_ eq "SQLite") {
            $sqlite = TRUE;
        }
    }
    if ($mysql) {
        $config->{'DatabaseType'} = "mysql";
    } elsif ($sqlite) {
        $config->{'DatabaseType'} = "SQLite";
    } else {
        die("No supported DB found");
    }
}

sub db_get_admin {
    debug("Get the db admin login information");
    if ($config->{'DatabaseType'} eq "mysql") {
        my $adminxml = Gtk2::GladeXML->new($globals->{'gladefile'},
            'dialogAdminLogin', 'lyricue');
        $adminxml->signal_autoconnect_from_package('');
        my $confirm = $adminxml->get_widget('dialogAdminLogin')->run();
        $globals->{'db_adminuser'} =
          $adminxml->get_widget('entryAdminLogin')->get_text();
        $globals->{'db_adminpassword'} =
          $adminxml->get_widget('entryAdminPass')->get_text();
        close_dialog($adminxml->get_widget('dialogAdminLogin'));
    } else {
        $globals->{'db_adminuser'}     = "lyricue";
        $globals->{'db_adminpassword'} = "";

    }
}

sub db_install_user {
    debug("Install lyric database user");
    my ($dbh);
    db_get_admin();
    if ($config->{'DatabaseType'} eq "mysql") {
        eval {
            $dbh = DBI->connect(
                "DBI:"
                  . $config->{'DatabaseType'}
                  . ":mysql:"
                  . $globals->{'mysqlhost'},
                $globals->{'db_adminuser'}, $globals->{'db_adminpassword'}
            );
        };
        if ($@) {
            my $labelText = Encode::decode(
                "utf-8",
                gettext(
"Unable to login to database as admin, maybe the database is down.\nPlease re-enter your database admin login and retry"
                )
            );
            my $loginxml = Gtk2::GladeXML->new($globals->{'gladefile'},
                'dialogConfirm', 'lyricue');
            $loginxml->signal_autoconnect_from_package('');
            $loginxml->get_widget('dialogConfirm')
              ->set_title(Encode::decode("utf-8", gettext("Login Error")));
            $loginxml->get_widget('labelDelete')->set_text($labelText);
            my $confirm = $loginxml->get_widget('dialogConfirm')->run();
            if ($confirm eq "ok") {
                close_dialog($loginxml->get_widget('dialogConfirm'));
                db_install_user();
                return;
            } else {
                display_fatal($errorcodes->{'lyricdbopen'}, $DBI::errstr);
            }
        }
    }
    my $sth = $dbh->prepare("select * from user where User='lyric'");
    my $rv  = $sth->execute;
    db_reload();
    if ($sth->rows) {
        debug("User already setup\n");
    } else {
        debug("Creating mysql user..");
        $sth =
          $dbh->prepare(
"insert into user set Host='%',User='lyric',Password='',Select_priv='Y',Insert_priv='Y', Update_priv='Y',Delete_priv='Y'"
          );
        $sth->execute;
        $sth =
          $dbh->prepare(
"insert into user set Host='localhost',User='lyric',Password='',Select_priv='Y',Insert_priv='Y', Update_priv='Y',Delete_priv='Y'"
          );
        $sth->execute;
        $sth = $dbh->prepare("flush privileges");
        $sth->execute;
        debug("Done\n");
    }
    db_check_databases();
}

sub db_check_databases {
    debug("Update/install databases");
    if ($config->{'DatabaseType'} eq "mysql") {
        my @dbs = DBI->data_sources($config->{'DatabaseType'},
            {"host" => $globals->{'mysqlhost'}, "user" => "lyric"});

        foreach (@dbs) {
            $_ =~ s/^DBI:.*://g;
            $globals->{'db_available_db'}{$_} = TRUE;
        }
    } else {
        opendir(DIR, $globals->{'basedir'});
        while ($_ = readdir(DIR)) {
            if (/.db$/) {
                $_ =~ s/.db$//g;
                $globals->{'db_available_db'}{$_} = TRUE;
            }
        }
    }

    if ($globals->{'db_available_db'}{'lyricDb'}) {
        db_updatedb_lyricDb();
    } else {
        db_installdb($globals->{'sharedir'} . "mysql/MySQL_create_Table.sql",
            "lyricDb");
    }

    if ($globals->{'db_available_db'}{'mediaDb'}) {
        db_updatedb_mediaDb();
    } else {
        debug("Creating mediaDb");
        db_installdb($globals->{'sharedir'} . "mysql/MySQL_create_media.sql",
            "mediaDb");
        db_reload();
        debug("Importing existing backgrounds/images\n");

        # NOTE FAILS WITH SQLITE
        if ($config->{'DatabaseType'} eq "mysql") {
            system("import_media img " . $globals->{'sharedir'} . "images");
            system("import_media bg " . $globals->{'sharedir'} . "backgrounds");
        }
        debug("Done\n");
    }
}

sub db_reload {
    debug("Reload db");
    if ($globals->{'db_adminuser'} eq "") {
        db_get_admin();
    }
    if ($config->{'DatabaseType'} eq "mysql") {
        my $drh = DBI->install_driver('mysql');
        my $rc  = $drh->func(
            "reload",
            [
                $globals->{'mysqlhost'}, $globals->{'db_adminuser'},
                $globals->{'adminpassword'},
            ],
            'admin'
        );
    }
}

sub db_installdb {
    my ($db_file, $db_name) = @_;
    debug("Install db from " . $db_file);
    if ($globals->{'db_adminuser'} eq "") {
        db_get_admin();
    }
    if ($config->{'DatabaseType'} eq "mysql") {
        system( "cat " . $db_file
              . " | mysql -h "
              . $globals->{'mysqlhost'} . " -u "
              . $globals->{'db_adminuser'}
              . " --password="
              . $globals->{'db_adminpassword'});
    } else {
        my $dbh = db_connect($db_name, "");
        open(DB, $db_file);
        my $query = "";
        while (<DB>) {
            $_ =~ s/^CREATE DATABASE*;$//g;
            $_ =~ s/^USE *;$//g;
            if (   (/^ *UNIQUE KEY/)
                || (/^--/)
                || (/^CREATE DATABASE /)
                || (/^USE /))
            {
                $_ = "";
            }
            $_ =~ s/(PRIMARY KEY .*),$/$1/g;
            $_ =~ s/auto_increment,$/,/g;
            $_ =~ s/TYPE=MyISAM;$/;/g;
            $_ =~ s/unsigned NOT NULL/NOT NULL/g;
            $_ =~ s/int(11)/INTEGER/g;
            $_ =~ s/\\'/''/g;
            $_ =~ s/\\n/\n/g;

            if (!/;$/) {
                $query .= $_;
            } else {
                $query .= $_;
                if ($query ne "") {

                    #print $query ."\n";
                    $dbh->do($query);
                }
                $query = "";
            }
        }
        $dbh->disconnect();
    }
}

sub db_updatedb_mediaDb {
    debug("Update the mediaDb table if needed");
    my $dbh = db_connect($globals->{'mediadb'}, "");
    if ($config->{'DatabaseType'} eq "mysql") {
        my $fields = $dbh->selectall_arrayref("describe media");
        my $trans;
        foreach (@$fields) {
            $trans->{$_->[0]} = 1;
        }
        if (!defined $trans->{'textcolour'}) {
            debug("Text Colouring fields not found\n");
            debug("Upgrading database from 1.9 to 1.9.4\n");
            db_installdb($globals->{'sharedir'} . "mysql/Update_1.9.4.sql",
                "lyricDb");
            debug("Done\n");
        }
    }
}

sub db_updatedb_lyricDb {
    debug("Update the lyricDb table if needed");
    my $dbh = db_connect($globals->{'lyricdb'}, "");
    my @tables = $dbh->tables;
    my $table;
    foreach (@tables) {
        $_ =~ s/^`(.*)`$/$1/g;
        $_ =~ s/^"(.*)"$/$1/g;
        $table->{$_} = 1;
    }
    if (!defined $table->{'associations'}) {
        debug("Associations table not found\n");
        debug("Upgrading database from < 1.2 to 1.2\n");
        db_installdb($globals->{'sharedir'} . "mysql/Update_1.2.sql",
            "lyricDb");
        debug("Done\n");
    }
    if ($config->{'DatabaseType'} eq "mysql") {
        my $fields = $dbh->selectall_arrayref("describe playlist");
        my $trans;
        foreach (@$fields) {
            $trans->{$_->[0]} = 1;
        }
        if (!defined $trans->{'transition'}) {
            debug("Transition field not found\n");
            debug("Upgrading database from 1.2 to 1.9\n");
            db_installdb($globals->{'sharedir'} . "mysql/Update_1.9.sql",
                "lyricDb");
            debug("Done\n");
        }
    }
}

sub check_tracker {

    #debug("Check tracker");
    my $query = "SELECT ref FROM playlists WHERE id=-1";

    #qdebug($query); # Commented out because it's too noisy
    my $sth = $lyricDbh->prepare($query)
      || display_fatal($errorcodes->{'sqlprepare'}, $!);
    my $rv = $sth->execute
      || display_fatal($errorcodes->{'sqlexecute'}, $!);
    my @row = $sth->fetchrow_array();

    #Mark the row
    if (@row && ($row[0] ne $globals->{'current_item'})) {
        $globals->{'previous_item'} = $globals->{'current_item'};
        $globals->{'current_item'}  = $row[0];
        my $model = $widgets->{'main'}->get_widget('treePlaylist')->get_model();
        if ($model) {
            $globals->{'current_item_path'} = "";
            $model->foreach(\&update_current);
            if ($globals->{'current_item_path'} ne "") {
                my $path =
                  Gtk2::TreePath->new_from_string(
                    $globals->{'current_item_path'});
                while ($path->up()) {
                    if ($path->get_depth() > 0) {
                        my $iter = $model->get_iter($path);
                        if ($iter) {
                            $model->set($iter, 1, $config->{'HighlightColour'});
                        }
                    }
                }
            }
        }
    }
    return TRUE;
}

sub update_current {
    my ($store, $path, $iter) = @_;
    my $playlistid = $store->get($iter, 2);
    if ($playlistid eq $globals->{'current_item'}) {
        $store->set($iter, 1, $config->{'HighlightColour'});
        $globals->{'current_item_path'} = $path->to_string;
    } else {
        if ($globals->{'current_item_path'} ne "") {
            if (
                !$path->is_ancestor(
                    Gtk2::TreePath->new_from_string(
                        $globals->{'current_item_path'}
                    )
                )
              )
            {
                $store->set($iter, 1, undef);
            }
        } else {
            $store->set($iter, 1, undef);
        }
    }
    return FALSE;
}

sub clear_search {
    debug("Clear search entry");
    $widgets->{'main'}->get_widget('entrySearch')->set_text("");
    $widgets->{'main'}->get_widget('entrySearch')->grab_focus();
}

sub db_connect {
    my ($dbname, $dberror) = @_;
    my ($dbh);
    if ($config->{'DatabaseType'} eq "SQLite") {
        $dbh =
          DBI->connect("dbi:SQLite:" . $globals->{'basedir'} . $dbname . ".db",
            "", "")
          || display_fatal($dberror, $DBI::errstr);
    } else {
        $dbh = DBI->connect(
            "DBI:"
              . $config->{'DatabaseType'}
              . ":database=$dbname;host=$globals->{'mysqlhost'}",
            "lyric", ""
        ) || display_fatal($dberror, $DBI::errstr);
    }
    return $dbh;
}

sub prefs_select_mysql {
    if ($config->{'DatabaseType'} ne "mysql") {
        debug("Selecting MySql DB");
        $config->{'DatabaseType'} = "mysql";
        db_restart();
    }
}

sub prefs_select_sqlite {
    if ($config->{'DatabaseType'} ne "SQLite") {
        debug("Selecting SQLite DB");
        $config->{'DatabaseType'} = "SQLite";
        db_restart();
    }
}

sub db_restart {
    debug("Reconnecting to DBs");
    $lyricDbh->disconnect();
    $mediaDbh->disconnect();
    db_select();
    if (defined $config->{'DefBible'} && ($config->{'DefBible'} ne "")) {
        my @tmpbible = split(/;/, $config->{'DefBible'}, 2);
        $globals->{'biblename'} = $tmpbible[1];
        @tmpbible = split(/:/, $tmpbible[0], 2);
        do_change_bible($tmpbible[1], $tmpbible[0]);
    }
    choose_playlist();
}

sub db_select {

    # Open lyricDB, bibleDB and mediaDb
    if ($config->{'DatabaseType'} eq "mysql") {
        my $sqldb = DBI->connect(
            "DBI:" . $config->{'DatabaseType'} . ":mysql:" . $globals->{'mysqlhost'},
            "lyric", ""
        ) || db_install_user();
        if ($sqldb) {
            $sqldb->disconnect();
        }
    }
    db_check_databases();
    $lyricDbh = db_connect($globals->{'lyricdb'}, $errorcodes->{'lyricdbopen'});
    if ($config->{'DatabaseType'} eq "SQLite") {

        # Define a NOW command in sqlite
        $lyricDbh->func('NOW', 0, sub { return time }, 'create_function');
    }
    $mediaDbh = db_connect($globals->{'mediadb'}, $errorcodes->{'mediadbopen'});
    $config->{'Bibles'} = get_bibles();
}

sub add_to_playlist_search {
    my ($widget) = @_;
    my $selection =
      $widgets->{'search'}->get_widget('treeSearch')->get_selection;
    my ($model, $iter) = $selection->get_selected;
    if ($iter) {
        my $songid = $model->get($iter, 3);
        debug("Songid \"" . $songid . "\" selected");
        add_single_song($songid);
        update_playlist();
    }
}

