#!/usr/bin/env perl
my $ID = q$Id: lyricue,v 1.403 2010/08/09 05:01:05 cjdebenh Exp $;

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

=head1 NAME

lyricue

=head1 SYNOPSIS

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

=head1 DESCRIPTION

This application is used to edit/display song lyrics on a second screen/projector for use at singing events such as church services.

=head1 OPTIONS

=over 4

=item B<-v>

Get lyricue version

=item B<-d>

Turn on debugging mode. Prints out debugging information. 
Use -dq to enable SQL debugging as well

=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

=item B<-r>

Set server to connect to for MySQL

=item B<-w>

Do not maximize window on startup

=item B<-i>

Import song list from a file

=back

=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, Clutter-perl, Gstreamer

=head1 SEE ALSO

lyricue_server, lyricue_remote

=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 3.7.1-1

=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 Time::HiRes qw(tv_interval gettimeofday);
use warnings;
use threads;
use threads::shared;
use DBI;
use POSIX;
use locale;
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::Pango;
use Gtk2::Gdk::Keysyms;
use URI::file;
use XML::Simple;
use Gnome2;
use File::Temp qw/tempdir tempfile/;
use File::MimeInfo qw/globs/;
use Cwd qw(abs_path);
use File::Basename;
use Sys::Hostname;

eval { require Locale::gettext };

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

    sub gettext {
        return $_[0];
    }
} else {
    import Locale::gettext;
    textdomain('lyricue');
    bind_textdomain_codeset('lyricue', "UTF-8");
}
binmode(STDOUT, ":utf8");
binmode(STDERR, ":utf8");

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

my %lsdvd;

$globals->{'execdir'} = dirname(abs_path($0));

if ($^O eq 'MSWin32') {
    $globals->{'etcdir'}   = "etc/lyricue/";
    $globals->{'basedir'}  = ".lyricue/";
    $globals->{'sharedir'} = "";
} else {
    $globals->{'etcdir'} =
      abs_path($globals->{'execdir'} . "/../../etc/lyricue/");
    if ((!defined $globals->{'etcdir'}) || (!-d "$globals->{'etcdir'}")) {
        $globals->{'etcdir'} =
          abs_path($globals->{'execdir'} . "/../etc/lyricue/");
        if ((!defined $globals->{'etcdir'}) || (!-d "$globals->{'etcdir'}")) {
            $globals->{'etcdir'} = "/etc/lyricue";
        }
    }
    $globals->{'etcdir'} = $globals->{'etcdir'} . "/";
    $globals->{'sharedir'} =
      abs_path($globals->{'execdir'} . "/../share/lyricue/");
    if ((!defined $globals->{'sharedir'}) || (!-d $globals->{'sharedir'})) {
        $globals->{'sharedir'} = "/usr/share/lyricue";
    }
    $globals->{'sharedir'} = $globals->{'sharedir'} . "/";
    $globals->{'basedir'}  = Glib::get_user_data_dir . "/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;
use constant GONE  => 2;

$globals->{'version'}    = "3.7.1-1";
$globals->{'accessfile'} = $globals->{'etcdir'} . "access.conf";
$globals->{'gladefile'}  = $globals->{'sharedir'} . "lyricue.glade";

$globals->{'profile'}     = "";
$globals->{'mysqlhost'}   = "localhost";
$globals->{'lyricdb'}     = "lyricDb";
$globals->{'bibledb'}     = "";
$globals->{'biblename'}   = "";
$globals->{'category'}    = "";
$globals->{'access'}      = "";
$globals->{'usesword'}    = TRUE;
$globals->{'mediadb'}     = "mediaDb";
$globals->{'sortby'}      = "title";
$globals->{'bg_previews'} = TRUE;
my (%previews, %miniviews, %displays, $local_preview) :shared;
if ($^O eq 'MSWin32') {
    $globals->{'diatheke'} = "";
} else {
    $globals->{'diatheke'} = `which diatheke`;
    chomp $globals->{'diatheke'};
}
$globals->{'unoconv'} = `which unoconv`;
chomp $globals->{'unoconv'};
$globals->{'convert'} = `which convert`;
chomp $globals->{'convert'};
$globals->{'video-thumbnailer'} = `which totem-video-thumbnailer`;
if ($globals->{'video-thumbnailer'} eq "") {
    $globals->{'video-thumbnailer'} = `which totem-gstreamer-thumbnailer`;
}
if ($globals->{'video-thumbnailer'} eq "") {
    $globals->{'video-thumbnailer'} = `which totem-video-thumbnailer`;
}
chomp $globals->{'video-thumbnailer'};
$globals->{'lyricue_server'} = `which lyricue_display`;
if ($globals->{'lyricue_server'} eq "") {
    $globals->{'lyricue_server'} = `which lyricue_server`;
}
chomp $globals->{'lyricue_server'};

$globals->{'preview_port'}       = "2347";    #port used for preview
$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->{'hovering_over_link'} = FALSE;
$globals->{'db_adminuser'}       = "";
$globals->{'icon_width'}         = 96;
$globals->{'thumb_width'}        = 128;
$globals->{'snapshot_width'}     = 256;
$globals->{'firstrun'}           = FALSE;
$globals->{'image_playlist'}     = FALSE;
$globals->{'local_hostname'}     = hostname();
$globals->{'preview_starting'}   = 0;

# Optional modules
eval { require Gtk2::Spell };
if ($@) {
    print STDERR "Gtk-Spell not available, spell checking turned off\n";
    $globals->{'spell'} = FALSE;
} else {
    import Gtk2::Spell;
}
eval { require Gtk2::TrayIcon };
if ($@) {
    print STDERR "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 DEFAULT     => 0;
use constant NOTRANS     => 1;
use constant FADE        => 2;
use constant SLIDE_TEXT  => 3;
use constant ROTATE_TEXT => 4;
use constant RANDOM      => 5;

# Transition directions
use constant NONE      => 0;
use constant WAIT      => 2**0;
use constant UP        => 2**1;
use constant DOWN      => 2**2;
use constant RIGHT     => 2**3;
use constant LEFT      => 2**4;
use constant X_AXIS    => 2**5;
use constant Y_AXIS    => 2**6;
use constant Z_AXIS    => 2**7;
use constant NUM_TRANS => 8;

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

my ($errorcodes);
$errorcodes->{'lyricdbopen'} = fromutf(
    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'} = fromutf(
    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'} = fromutf(
    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'} =
  fromutf(gettext("Unable to prepare query.\nHas mysql died?"));
$errorcodes->{'sqlexecute'} =
  fromutf(gettext("Unable to execute query.\nHas mysql died?"));
$errorcodes->{'socketopen'} = fromutf(
    gettext(
"Sorry, I was unable to listen on the network.\nPlease make sure I am not already running"
    )
);
$errorcodes->{'erroropen'}    = fromutf(gettext("Could not open "));
$errorcodes->{'fileopenread'} = fromutf(
    gettext(
"Unable to read the file, are you sure it exists?\nThe file asked for was "
    )
);
$errorcodes->{'fileopenwrite'} = fromutf(
    gettext(
"Unable to write to the file, you may not have sufficent permissions.\nPlease check the permissions for "
    )
);
$errorcodes->{'usage'} = fromutf(
    gettext(
            "\nUsage: lyricue <-v|-l> <-b> <-k> <-d> <-p>\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-p:  Specify the profile to use\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-i:  Import songlist from file\n\n"
    )
);

$errorcodes->{'nobible'} =
  fromutf(
    gettext("No bible has been selected\n" . "Please select one from the menu")
  );

# 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",
    "buttonMainReshow"
);

# 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 "Lyricue Interface version " . $globals->{'version'} . "\n";
            exit;
        } elsif ($ARGV[$_] eq "-l") {
            $globals->{'print_songs'} = TRUE;
        } elsif ($ARGV[$_] eq "-b") {
            $globals->{'bg_previews'} = FALSE;
        } elsif ($ARGV[$_] eq "-d") {
            $globals->{'debugging'} = 1;
        } elsif ($ARGV[$_] eq "-dq") {
            $globals->{'debugging'} = 2;
        } elsif ($ARGV[$_] eq "-w") {
            $globals->{'run_windowed'} = TRUE;
        } elsif ($ARGV[$_] eq "-p") {
            $globals->{'profile'} = $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 "") {

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

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

# Set umask
umask 0002;

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

# Setup logging
my $logname = $globals->{'basedir'} . "/frontend.log";
rename($logname, $logname . ".old")
  or system("mv", $logname, $logname . ".old");
open(LOG, ">" . $logname);
binmode LOG, ":utf8";
debug("Lyricue Interface version " . $globals->{'version'} . "\n" . $ID);
debug("Running from " . $globals->{'execdir'});
$widgets->{'main'} = load_window("windowMain");
fix_widget_names();    # Needed since gtk2::builder is not setting widget names

# Load the config file
$globals->{'access'} = load_access();

# Open lyricDB, bibleDB and mediaDb
db_select();
$query = "SELECT profile FROM profiles WHERE host='" . hostname() . "'";
$sth = do_query($lyricDbh, $query, FALSE);
my @row = $sth->fetchrow_array();
if (defined $row[0]) {

    if ($globals->{'profile'} eq "") {
        $globals->{'profile'} = $row[0];
    }
} else {
    if ($globals->{'profile'} eq "") {
        $globals->{'profile'} = "Default";
    }
    $query =
        "INSERT INTO profiles (host, profile) VALUES ('"
      . hostname() . "', '"
      . $globals->{'profile'} . "')";
    do_query($lyricDbh, $query, FALSE);
}

debug("Profile $globals->{'profile'}");
$config = load_config($globals->{'profile'});

if ($globals->{'print_songs'}) {
    print_songs();
    exit;
}

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);
        my $trayxml = load_window("windowTray");
        eval { $tray = Gtk2::TrayIcon->new("Lyricue-" . $trayicon); };
        if ($@) {
            debug("Unable to load system tray");
        } else {
            $trayxml->get_object($trayicon)->reparent($tray);
            $tray->show_all;
        }
    }
}
$0 = "Lyricue Interface";

$globals->{'thumbnail_factory'} = Gnome2::ThumbnailFactory->new('normal');
check_displays();
init_mainWindow();
$widgets->{'main'}->get_object('labelCurrentPlaylist')->{user_data} = -1;
choose_playlist();

# Don't let them cancel out of the playlist chooser yet
$widgets->{'main'}->get_object('toolbuttonPlayCancel')->hide;

# Setup background tasks
$globals->{'status_timer'} = Glib::Timeout->add(1000, \&check_status);
$globals->{'clean_idle'} = Glib::Idle->add(\&clean_database);

Gtk2->main();

# Should never get here
exit(0);

#***

#****f* lyricue/close_main
# NAME
#   close_main
# 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
    write_config();

    $lyricDbh->disconnect;
    if ((!$globals->{'usesword'}) && ($bibleDbh != NULL)) {
        $bibleDbh->disconnect;
    }
    $mediaDbh->disconnect;
    Gtk2->main_quit;
    if ($globals->{'editview_pid'}) {
        debug("Killing $globals->{'editview_pid'}");
        kill 9, $globals->{'editview_pid'};
    }
    if ($globals->{'preview_pid'}) {
        debug("Killing $globals->{'preview_pid'}");
        kill 9, $globals->{'preview_pid'};
    }
    my $miniviews = $globals->{'miniview_pid'};
    foreach my $display (keys %miniviews) {
        debug("Killing $globals->{'miniview_pid'}->{$display}");
        kill 9, $globals->{'miniview_pid'}->{$display};
    }
    close LOG;
    exit();
    return FALSE;
}

#***

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

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

    my ($expanded, $model);

    $model = $widgets->{'main'}->get_object('treePlaylist')->get_model();
    if ($model) {
        $model->foreach(\&save_expanded, \$expanded);
    }

    if ($globals->{'image_playlist'}) {
        debug("image playlist");

        # Column 1 -> Visible text
        # Column 2 -> Image
        # Column 3 -> playorder
        # Column 4 -> transition
        $model = Gtk2::ListStore->new(
            'Glib::String', 'Gtk2::Gdk::Pixbuf',
            'Glib::String', 'Glib::String'
        );
        $widgets->{'main'}->get_object('iconPlaylist')->set_model($model);
        $widgets->{'main'}->get_object('iconPlaylist')->set_markup_column(0);
        $widgets->{'main'}->get_object('iconPlaylist')->set_pixbuf_column(1);
        $widgets->{'main'}->get_object('iconPlaylist')->set_item_width(400);
    } else {
        debug("Tree playlist");

        # 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_object('treePlaylist')->set_model($model);
        my $column = Gtk2::TreeViewColumn->new_with_attributes(
            "",
            Gtk2::CellRendererText->new,
            markup     => 0,
            background => 1
        );
        foreach ($widgets->{'main'}->get_object('treePlaylist')->get_columns) {
            $widgets->{'main'}->get_object('treePlaylist')->remove_column($_);
        }
        $widgets->{'main'}->get_object('treePlaylist')->append_column($column);

        $widgets->{'main'}->get_object('treePlaylist')->set_model($model);
    }
    add_playlist($playlist, undef, $model, $selectedid, \$expanded);
    if ($globals->{'image_playlist'}) {
        $globals->{'image_playlist_idle'} =
          Glib::Idle->add(\&playlist_thumbnails,
            $widgets->{'main'}->get_object('iconPlaylist'));
    }
    undef $globals->{'current_items'};
}

#***

#****f* lyricue/add_playlist
# NAME
#   add_playlist - Add a playlist to the playlist area
# SYNOPSIS
#   add_playlist ($playlist, $iter, $model, $selectedid, $expanded)
# 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
#   $selectedid - The currently selected item
#   $expanded - A lookup of all items saying which are expanded
# OUTPUT
#   A bigger playlist
# SOURCE
#
sub add_playlist {
    my ($playlist, $iter, $model, $selectedid, $expanded) = @_;
    debug("Add playlist " . $playlist);
    my $query =
        "SELECT * FROM playlist WHERE playlist="
      . $playlist
      . " ORDER BY playorder";

    my $sth = do_query($lyricDbh, $query, TRUE);
    while (my $row = $sth->fetchrow_hashref()) {

        my $title = "";
        if ($row->{'type'} eq "back") {
            my $query2 = "SELECT description FROM media WHERE id=\""
              . $row->{'data'} . "\"";

            my $sth2 = do_query($mediaDbh, $query2, TRUE);
            my $row2 = $sth2->fetchrow_hashref();
            $title = "Background : " . $row2->{'description'};
        } elsif ($row->{'type'} eq "file") {
            $_ = $row->{'data'};
            if (/^(.*)\/(.*?)$/) {
                my $filename = $2;
                my $dir      = $1;
                if ($dir =~ /^\/var\/tmp\/lyricue-/) {
                    $filename =~ s/\..*?$//g;
                    $filename =~ s/_/ /g;
                    $title = "Presentation: " . $filename;
                } elsif ($dir =~ /^dvd:/) {
                    if ($filename =~ / /) {
                        $filename =~ s/ / from /;
                    }
                    $title = "DVD Title: " . $filename;
                } else {
                    $title = "File: " . $filename . " in " . $dir;
                }
            } else {
                $title = "File: " . $row->{'data'};
            }
        } elsif ($row->{'type'} eq "imag") {
            my ($type, $id) = split /;/, $row->{'data'};
            if (!defined $id) {
                $id   = $type;
                $type = "db";
            }
            if ($type eq "dir") {
                $id =~ s/^$config->{'ImageDirectory'}\///;
                $title = "Image: " . $id;
            } else {
                my $query2 =
                  "SELECT description FROM media WHERE id=\"" . $id . "\"";

                my $sth2 = do_query($mediaDbh,$query2,TRUE);
                my $row2 = $sth2->fetchrow_hashref();
                $title = "Image: " . $row2->{'description'};
            }
        } elsif ($row->{'type'} eq "vers") {
            $title = "Verses " . $row->{'data'};
        } elsif (($row->{'type'} eq "song") || ($row->{'type'} eq "temp")) {
            my $query2 = "SELECT pagetitle, lyrics FROM page WHERE pageid="
              . $row->{'data'};

            my $sth2 = do_query($lyricDbh, $query2, TRUE);
            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 (defined $row2->{'pagetitle'} && ($row2->{'pagetitle'} ne '')) {
                $title = "<b>" . $row2->{'pagetitle'} . "</b>\n" . $title;
            }
            if (!$title) {
                $title = "";
            }
        } elsif (($row->{'type'} eq "play") || ($row->{'type'} eq "sub")) {

            my $query2 = "SELECT * FROM playlists WHERE id=" . $row->{'data'};

            my $sth2 = do_query($lyricDbh, $query2, TRUE);
            my $row2 = $sth2->fetchrow_hashref();

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

            if ($row2->{'ref'} && $row2->{'ref'} != 0) {
                $query2 =
                  "SELECT songnum FROM lyricMain WHERE id=" . $row2->{'ref'};

                $sth2 = do_query($lyricDbh, $query2, TRUE);
                $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'};

        my $sth3 = do_query($lyricDbh,$query3, TRUE);
        my $imagename = "";
        while (my $row3 = $sth3->fetchrow_hashref()) {
            $imagename = $row3->{'imagename'};
        }

        my $playorder = $row->{'playorder'};
        my ($newiter);
        $title =~ s/&/&amp;/g;
        if ($globals->{'image_playlist'}) {
            if (($row->{'type'} eq "play") || ($row->{'type'} eq "sub")) {
                $title = "<b>" . $title . "</b>";
            }
            $newiter = $model->append();
            $model->set(
                $newiter,
                0 => $title,
                1 => undef,
                2 => $row->{'playorder'}
            );
        } else {
            $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 ($type, $id) = split /;/, $imagename;
                if ($type eq "db") {
                    my $query2 =
                      "SELECT description FROM media WHERE id=\"" . $id . "\"";

                    my $sth2 = do_query($mediaDbh, $query2, TRUE);
                    my $row2    = $sth2->fetchrow_hashref();
                    my $imgiter = $model->append($newiter);
                    $model->set(
                        $imgiter,
                        0 => "* Background: " . $row2->{'description'},
                        1 => "",
                        2 => $row->{'playorder'},
                        3 => $row->{'transition'}
                    );
                } else {
                    my $imgiter = $model->append($newiter);
                    $id =~ s/^$config->{'BGDirectory'}//g;
                    $model->set(
                        $imgiter,
                        0 => "* Background: " . $id,
                        1 => "",
                        2 => $row->{'playorder'},
                        3 => $row->{'transition'}
                    );
                }
            }

            # Set sub-item if transition associated
            if (($row->{'transition'} >> (NUM_TRANS * 2)) != 0) {
                my $transtext = $row->{'transition'} >> (NUM_TRANS * 2);
                if ($transtext == FADE) {
                    $transtext = "radioTransFade";
                } elsif ($transtext == SLIDE_TEXT) {
                    $transtext = "radioTransSlide";
                } elsif ($transtext == ROTATE_TEXT) {
                    $transtext = "radioTransRotate";
                } elsif ($transtext == RANDOM) {
                    $transtext = "radioTransRandom";
                } else {
                    $transtext = "radioTransNone";
                }
                my $transiter = $model->append($newiter);
                $model->set(
                    $transiter,
                    0 => "* Transition: "
                      . $widgets->{'main'}->get_object($transtext)->get_label(),
                    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})
            || ($config->{'ExpandNew'} && !(defined $$expanded->{$playorder})))
        {
            $widgets->{'main'}->get_object('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_object('treePlaylist')
                  ->expand_to_path(
                    Gtk2::TreePath->new_from_string($pathstring));
            }
            $widgets->{'main'}->get_object('treePlaylist')
              ->get_selection->select_iter($newiter);
        }
    }
}

#***

#****f* lyricue/save_expanded
# NAME
#   save_expanded
# SYNOPSIS
#   save_expanded($tree, $path, $expanded)
# FUNCTION
#   Save the expanded state of the treeview item
# INPUTS
#   $tree - Treeview to save state of
#   $part - Item to check
#   $expanded - state lookup
# OUTPUT
#   $expanded is updated
# SOURCE
#
sub save_expanded {
    my ($store, $path, $iter, $expanded) = @_;
    if (!(defined $$expanded->{$store->get($iter, 2)})) {
        $$expanded->{$store->get($iter, 2)} =
          $widgets->{'main'}->get_object('treePlaylist')->row_expanded($path);
    }
    return FALSE;
}

#***

#****f* lyricue/change_sort_order
# NAME
#   change_sort_order
# SYNOPSIS
#   change_sort_order (undef,$column)
# FUNCTION
#   Change the order by which the available songs are displayed
# INPUTS
#   undef - not used
#   $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();
}

#***

#****f* lyricue/update_available
# NAME
#   update_available
# SYNOPSIS
#   update_available ()
# FUNCTION
#   Update the list of available songs, limited by keyword and sorted by orderby
# 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_object('treeAvailable')->{user_data}
        && ($widgets->{'main'}->get_object('treeAvailable')->{user_data} eq
            "load")
      )
    {
        $widgets->{'main'}->get_object('treeAvailable')->{data} = ();
    } else {
        $widgets->{'main'}->get_object('treeAvailable')->{user_data} = "load";
        my $column1 =
          Gtk2::TreeViewColumn->new_with_attributes(fromutf(gettext("Title")),
            Gtk2::CellRendererText->new, text => 0);
        my $column2 =
          Gtk2::TreeViewColumn->new_with_attributes(fromutf(gettext("Book")),
            Gtk2::CellRendererText->new, text => 1);
        my $column3 = Gtk2::TreeViewColumn->new_with_attributes(
            fromutf(gettext("Song Number")),
            Gtk2::CellRendererText->new, text => 2);
        $column1->set_resizable(TRUE);
        $column2->set_resizable(TRUE);
        $column3->set_resizable(TRUE);
        $widgets->{'main'}->get_object('treeAvailable')
          ->append_column($column1);
        $widgets->{'main'}->get_object('treeAvailable')
          ->append_column($column2);
        $widgets->{'main'}->get_object('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_object('entrySearch')->get_text();

    my $special_id = 0;
    if (($config->{'SpecialSong'}) ne "" && ($songname eq "")) {
        my $query =
          "SELECT id,title,songnum,book FROM lyricMain WHERE title LIKE \"%"
          . $config->{'SpecialSong'} . "%\"";
        $sth = do_query($lyricDbh,$query, FALSE);
        $row = $sth->fetchrow_hashref();
        my $iter = $store->append;
        $store->set(
            $iter,                   0, fromutf($row->{'title'}), 1,
            fromutf($row->{'book'}), 2, $row->{'songnum'},        3,
            $row->{'id'}
        );
        $special_id = $row->{'id'};
    }

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

    if (defined $special_id && ($special_id > 0)) {
        $query .= " AND id != " . $special_id;
    }

    # 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;
        $query .= " AND title LIKE \"%" . $songname . "%\"";
    }

    #}
    $query .= " ORDER BY " . $globals->{'sortby'};
    $sth = do_query($lyricDbh, $query, FALSE);

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

    if ($songname ne "") {
        $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'};
        $sth = do_query($lyricDbh, $query, FALSE);

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

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

#***

#****f* lyricue/treeAvailable_row_activated
# NAME
#   treeAvailable_row_activated
# SYNOPSIS
#   treeAvailable_row_activated ($widget, $event)
# FUNCTION
#   Add the currently selected song to the playlist
# INPUTS
#   $widget - Calling widget
#   $event - Calling event
# OUTPUT
#   New song on playlist
# SOURCE
#
sub treeAvailable_row_activated {
    my ($widget, $event) = @_;
    debug("Song activated from available songs list");
    add_to_playlist();
}

#***

#****f* 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,
              [fromutf(gettext("/Edit Song")), undef, 'edit_song', 1, '', ''];
        }
        if ($globals->{'access'} =~ /d/) {
            push @items,
              [
                fromutf(gettext("/Delete Song")),
                undef, 'delete_song', 1, '', ''
              ];
        }
        push @items,
          (
            [
                fromutf(gettext("/Refresh List")),
                undef, 'update_available', 1, '', ''
            ],
            [
                fromutf(gettext("/Order - Songbook -> No.")),
                undef, 'change_sort_order', 4, '', ''
            ],
            [
                fromutf(gettext("/Order - Songbook -> Name.")),
                undef, 'change_sort_order', 5, '', ''
            ]
          );
        if ($globals->{'access'} =~ /p/) {
            push @items,
              (
                [
                    fromutf(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);
    } else {
        my $path = $widget->get_path_at_pos($event->x, $event->y);
        if (!defined $path) {
            return FALSE;
        }
        debug("song selected");
        my $model  = $widgets->{'main'}->get_object('treeAvailable')->get_model;
        my $iter   = $model->get_iter($path);
        my $songid = $model->get($iter, 3);
        my $query =
            "SELECT pageid FROM page WHERE songid="
          . $songid
          . " ORDER BY pagenum";
        $sth = do_query($lyricDbh, $query, FALSE);
        my @row = $sth->fetchrow_array();

        if (defined $row[0]) {
            my $pageid = $row[0];
            preview_pageid($pageid);
        }

        return (FALSE);
    }

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

#***

#****f* lyricue/popup_backdrop_menu
# NAME
#   popup_backdrop_menu
# SYNOPSIS
#   popup_backdrop_menu ($event, $selected)
# FUNCTION
#   popup a menu when backdrop item right-clicked
# INPUTS
#   $event - the calling event
# OUTPUT
#   Displays the right-click menu
# SOURCE
#
sub popup_backdrop_menu {
    my ($event, $selected) = @_;
    debug("Button clicked on Backdrops");
    my @items = ();

    push @items,
      [fromutf(gettext("/Set font colours")), undef, 'manage_bg', undef];
    my $factory =
      Gtk2::ItemFactory->new('Gtk2::Menu', "<backdroppopup>", undef);
    $factory->create_items(1, @items);
    $factory->popup($event->x_root, $event->y_root, $event->button,
        $event->time);
}

sub manage_bg {
    my ($widget) = @_;
    my $selected = "";
    my @list =
      $widgets->{'main'}->get_object('iconviewBack')->get_selected_items;
    if (defined $list[0]) {
        my $model = $widgets->{'main'}->get_object('iconviewBack')->get_model;
        my $iter  = $model->get_iter($list[0]);
        $selected = $model->get($iter, 2);
    }
    if ($selected eq "") {
        return;
    }
    debug("Manage bg:" . $selected);
    $widgets->{'image'} = load_window("dialogImage");
    $widgets->{'image'}->get_object('vboxImageLeft')->hide;
    my $store = Gtk2::ListStore->new('Glib::String', 'Glib::String');
    my $iter = $store->append;
    $store->set($iter, 0, $selected, 1, $selected);
    $widgets->{'image'}->get_object('treeImage')->set_model($store);
    $widgets->{'image'}->get_object('treeImage')
      ->get_selection->select_iter($iter);
    change_preview();
    $widgets->{'image'}->get_object('colorbuttonFontColour')
      ->signal_connect("button_press_event", "open_dialogColour", "ImageFont");
    $widgets->{'image'}->get_object('colorbuttonShadowColour')
      ->signal_connect("button_press_event", "open_dialogColour",
        "ImageShadow");
    $widgets->{'image'}->get_object('buttonImageOK')
      ->signal_connect('clicked', "close_dialog");
    $widgets->{'image'}->get_object('buttonImageCancel')->hide();
    $widgets->{'image'}->get_object('hboxImageColour')->show();
    $widgets->{'image'}->get_object('dialogImage')->show;
}

sub reset_font_colour {
    my ($widget, $object) = @_;
    if ($widget eq $widgets->{'image'}->get_object('buttonFontColourDefault')) {
        $widgets->{'image'}->get_object('entryImageFontColour')
          ->set_text("Default");
        $widgets->{'image'}->get_object('colorbuttonFontColour')
          ->set_color(Gtk2::Gdk::Color->parse($config->{'Colour'}));
        change_colour_media();
    } elsif (
        $widget eq $widgets->{'image'}->get_object('buttonShadowColourDefault'))
    {
        $widgets->{'image'}->get_object('entryImageShadowColour')
          ->set_text("Default");
        $widgets->{'image'}->get_object('colorbuttonShadowColour')
          ->set_color(Gtk2::Gdk::Color->parse($config->{'ShadowColour'}));
        change_colour_media();
    }

}

#****f* lyricue/popup_play_menu
# NAME
#   popup_play_menu
# SYNOPSIS
#   popup_play_menu ($event)
# FUNCTION
#   popup a menu when playlist item right-clicked
# INPUTS
#   $event - the calling event
# OUTPUT
#   Displays the right-click menu
# SOURCE
#
sub popup_play_menu {
    my ($event) = @_;
    debug("Button clicked on playlist window");
    my $playlist =
      $widgets->{'main'}->get_object('labelCurrentPlaylist')->{user_data};
    my $query =
"SELECT title, data FROM playlist, playlists WHERE playlist.data = playlists.id AND playlist = "
      . $playlist
      . " AND type = 'sub'";
    my $sth = do_query($lyricDbh, $query, FALSE);
    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=" . $_;
            my $sth2 = do_query($lyricDbh, $query2, FALSE);
            my $row2 = $sth2->fetchrow_hashref();
            my ($text);
            $text->{'data'}  = $row2->{'data'};
            $text->{'title'} = $row2->{'title'};
            push @list, $text;
        }
    }

    my @items = (
        [fromutf(gettext("/Duplicate Item")), undef, 'copy_item', 1],
        [
            fromutf(gettext("/Remove from Playlist")), undef,
            'remove_from_playlist',                    1
        ],
        [fromutf(gettext("/Refresh Playlist")),    undef, 'update_playlist', 1],
        [fromutf(gettext("/Invert Line Display")), undef, 'invert_lines',    1],
        [fromutf(gettext("/Loop this playlist item")), undef, 'begin_loop', 1],
        [
            fromutf(gettext("/Associate background")), undef,
            'prepare_for_association',                 1
        ],
        [
            fromutf(gettext("/Dis-associate background")), undef,
            'disassociate_bg',                             1
        ],
        [
            fromutf(gettext("/Move to sublist/Main")), undef,
            'move_item_to_sublist',                    $playlist
        ],
    );
    foreach (sort { uc($a->{'title'}) cmp uc($b->{'title'}) } @list) {
        my $item = fromutf(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);
}

#***

#****f* 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_object('windowEditSong')) {
        if (!$widgets->{'add'}->get_object('windowEditSong')->visible) {
            $widgets->{'add'}->get_object('windowEditSong')->destroy;
        } else {
            return;
        }
    }
    $widgets->{'add'} = load_window("windowEditSong");
    load_presets();
    start_editview();
    $widgets->{'add'}->get_object('buttonEditPreviewServer')
      ->signal_connect("clicked", "preview_page", "SERVER");
    $widgets->{'add'}->get_object('notebookEditPages')->remove_page(0);
    %pageOrder = ();
    add_page();
    $widgets->{'add'}->get_object('windowEditSong')->{user_data} = 0;
    $widgets->{'add'}->get_object('windowEditSong')->show_all();
    $widgets->{'add'}->get_object('buttonEditRemovePage')->set_sensitive(FALSE);
    $widgets->{'add'}->get_object('remove_page1')->set_sensitive(FALSE);
}

#***

#****f* 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_object('treeAvailable')->get_selection;
    my $modelTree = $widgets->{'main'}->get_object('treeAvailable')->get_model;
    my @sel       = $selection->get_selected_rows;
    my $deletexml = load_window("dialogSelectSongs");
    $deletexml->get_object('labelSelectSongs')
      ->set_text(gettext("Are you sure you wish to delete "));
    my $model = Gtk2::ListStore->new(
        'Glib::Boolean', 'Glib::String', 'Glib::String', 'Glib::String',
        'Glib::Uint',    'Glib::Uint'
    );
    my $renderer = Gtk2::CellRendererToggle->new;
    $renderer->signal_connect(
        toggled => sub {
            my ($cell, $path_str, $model) = @_;
            my $path   = Gtk2::TreePath->new_from_string($path_str);
            my $column = 0;
            my $iter   = $model->get_iter($path);
            my ($toggle_item) = $model->get($iter, $column);
            $toggle_item ^= 1;
            debug('setting ' . $path_str . ' to ' . $toggle_item);

            # set new value
            $model->set($iter, $column, $toggle_item);
        },
        $model
    );
    my $column1 =
      Gtk2::TreeViewColumn->new_with_attributes("", $renderer, active => 0);
    my $column2 =
      Gtk2::TreeViewColumn->new_with_attributes(fromutf(gettext("Title")),
        Gtk2::CellRendererText->new, text => 1);
    my $column3 =
      Gtk2::TreeViewColumn->new_with_attributes(fromutf(gettext("Artist")),
        Gtk2::CellRendererText->new, text => 2);
    my $column4 =
      Gtk2::TreeViewColumn->new_with_attributes(fromutf(gettext("Book")),
        Gtk2::CellRendererText->new, text => 3);
    my $column5 =
      Gtk2::TreeViewColumn->new_with_attributes(fromutf(gettext("Song Number")),
        Gtk2::CellRendererText->new, text => 4);
    $column1->set_resizable(TRUE);
    $column2->set_resizable(TRUE);
    $column3->set_resizable(TRUE);
    $column4->set_resizable(TRUE);
    $column5->set_resizable(TRUE);
    $deletexml->get_object('treeSongImport')->append_column($column1);
    $deletexml->get_object('treeSongImport')->append_column($column2);
    $deletexml->get_object('treeSongImport')->append_column($column3);
    $deletexml->get_object('treeSongImport')->append_column($column4);
    $deletexml->get_object('treeSongImport')->append_column($column5);

    foreach my $path (@sel) {
        my $iter = $modelTree->get_iter($path);
        my $songid = $modelTree->get($iter, 3);
        if (!($modelTree->get($iter, 1) eq $config->{'SpecialSong'})) {
            $query = "SELECT title,songnum,book,artist FROM lyricMain WHERE id="
              . $songid;
            $sth = do_query($lyricDbh, $query, FALSE);
            $row = $sth->fetchrow_hashref();

            my $newiter = $model->append;
            $model->set(
                $newiter,        0, TRUE,              1,
                $row->{'title'}, 2, $row->{'artist'},  3,
                $row->{'book'},  4, $row->{'songnum'}, 5,
                $songid
            );
        }
    }
    $deletexml->get_object('treeSongImport')->set_model($model);
    $deletexml->get_object('treeSongImport')->set_headers_clickable(TRUE);
    my $confirm = $deletexml->get_object('dialogSelectSongs')->run();
    if ($confirm == 1) {
        $model->foreach(\&do_delete_song);
    }
    close_dialog($deletexml->get_object('dialogSelectSongs'));
    update_available();
}

#***

#****f* 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 ($store, $path, $iter) = @_;
    my ($query, $sth, $rv);
    if ($store->get($iter, 0) == FALSE) {
        return;
    }
    my $songid = $store->get($iter, 5);
    debug("do delete song $songid");

    start_transaction();
    $query = "DELETE FROM lyricMain WHERE id=" . $songid;
    $sth = do_query($lyricDbh, $query, FALSE);

    $query = "DELETE FROM page WHERE songid=" . $songid;
    $sth = do_query($lyricDbh, $query, FALSE);;;;

    $query =
"SELECT playorder FROM playlist,page WHERE playlist.data=page.pageid AND playlist.type=\"song\" AND page.songid="
      . $songid;
    $sth = do_query($lyricDbh, $query, FALSE);
    my $disable = 0;

    while (my @row = $sth->fetchrow_array()) {

        my $query2 = "DELETE FROM playlist WHERE playorder=" . $row[0];
        my $sth2 = do_query($lyricDbh, $query2, FALSE);
        $disable = 1;
    }
    end_transaction();
    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'});

        #
    }

}

#***

#****f* 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_object('windowEditSong')->{user_data};
    my $numpages =
      $widgets->{'add'}->get_object('notebookEditPages')->get_n_pages;

    start_transaction();
    if ($songid != 0) {

        # Remove extra pages
        debug("Song number: " . $songid);
        $query =
            "DELETE FROM page WHERE songid="
          . $songid
          . " AND pagenum > "
          . $numpages;
        $sth = do_query($lyricDbh, $query, FALSE);

        $query =
"DELETE playlist FROM playlist LEFT JOIN page ON playlist.data=page.pageid WHERE page.songid="
          . $songid
          . " AND page.pagenum > "
          . $numpages;
        $sth = do_query($lyricDbh, $query, FALSE);

        # Update lyricMain with new info
        $query = "UPDATE lyricMain SET title="
          . quote($widgets->{'add'}->get_object('entryEditName')->get_text());
        $query .= ", songnum="
          . quote($widgets->{'add'}->get_object('entryEditNumber')->get_text());
        $query .= ", book="
          . quote($widgets->{'add'}->get_object('entryEditBook')->get_text());
        $query .= ", artist="
          . quote($widgets->{'add'}->get_object('entryEditArtist')->get_text());
        $query .=
          ", copyright="
          . quote(
            $widgets->{'add'}->get_object('entryEditCopyright')->get_text());
        my $noaudit = "";
        if (!$widgets->{'add'}->get_object('checkEditAudit')->get_active()) {
            $noaudit = "NOAUDIT ";
        }
        $query .= ", keywords="
          . quote(
            toutf(
                    $noaudit
                  . $widgets->{'add'}->get_object('entryEditKeywords')
                  ->get_text()
            )
          );
        $query .= " WHERE id=" . $songid;
        $sth = do_query($lyricDbh, $query, FALSE);
    } else {

        # Find next id
        $query = "SELECT MAX(id)+1 FROM lyricMain WHERE id < 2000000";
        $sth = do_query($lyricDbh, $query, FALSE);
        my @row = $sth->fetchrow_array;
        $songid = $row[0];
        if ((!defined $songid) || ($songid < 1)) {
            $songid = 1;
        }
        debug("Song number: " . $songid);

        # insert into db
        $query =
"INSERT INTO lyricMain ( id, title, songnum, book, artist, keywords, copyright, entered, written ) VALUES ( "
          . $songid . ", ";
        $query .=
          quote(
            toutf($widgets->{'add'}->get_object('entryEditName')->get_text()))
          . ",";
        $query .=
          quote(
            toutf($widgets->{'add'}->get_object('entryEditNumber')->get_text()))
          . ",";
        $query .=
          quote(
            toutf($widgets->{'add'}->get_object('entryEditBook')->get_text()))
          . ",";
        $query .=
          quote(
            toutf($widgets->{'add'}->get_object('entryEditArtist')->get_text()))
          . ",";
        $query .= quote(
            toutf(
                $widgets->{'add'}->get_object('entryEditKeywords')->get_text()
            )
        ) . ",";
        $query .= quote(
            toutf(
                $widgets->{'add'}->get_object('entryEditCopyright')->get_text()
            )
        ) . ", NOW(), NOW() )";
        $sth = do_query($lyricDbh, $query, FALSE);
    }
    foreach my $pagenum (1 .. $numpages) {
        my $page =
          $widgets->{'add'}->get_object('notebookEditPages')
          ->get_nth_page($pagenum - 1);
        my $title =
          $widgets->{'add'}->get_object('notebookEditPages')
          ->get_tab_label($page)->get_children->get_text;
        my $text = fromutf(gettext("Page "));
        my $lyrics =
          $lyricDbh->quote(get_buffer_text($page->get_children->get_buffer));
        if ($title =~ /^$text/) {
            $text = $lyricDbh->quote("");
        } else {
            $text = $lyricDbh->quote($title);
        }
        my $pagequery = "";
        if ($songid != 0) {
            $query =
                "SELECT pageid FROM page WHERE songid="
              . $songid
              . " AND pagenum="
              . $pagenum;
            $sth = do_query($lyricDbh, $query, FALSE);
            my @row = $sth->fetchrow_array();
            if (defined $row[0]) {
                $pagequery =
                    "UPDATE page SET lyrics="
                  . $lyrics
                  . ", pagetitle="
                  . $text
                  . " WHERE pageid="
                  . $row[0];
                debug("Page Edit : " . $pagenum . "|" . $title);
            }
        }
        if ($pagequery eq "") {
            $pagequery =
                "INSERT INTO page (songid,pagenum,pagetitle,lyrics) VALUES ("
              . $songid . ", "
              . $pagenum . ", "
              . $text . ", "
              . $lyrics . ")";
            debug("Page Add : " . $pagenum . "|" . $title);
        }
        $sth = do_query($lyricDbh,$pagequery,FALSE);
    }
    end_transaction();
    return $songid;
}

#***

#****f* 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();
}

#***

#****f* lyricue/save_song_as
# NAME
#   save_song_as
# SYNOPSIS
#   save_song_as ()
# FUNCTION
#   Save the song as new name, close the add window
#   and update the available songs
# INPUTS
# OUTPUT
#   Calls a few others to do anything
# SOURCE
#
sub save_song_as {
    debug("save song as new");
    $widgets->{'add'}->get_object('windowEditSong')->{user_data} = 0;
    do_save_song();
    close_add_window();
    update_available();
}

#***

#****f* 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_object('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(fromutf(gettext('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);

    # And a event box so you can change it
    $widgets->{'eventAPage'}{$hashnum} = Gtk2::EventBox->new;
    $widgets->{'eventAPage'}{$hashnum}->{user_data} = $hashnum;
    $widgets->{'eventAPage'}{$hashnum}->add($widgets->{'labelAPage'}{$hashnum});
    $widgets->{'eventAPage'}{$hashnum}->set_above_child(FALSE);
    $widgets->{'eventAPage'}{$hashnum}->set_visible_window(FALSE);
    $widgets->{'eventAPage'}{$hashnum}
      ->signal_connect('button-press-event', 'update_pagename');

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

    return $hashnum;
}

#***

#****f* lyricue/update_pagename
# NAME
#   update_pagename
# SYNOPSIS
#   update_pagename ()
# FUNCTION
#   Update the page name if double-clicked
# SOURCE
#
sub update_pagename {
    my ($widget, $event) = @_;
    if ($event->type eq '2button-press') {
        debug("Changing page name");
        my $hashnum = $widget->{user_data};
        my $entry   = Gtk2::Entry->new;
        $entry->{user_data} = $hashnum;
        $entry->set_text($widgets->{'labelAPage'}{$hashnum}->get_label);
        $entry->signal_connect('activate', 'do_update_pagename');
        $widgets->{'add'}->get_object('notebookEditPages')
          ->set_tab_label($widgets->{'scrollAPage'}{$hashnum}, $entry);
    }
}

#***

#****f* lyricue/do_update_pagename
# NAME
#   do_update_pagename
# SYNOPSIS
#   do_update_pagename ()
# FUNCTION
#   A newly named tab
# INPUTS
# SOURCE
#
sub do_update_pagename {
    my ($widget, $event) = @_;
    debug("Apply pagename change");
    my $text    = $widget->get_text;
    my $hashnum = $widget->{user_data};
    $widgets->{'labelAPage'}{$hashnum}->set_label($text);
    $widgets->{'add'}->get_object('notebookEditPages')->set_tab_label(
        $widgets->{'scrollAPage'}{$hashnum},
        $widgets->{'eventAPage'}{$hashnum}
    );
}

#***

#****f* 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}");
        my $text = fromutf(gettext("Page"));
        if ($widgets->{'labelAPage'}{$i}->get_text =~ /^$text/) {
            $widgets->{'labelAPage'}{$i}
              ->set_text($text . " " . $pageOrder{$i});
        }
        $widgets->{'labelAPage'}{$i}->show();
    }
}

#***

#****f* 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_object('notebookEditPages')->get_current_page + 1;
    my %newpageOrder;

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

#***

#****f* 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");
    stop_editview();
    $widgets->{'add'}->get_object('windowEditSong')->destroy();
    undef $widgets->{'add'};
}

#***

#****f* 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: " . $widget->get_name);
    if ($widget) {
        $widget->get_toplevel->destroy;
    }
}

#***

#****f* 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_object('treeAvailable')->get_selection;
    my @sel   = $selection->get_selected_rows;
    my $model = $widgets->{'main'}->get_object('treeAvailable')->get_model;
    my $iter  = $model->get_iter($sel[0]);

    if ($iter) {
        my $songid = $model->get($iter, 3);
        if (   $widgets->{'add'}
            && $widgets->{'add'}->get_object('windowEditSong'))
        {
            if (!$widgets->{'add'}->get_object('windowEditSong')->visible) {
                $widgets->{'add'}->get_object('windowEditSong')->destroy;
                $widgets->{'add'} = undef;

                $widgets->{'add'} = load_window("windowEditSong");
                load_presets();
                start_editview();
                $widgets->{'add'}->get_object('buttonEditPreviewServer')
                  ->signal_connect("clicked", "preview_page", "SERVER");
                %pageOrder = ();
                $widgets->{'add'}->get_object('notebookEditPages')
                  ->remove_page(0);
                $widgets->{'add'}->get_object('windowEditSong')->show();
                update_songinfo($songid);
            }
        } else {
            $widgets->{'add'} = load_window("windowEditSong");
            $widgets->{'add'}->get_object('buttonEditPreviewServer')
              ->signal_connect("clicked", "preview_page", "SERVER");
            load_presets();
            start_editview();
            %pageOrder = ();
            $widgets->{'add'}->get_object('notebookEditPages')->remove_page(0);
            $widgets->{'add'}->get_object('windowEditSong')->show();
            update_songinfo($songid);
        }
    }
}

#***

sub load_presets() {
    my $menutop    = Gtk2::Menu->new();
    my $preset_num = 1;
    while (defined $config->{'Preset' . $preset_num}) {
        my $presetMenuItem = Gtk2::MenuItem->new_with_label(
            "Preset " . $preset_num . ": " . $config->{'Preset' . $preset_num});
        $presetMenuItem->{user_data} = $preset_num;
        $presetMenuItem->signal_connect("activate", "add_copyright_preset",
            $preset_num);
        $menutop->append($presetMenuItem);
        $presetMenuItem->show();
        $preset_num++;
    }
    $widgets->{'add'}->get_object('insert_preset_copyright1')
      ->set_submenu($menutop);
}

#****f* 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);
}

#***

#****f* lyricue/insert_verse
# NAME
#   insert_verse
# SYNOPSIS
#   insert_verse ( $book, $chapter, $start, $end)
# FUNCTION
#   Add the passed verse to the playlist
# INPUTS
#   $book - Book
#   $chaper - Chapter
#   $start - Start verse
#   $end - End verse
# OUTPUT
#   Closes the interface
# SOURCE
sub insert_verse {
    my ($book, $chapter, $start, $end) = @_;
    debug("insert verse");
    my $main_playlist =
      $widgets->{'main'}->get_object('labelCurrentPlaylist')->{user_data};
    if ($main_playlist == -1) {
        return;
    }
    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";
    $sth = do_query($lyricDbh, $query, FALSE);

    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";
    $sth = do_query($lyricDbh, $query, FALSE);
    $row      = $sth->fetchrow_hashref();
    $playlist = $row->{'MAX(id)'} + 1;

    # Add verse to main playlist
    $query =
        "INSERT INTO playlist (playorder,playlist,type,data) VALUES ("
      . $playorder . ", "
      . $main_playlist
      . ", \"play\", "
      . $playlist . ")";
    $sth = do_query($lyricDbh, $query, FALSE);
    $playorder++;

    # Add entry to playlists table
    $query =
        "INSERT INTO playlists (id,title, profile) VALUES ("
      . $playlist . ", \""
      . toutf($verse)
      . "\", \""
      . $globals->{'profile'} . "\")";
    $sth = do_query($lyricDbh, $query, FALSE);

    # 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++;
        my $sth2 = do_query($lyricDbh, $query, FALSE);
        if ($maxv >= $end) {
            $loop = FALSE;
        }
        $minv = $maxv + 1;
    }

    update_playlist();
}

#***

#****f* lyricue/import_song
# NAME
#   import_song
# SYNOPSIS
#   import_song ()
# FUNCTION
#   Import a song into the add song dialog
# INPUTS
# OUTPUT
#   A dialog to open song to import
# SOURCE
#
sub import_song {
    debug("import song");
    my $filexml = load_window("dialogFileChooser");
    $filexml->get_object('buttonFileOK')
      ->signal_connect("clicked", "do_import_song", $filexml);
    $filexml->get_object('dialogFileChooser')
      ->set_title(gettext("Select song to import"));
    $filexml->get_object('dialogFileChooser')->show_all();
}

#***

#****f* lyricue/do_import_song
# NAME
#   do_import_song
# SYNOPSIS
#   do_import_song ($widget, $filexml)
# FUNCTION
#   Actually import the song into the add song dialog
# INPUTS
#   $widget - Calling widget
#   $filexml - 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_object('dialogFileChooser')->get_filename;
    my $lcfilename = lc($filename);

    if (($lcfilename =~ /xmlz$/) || ($lcfilename =~ /xml$/)) {
        import_song_xml($filename);
    } elsif ($lcfilename =~ /txt$/) {
        import_song_text($filename);
    } elsif ($lcfilename =~ /sng$/) {
        import_song_songbeamer($filename);
    } elsif ($lcfilename =~ /usr$/) {
        import_song_songselect($filename);
    } elsif ($lcfilename =~ /opw$/) {
        import_song_opw($filename);
    } elsif ($lcfilename =~ /html$/) {
        import_song_html($filename);
    }

    close_dialog($widget);
}

#***

#****f* lyricue/import_song_text
# NAME
#   import_song_text
# SYNOPSIS
#   import_song_text ()
# FUNCTION
#   Import a song which was stored as a text file
# INPUTS
#   $filename - File to import
# OUTPUT
#   Closes the interface
# SOURCE
sub import_song_text {
    my ($filename) = @_;
    debug("import text song");

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

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

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

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

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

            if (!$copyright) {
                chomp;
                $copyright = $_;
                $copyright =~ s/^.*://g;
                $copyright =~ s/^ *//g;
                $widgets->{'add'}->get_object('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;
}

#***

#****f* lyricue/import_song_xml
# NAME
#   import_song_xml
# SYNOPSIS
#   import_song_xml ($filename)
# FUNCTION
#   Import a song which was stored as an xml file
# INPUTS
#   $filename - File to import
# OUTPUT
#   Closes the interface
# SOURCE
sub import_song_xml {
    my ($filename) = @_;
    debug("import xml song");

    my ($xml);
    if ($filename =~ /[zZ]$/) {
        open my $fh, "gzip -dc \"" . $filename . "\" |";
        $xml = XMLin($fh, ForceArray => ['page'], SuppressEmpty => '');
        close $fh;
    } else {
        $xml = XMLin($filename, ForceArray => ['page'], SuppressEmpty => '');
    }
    $widgets->{'add'}->get_object('entryEditName')
      ->set_text($xml->{'song'}->{'name'});
    $widgets->{'add'}->get_object('entryEditBook')
      ->set_text($xml->{'song'}->{'book'});
    $widgets->{'add'}->get_object('entryEditNumber')
      ->set_text($xml->{'song'}->{'number'});
    $widgets->{'add'}->get_object('entryEditArtist')
      ->set_text($xml->{'song'}->{'artist'});
    $widgets->{'add'}->get_object('entryEditKeywords')
      ->set_text($xml->{'song'}->{'keywords'});
    $widgets->{'add'}->get_object('entryEditCopyright')
      ->set_text($xml->{'song'}->{'copyright'});
    my $pages     = $xml->{'song'}->{'page'};
    my $firstpage = TRUE;

    foreach (@$pages) {
        debug("add page");
        if ($firstpage) {
            $firstpage = FALSE;
            $widgets->{'textAPageB'}{0}->set_text($_);
        } else {
            my $hashnum = add_page();
            $widgets->{'textAPageB'}{$hashnum}->set_text($_);
        }
    }
}

#***

#****f* lyricue/import_song_songbeamer
# NAME
#   import_song_songbeamer
# SYNOPSIS
#   import_song_songbeamer ($filename)
# FUNCTION
#   Import a song from Songbeamer format
# INPUTS
#   $filename - Filename of .sng file to import
# OUTPUT
#   Filled in add song dialog
# SOURCE
#
sub import_song_songbeamer {
    my ($filename) = @_;
    debug("import songbeamer song");

    my ($artist, $name, $number, $book, $copyright, $firstpage) = "";
    my $hashnum = 0;
    open(SONG, $filename) || return;
    while (<SONG>) {
        if (/^#Title=/) {
            if (!$name) {
                chomp;
                $name = $_;
                $name =~ s/^#.*=//g;
                $name =~ s/^ *//g;
                $name =~ s/\r//g;
                $widgets->{'add'}->get_object('entryEditName')->set_text($name);
            }
        } elsif (/^#Songbook=/) {

            if (!$book) {
                chomp;
                $book = $_;
                $book =~ s/^#.*=//g;
                $book =~ s/^ *//g;
                $book =~ s/\/.*$//g;
                $book =~ s/\r//g;
                $widgets->{'add'}->get_object('entryEditBook')->set_text($book);
            }
            if (!$number) {
                chomp;
                $number = $_;
                $number =~ s/^#.*=[^\/]*\///g;
                $number =~ s/^ *//g;
                $number =~ s/\r//g;
                $widgets->{'add'}->get_object('entryEditNumber')
                  ->set_text($number);
            }
        } elsif (/^#Author=/) {
            chomp;
            $_ =~ s/^#.*=//g;
            $_ =~ s/^ *//g;
            $_ =~ s/\r//g;

            if (!$artist) {
                $artist = $_;
                $widgets->{'add'}->get_object('entryEditArtist')
                  ->set_text($artist);
            } else {
                $artist = ", " . $_;
                $widgets->{'add'}->get_object('entryEditArtist')
                  ->append_text($artist);
            }
        } elsif (/^#Melody=/) {
            chomp;
            $_ =~ s/^#.*=//g;
            $_ =~ s/^ *//g;
            $_ =~ s/\r//g;

            if (!$artist) {
                $artist = $_;
                $widgets->{'add'}->get_object('entryEditArtist')
                  ->set_text($artist);
            } else {
                $artist = ", " . $_;
                $widgets->{'add'}->get_object('entryEditArtist')
                  ->append_text($artist);
            }
        } elsif (/^#\(c\)=/) {

            if (!$copyright) {
                chomp;
                $copyright = $_;
                $copyright =~ s/^#.*=//g;
                $copyright =~ s/^ *//g;
                $copyright =~ s/\r//g;
                $widgets->{'add'}->get_object('entryEditCopyright')
                  ->set_text($copyright);
            }
        } elsif (/^#/) {   # many functions in SongBeamer are unknown to Lyricue
            debug("unknown operator");
        } elsif (/^---/) {
            if ($firstpage) {    #no new page in the first run
                debug("add page");
                $hashnum = add_page();
            } else {
                $firstpage = "yes";
            }
        } else {
            debug("add line");
            my $iter = $widgets->{'textAPageB'}{$hashnum}->get_end_iter();
            $widgets->{'textAPageB'}{$hashnum}->insert($iter, $_);
        }
    }
    close SONG;
}

#***

#****f* 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_object('windowEditSong')->{user_data} = $songid;
        $query =
            "SELECT lyrics,pagetitle FROM page WHERE songid=\""
          . $songid
          . "\" ORDER BY pagenum";
        $sth = do_query($lyricDbh, $query, FALSE);

        while ($row = $sth->fetchrow_hashref()) {
            $hashnum = add_page();
            $widgets->{'textAPageB'}{$hashnum}
              ->set_text(fromutf($row->{'lyrics'}));
            if ($row->{'pagetitle'} ne "") {
                $widgets->{'labelAPage'}{$hashnum}
                  ->set_text(fromutf($row->{'pagetitle'}));
            }
        }

        $query =
"SELECT title,songnum,book,artist,keywords,copyright from lyricMain WHERE id="
          . $songid;
        $sth = do_query($lyricDbh, $query, FALSE);
        if ($row = $sth->fetchrow_hashref()) {
            $widgets->{'add'}->get_object('entryEditName')
              ->set_text(fromutf($row->{'title'}));
            $widgets->{'add'}->get_object('entryEditBook')
              ->set_text(fromutf($row->{'book'}));
            $widgets->{'add'}->get_object('entryEditNumber')
              ->set_text(fromutf($row->{'songnum'}));
            $widgets->{'add'}->get_object('entryEditArtist')
              ->set_text(fromutf($row->{'artist'}));
            if ($row->{'keywords'} =~ /^NOAUDIT /) {
                $row->{'keywords'} =~ s/^NOAUDIT //g;
                $widgets->{'add'}->get_object('checkEditAudit')
                  ->set_active(FALSE);
            } else {
                $widgets->{'add'}->get_object('checkEditAudit')
                  ->set_active(TRUE);
            }
            $widgets->{'add'}->get_object('entryEditKeywords')
              ->set_text(fromutf($row->{'keywords'}));
            $widgets->{'add'}->get_object('entryEditCopyright')
              ->set_text(fromutf($row->{'copyright'}));
        }
        $widgets->{'add'}->get_object('notebookEditPages')->set_current_page(0);
    }
}

#***

#****f* lyricue/add_to_playlist
# NAME
#   add_to_playlist
# SYNOPSIS
#   add_to_playlist ()
# FUNCTION
#   Add the chosen songs to the playlist
# OUTPUT
#   More songs in the playlist
# SOURCE
#
sub add_to_playlist {
    debug("Add to playlist clicked");
    my $selection =
      $widgets->{'main'}->get_object('treeAvailable')->get_selection;
    my $model = $widgets->{'main'}->get_object('treeAvailable')->get_model;
    my @sel   = $selection->get_selected_rows;
    foreach my $path (@sel) {
        add_single_song($model->get($model->get_iter($path), 3));
    }

    update_playlist();
}

#***

#****f* 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");

    # Drop out if no playlist selected
    if ($widgets->{'main'}->get_object('labelCurrentPlaylist')->{user_data} ==
        -1)
    {
        return;
    }
    my ($playorder);

    my $query = "SELECT MAX(playorder) FROM playlist";
    $sth = do_query($lyricDbh, $query, FALSE);
    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";
    $sth = do_query($lyricDbh, $query, FALSE);
    $row = $sth->fetchrow_hashref();
    my $playlist = $row->{'MAX(id)'} + 1;

    $query =
        "INSERT INTO playlist (playorder, playlist, data,type) VALUES ("
      . $playorder . ","
      . $widgets->{'main'}->get_object('labelCurrentPlaylist')->{user_data}
      . ","
      . $playlist
      . ",\"play\")";
    $sth = do_query($lyricDbh, $query, FALSE);

    # Find song info
    my $title = "";
    $query =
"SELECT title,pageid,keywords FROM lyricMain, page WHERE songid=id AND id="
      . $availableSelection
      . " ORDER BY pagenum";
    $sth = do_query($lyricDbh, $query, FALSE);
    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\")";
        my $sth2 = do_query($lyricDbh, $query2, FALSE);
    }

    # Add playlists entry
    $query =
        "INSERT INTO playlists (id,title,profile,ref) VALUES ("
      . $playlist . ",\""
      . $title
      . "\", \""
      . $globals->{'profile'} . "\", "
      . $availableSelection . ")";
    $sth = do_query($lyricDbh, $query, FALSE);

    # audit this addition
    # currently always runs due to problem with passing in widgets
    debug("Audit: $config->{'Audit'}");
    if ($config->{'Audit'} && $audit) {
        $query = "SELECT MAX(id)+1 FROM audit";
        $sth = do_query($lyricDbh, $query, FALSE);
        my @row = $sth->fetchrow_array;
        if (!defined $row[0]) {
            $row[0] = "1";
        }
        $query =
            "INSERT INTO audit (id,songid,playdate) VALUES("
          . $row[0] . ", "
          . $availableSelection
          . ", NOW())";
        debug("Auditing with: " . $query);
        $sth = do_query($lyricDbh, $query, FALSE);
    }
}

#***

#****f* lyricue/remove_from_playlist
# NAME
#   remove_from_playlist
# SYNOPSIS
#   remove_from_playlist ()
# FUNCTION
#   Remove selected songs from the playlist
# 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_object('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();
    }
}

#***

#****f* 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 from playlist");
    reset_timer($globals->{'timer'});
    my $query = "SELECT type,data  FROM playlist WHERE playorder=" . $item;
    my $sth = do_query($lyricDbh, $query, FALSE);
    my @row = $sth->fetchrow_array;

    if ($row[0] eq "play" | $row[0] eq "sub") {
        $query = "SELECT playorder FROM playlist WHERE playlist=" . $row[1];
        $sth = do_query($lyricDbh, $query, FALSE);
        while (my @row2 = $sth->fetchrow_array) {
            remove_single_item($row2[0]);
        }

        $query = "DELETE FROM playlist WHERE playlist=" . $row[1];
        $sth = do_query($lyricDbh, $query, FALSE);

        $query = "DELETE FROM playlists WHERE id=" . $row[1];
        $sth = do_query($lyricDbh, $query, FALSE);
    }

    $query = "DELETE FROM playlist WHERE playorder=" . $item;
    $sth = do_query($lyricDbh, $query, FALSE);
    $query = "DELETE FROM associations WHERE playlist=" . $item;
    $sth = do_query($lyricDbh, $query, FALSE);

    if (($row[0] eq "file") && ($row[1] =~ /^\/var\/tmp\/lyricue-/)) {

        # Removing temporary file
        my $command = "rm " . $row[1];
        my $dir     = $row[1];
        $dir =~ s/^(\/var\/tmp\/lyricue-.*\/).*$/$1/g;

        # Check if used elsewhere
        $query =
          "SELECT playorder FROM playlist WHERE data=\"" . $row[1] . "\"";
        $sth = do_query($lyricDbh, $query, FALSE);
        if (@row = $sth->fetchrow_array) {
            debug("Temporary file used elsewhere - not removing");
            return;
        }
        debug($command);
        system($command);

        # Check if directory empty
        my $ret = opendir DIR, $dir;
        if ($ret) {
            my $found = FALSE;
            foreach (sort readdir(DIR)) {
                if (!/^\.*$/) {
                    $found = TRUE;
                }
            }
            if (!$found) {
                debug("Empty directory - removing");
                $command = "rmdir " . $dir;
                debug($command);
                system($command);
            }
        }
    } elsif ($row[0] eq "temp") {
        my $pageid = $row[1];

        # Check if used elsewhere
        $query =
          "SELECT playorder FROM playlist WHERE data=\"" . $pageid . "\"";
        $sth = do_query($lyricDbh, $query, FALSE);
        if (@row = $sth->fetchrow_array) {
            debug("Temporary page used elsewhere - not removing");
            return;
        }

        $query = "DELETE FROM page WHERE pageid=" . $pageid;
        $sth = do_query($lyricDbh, $query, FALSE);
    }
}

#***

#****f* 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 = load_window("dialogConfirm");
    my $labelText =
      fromutf(gettext("Are you sure you wish to clear the current playlist?"));
    $deletexml->get_object('labelDelete')->set_text($labelText);
    $deletexml->get_object('dialogConfirm')
      ->set_title(fromutf(gettext("Confirm Clear Playlist")));
    my $confirm = $deletexml->get_object('dialogConfirm')->run();

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

        my $query =
          "SELECT playorder FROM playlist WHERE playlist=" . $main_playlist;
        $sth = do_query($lyricDbh, $query, FALSE);
        while (my @row = $sth->fetchrow_array()) {
            remove_single_item($row[0]);
        }
        update_playlist();
    }
    close_dialog($deletexml->get_object('dialogConfirm'));
}

#***

#****f* 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, $path,, $column) = @_;
    debug("display song");
    reset_timer($globals->{'timer'});
    if ($globals->{'image_playlist'}) {
        my @list =
          $widgets->{'main'}->get_object('iconPlaylist')->get_selected_items;
        if (defined $list[0]) {
            my $model =
              $widgets->{'main'}->get_object('iconPlaylist')->get_model;
            my $iter = $model->get_iter($list[0]);
            update_display("display", $model->get($iter, 2), "");
        }
    } else {
        my $selection =
          $widgets->{'main'}->get_object('treePlaylist')->get_selection;
        my ($model, $iter) = $selection->get_selected;
        if ($iter) {
            update_display("display", $model->get($iter, 2), "");
        }
    }
    return TRUE;
}

sub treePlaylist_cursor_changed {
    my ($widget) = @_;
    debug("preview playlist item");
    if (!$globals->{'image_playlist'}) {
        my $selection =
          $widgets->{'main'}->get_object('treePlaylist')->get_selection;
        my ($model, $iter) = $selection->get_selected;
        if ($iter) {
            my $transition = $model->get($iter, 3);

            # Set the transition values
            my $out_direction = mod($transition, 2**NUM_TRANS);
            $transition = $transition >> NUM_TRANS;
            my $in_direction = mod($transition, 2**NUM_TRANS);
            my $effect = $transition >> NUM_TRANS;
            if ($effect == NOTRANS) {
                $widgets->{'main'}->get_object('radioTransNone')
                  ->set_active(TRUE);
            } elsif ($effect == FADE) {
                $widgets->{'main'}->get_object('radioTransFade')
                  ->set_active(TRUE);
            } elsif ($effect == SLIDE_TEXT) {
                $widgets->{'main'}->get_object('radioTransSlide')
                  ->set_active(TRUE);
            } elsif ($effect == ROTATE_TEXT) {
                $widgets->{'main'}->get_object('radioTransRotate')
                  ->set_active(TRUE);
            } elsif ($effect == RANDOM) {
                $widgets->{'main'}->get_object('radioTransRandom')
                  ->set_active(TRUE);
            } else {
                $widgets->{'main'}->get_object('radioTransDefault')
                  ->set_active(TRUE);
            }
            my $indir = "";
            if ($in_direction & UP)    { $indir .= "Up" }
            if ($in_direction & DOWN)  { $indir .= "Down" }
            if ($in_direction & LEFT)  { $indir .= "Left" }
            if ($in_direction & RIGHT) { $indir .= "Right" }
            if ($indir eq "") { $indir = "None" }
            $widgets->{'main'}->get_object("toggleIn" . $indir)
              ->set_active(TRUE);
            $widgets->{'main'}->get_object("toggleRotX")
              ->set_active($in_direction & X_AXIS);
            $widgets->{'main'}->get_object("toggleRotY")
              ->set_active($in_direction & Y_AXIS);
            $widgets->{'main'}->get_object("toggleRotZ")
              ->set_active($in_direction & Z_AXIS);

            my $outdir = "";
            if ($out_direction & UP)    { $outdir .= "Up" }
            if ($out_direction & DOWN)  { $outdir .= "Down" }
            if ($out_direction & LEFT)  { $outdir .= "Left" }
            if ($out_direction & RIGHT) { $outdir .= "Right" }
            if ($outdir eq "") { $outdir = "None" }
            $widgets->{'main'}->get_object("toggleOut" . $outdir)
              ->set_active(TRUE);
        }
    }
    update_quickedit();
    if ($config->{'DynamicPreview'}) {
        preview_playlist_item();
    }
}

sub on_treePlaylist_button_press_event {
    my ($widget, $event) = @_;

    # Right-click menu
    if ($event->button == 3) {
        popup_play_menu($event);
        return TRUE;
    }
    return FALSE;
}

#***

#****f* 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_object('treePlaylist')->get_selection;
    my ($model, $iter) = $selection->get_selected;
    if ($iter) {
        update_display("display", $model->get($iter, 2), "");
    }
}

#***

#****f* 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'});
}

#***

#****f* 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'});
}

#***

#****f* 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'});
}

#***

#****f* 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'});
}

#***

#****f* lyricue/blank_page
# NAME
#   blank_page
# SYNOPSIS
#   blank_page ()
# FUNCTION
#   Blank the server
# INPUTS
#   None
# OUTPUT
#   Calls update_display
# SOURCE
#
sub blank_page {
    my ($widget) = @_;
    if ($globals->{'ignore_blank'}) {
        debug("ignore blank");
        $globals->{'ignore_blank'} = FALSE;
        return;
    }
    debug("Blank page clicked");
    reset_timer($globals->{'timer'});
    update_display("blank", $config->{'BGImage'}, "");
}

#***

#****f* lyricue/clear_text
# NAME
#   clear_text
# SYNOPSIS
#   clear_text ()
# FUNCTION
#   Clear the server text
# INPUTS
#   None
# OUTPUT
#   Calls update_display
# SOURCE
#
sub clear_text {
    my ($widget) = @_;
    if ($globals->{'ignore_clear'}) {
        $globals->{'ignore_clear'} = FALSE;
        debug("ignore clear");
        return;
    }
    debug("Clear text clicked");
    reset_timer($globals->{'timer'});
    update_display("blank", "", "");
}

#***

#****f* lyricue/reshow_page
# NAME
#   reshow_page
# SYNOPSIS
#   reshow_page()
# FUNCTION
#   Reshow the current page
# INPUTS
#   None
# OUTPUT
#   Calls update_display
# SOURCE
#
sub reshow_page {
    my ($widget) = @_;
    debug("Reshow page clicked");
    reset_timer($globals->{'timer'});
    update_display("display", "current", "");
}

#***

#****f* 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", "", "");
}

#***

#****f* 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, $type) = @_;
    debug("preview page $type");
    reset_timer($globals->{'timer'});

    my $current_page =
      $widgets->{'add'}->get_object('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
            fromutf(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_object('entryEditName')->get_text()
      . "#BREAK#"
      . $widgets->{'add'}->get_object('entryEditArtist')->get_text()
      . "#BREAK#"
      . $widgets->{'add'}->get_object('entryEditCopyright')->get_text()
      . "#BREAK#wrap";
    $titledata =~ s/:/#SEMI#/g;
    my $songtext = get_buffer_text($widgets->{'textAPageB'}{$current_hash});
    $songtext =~ s/\n/#BREAK#/g;
    $songtext =~ s/:/#SEMI#/g;
    if ($type eq "SERVER") {
        update_display("preview", $titledata, $songtext);
    } else {
        preview_display("preview", $titledata, $songtext, FALSE);
    }
}

#***

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

    my $query = "SELECT lyrics, pagetitle FROM page WHERE pageid=" . $pageid;
    $sth = do_query($lyricDbh, $query, FALSE);
    $row = $sth->fetchrow_hashref();
    if (defined $row) {

     #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 $songtext  = ($row->{'lyrics'});
        my $pagetitle = ($row->{'pagetitle'});
        $songtext  =~ s/\n/#BREAK#/g;
        $songtext  =~ s/:/#SEMI#/g;
        $pagetitle =~ s/\n/#BREAK#/g;
        $pagetitle =~ s/:/#SEMI#/g;

        preview_display("preview", $pagetitle, $songtext, FALSE);
    }
}

#***

#****f* 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");

    if ((!defined $globals->{'bibledb'}) || ($globals->{'bibledb'} eq "")) {
        display_message($errorcodes->{'nobible'});
    } else {
        my $bookxml = load_window("windowBook");
        $bookxml->get_object('windowBook')->show;
    }
}

#***

#****f* lyricue/add_image_pl
# NAME
#   add_image_pl
# SYNOPSIS
#   add_image_pl()
# FUNCTION
#   Add an image to the playlist
# INPUTS
# OUTPUT
#   New item on playlist
# SOURCE
#
sub add_image_pl {
    debug("Add image to playlist clicked");
    my $widget   = $widgets->{'main'}->get_object('iconviewImage');
    my $selected = "";
    my @list     = $widget->get_selected_items;
    if (defined $list[0]) {
        my $model = $widget->get_model;
        my $iter  = $model->get_iter($list[0]);
        $selected = $model->get($iter, 2);
    }
    if ($selected ne "") {
        my $playlist =
          $widgets->{'main'}->get_object('labelCurrentPlaylist')->{user_data};
        if ($playlist != -1) {
            add_image_item($playlist, $selected);
            update_playlist();
        }
    }
}

#***

#****f* lyricue/add_image
# NAME
#   add_image
# SYNOPSIS
#   add_image ()
# FUNCTION
#   Load the image dialog
# INPUTS
# OUTPUT
#   dialogImage loaded
# SOURCE
#
sub add_image {
    debug("Add image clicked");

    $widgets->{'image'} = load_window("dialogImage");
    $widgets->{'image'}->get_object('buttonImageAdd')
      ->signal_connect('clicked', 'import_image');
    $widgets->{'image'}->get_object('dialogImage')->show;

    if ($widgets->{'image'}->get_object('treeImage')->{user_data}
        && ($widgets->{'image'}->get_object('treeImage')->{user_data} eq "load")
      )
    {
        $widgets->{'image'}->get_object('treeImage')->{data} = ();
    } else {
        $widgets->{'image'}->get_object('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_object('treeImage')->append_column($column);
        $widgets->{'image'}->get_object('treeImage')
          ->get_selection->set_mode('multiple');
        $widgets->{'image'}->get_object('buttonImageOK')
          ->signal_connect('clicked', "close_dialog");
    }

    # Fill sublists dropbox
    my $store =
      $widgets->{'image'}->get_object('optionImageSublist')->get_model();
    if ($store) {
        $store->clear;
    } else {
        $store = Gtk2::ListStore->new('Glib::String', 'Glib::Uint');
        $widgets->{'image'}->get_object('optionImageSublist')
          ->set_model($store);
    }

    my $cell = Gtk2::CellRendererText->new;
    $widgets->{'image'}->get_object('optionImageSublist')
      ->pack_start($cell, TRUE);
    $widgets->{'image'}->get_object('optionImageSublist')
      ->add_attribute($cell, text => 0);

    my $playlist =
      $widgets->{'main'}->get_object('labelCurrentPlaylist')->{user_data};
    my $query =
"SELECT title, data FROM playlist, playlists WHERE playlist.data = playlists.id AND playlist = "
      . $playlist
      . " AND type = 'sub'";
    my $sth = do_query($lyricDbh, $query, FALSE);
    $store->set($store->append, 0, "Main", 1, $playlist);
    $widgets->{'image'}->get_object('optionImageSublist')->set_active(0);

    while ($row = $sth->fetchrow_hashref()) {
        $store->set($store->append, 0, $row->{'title'}, 1, $row->{'data'});
        my @childid = find_more_children($row->{'data'});
        foreach (@childid) {
            debug("Child sublist found. ID: " . $_);
            my $query2 = "SELECT title FROM playlists WHERE id=" . $_;
            my $sth2 = do_query($lyricDbh, $query2, FALSE);
            my $row2 = $sth2->fetchrow_hashref();
            $store->set($store->append, 0, $row2->{'title'}, 1,
                $row2->{'data'});
        }
    }

    update_imagedir("img", "");
}

#***

#****f* lyricue/update_imagedir
# NAME
#   update_imagedir
# SYNOPSIS
#   update_imagedir($type, $data)
# FUNCTION
#   Update the view of an category
# INPUTS
#   $type - 'img' or 'bg'
#   $data - Category/type to load
# OUTPUT
#   Updated list of images
# SOURCE
#
sub update_imagedir {
    my ($type, $data) = @_;
    my ($access, $category);
    debug("update image directory");
    my $main = "";
    if ($type eq "bg") {
        $main = $config->{'SpecialBack'};
    } else {
        $main = $config->{'SpecialImage'};
    }
    if ($data eq "") {
        $data = $main;
    }
    ($access, $category) = split /;/, $data;
    if (!defined $category) {
        $category = $access;
        $access   = "db";
    }

    debug("changing to $access;$category");

    # Update categories list
    $widgets->{'image'}->get_object('optionImageCategory')->{'user_data'} =
      $type;
    $widgets->{'image'}->get_object('treeImage')->{'user_data'} = $data;
    my $store =
      $widgets->{'image'}->get_object('optionImageCategory')->get_model();
    if ($store) {
        $store->clear;
    } else {
        $store = Gtk2::ListStore->new('Glib::String', 'Glib::String');
        $widgets->{'image'}->get_object('optionImageCategory')
          ->set_model($store);
        my $cell = Gtk2::CellRendererText->new;
        $widgets->{'image'}->get_object('optionImageCategory')
          ->pack_start($cell, TRUE);
        $widgets->{'image'}->get_object('optionImageCategory')
          ->add_attribute($cell, text => 0);
    }

    # Add default category
    my $iter = $store->append;
    $store->set($iter, 0, $main, 1, $main);
    if ($main eq $data) {
        $widgets->{'image'}->get_object('optionImageCategory')
          ->set_active_iter($iter);
    }

    $query =
        "SELECT DISTINCT category FROM media WHERE type=\""
      . $type
      . "\" AND category != \""
      . $main
      . "\" ORDER BY category";
    $sth = do_query($mediaDbh, $query, FALSE);

    while ($row = $sth->fetchrow_hashref()) {
        $store->set($store->append, 0, $row->{'category'}, 1,
            "db;" . $row->{'category'});
    }

    # Add categories from directories
    my $dirname = "";
    if ($type eq "bg") {
        $dirname = $config->{'BGDirectory'};
    } else {
        $dirname = $config->{'ImageDirectory'};
    }
    my (@files, $file);
    my $ret = opendir DIR, $dirname;
    if ($ret) {
        foreach $file (sort readdir(DIR)) {
            if (!($file =~ /^\./) && (-d $dirname . "/" . $file)) {
                my $iter = $store->append;
                $store->set($iter, 0, $file, 1, "dir;" . $file);
                if ("dir;" . $file eq $data) {
                    $widgets->{'image'}->get_object('optionImageCategory')
                      ->set_active_iter($iter);
                }
                debug($file);
            }
        }
        closedir DIR;
    }

    # Load list of files
    $store = Gtk2::ListStore->new('Glib::String', 'Glib::String');

    if ($access eq "dir") {
        my $dirname = "";
        if ($type eq "bg") {
            $dirname = $config->{'BGDirectory'} . "/" . $category;
        } else {
            $dirname = $config->{'ImageDirectory'} . "/" . $category;
        }
        my $ret = opendir DIR, $dirname;
        if ($ret) {
            my $file = "";
            foreach $file (sort readdir(DIR)) {
                if (!($file =~ /^\./)) {
                    $iter = $store->append;
                    $store->set($iter, 0, $file, 1,
                        "dir;" . $dirname . "/" . $file);
                }
            }
        }
    }

    if ($access eq "db") {

        # update list of available images
        $query =
            "SELECT * FROM media WHERE category LIKE \""
          . $category
          . "\" AND type=\""
          . $type
          . "\" ORDER BY description";
        $sth = do_query($mediaDbh, $query, FALSE);
        while ($row = $sth->fetchrow_hashref()) {
            $iter = $store->append;
            $store->set($iter, 0, $row->{'description'}, 1,
                "db;" . $row->{'id'});
        }
    }
    $widgets->{'image'}->get_object('treeImage')->set_model($store);
    $widgets->{'image'}->get_object('entryImageFontColour')
      ->set_text("Default");
    $widgets->{'image'}->get_object('colorbuttonFontColour')
      ->set_color(Gtk2::Gdk::Color->parse($config->{'Colour'}));
    $widgets->{'image'}->get_object('entryImageShadowColour')
      ->set_text("Default");
    $widgets->{'image'}->get_object('colorbuttonShadowColour')
      ->set_color(Gtk2::Gdk::Color->parse($config->{'ShadowColour'}));
}

#***

#****f* lyricue/change_image_category
# NAME
#   change_image_category
# SYNOPSIS
#   change_image_category()
# FUNCTION
#   change image category
# INPUTS
# OUTPUT
# SOURCE
#
sub change_image_category {
    my $iter =
      $widgets->{'image'}->get_object('optionImageCategory')->get_active_iter;
    if (defined $iter) {
        debug("change image category");
        my $category =
          $widgets->{'image'}->get_object('optionImageCategory')
          ->get_model->get($iter, 1);
        if (defined $category) {
            if ($widgets->{'image'}->get_object('treeImage')->{user_data} ne
                $category)
            {
                update_imagedir(
                    $widgets->{'image'}->get_object('optionImageCategory')
                      ->{'user_data'},
                    $category
                );
            }
        }
        debug("done");
    }
}

#***

#****f* 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 (keys(%displays) == 0) {
        debug("No displays running");
        return;
    }
    foreach my $display (keys %displays) {
        if ($displays{$display}) {
            my @display_items = split(/:/, $display);
            debug("Updating " . $display);
            if (
                my $server = IO::Socket::INET->new(
                    Proto    => "tcp",
                    PeerAddr => $display_items[0],
                    PeerPort => $display_items[1]
                )
              )
            {
                print $server toutf(
                    $command . ":" . $primary . ":" . $secondary . "\n");
                close($server);
            }
        }
    }
}

#***

#****f* lyricue/preview_playlist_item
# NAME
#   preview_playlist_item
# SYNOPSIS
#   preview_playlist_item()
# FUNCTION
#   Preview a playlist item
# INPUTS
#
# OUTPUT
#   Updated preview
# SOURCE
#
sub preview_playlist_item {
    debug("Previewing a playlist item...");

    if ($globals->{'image_playlist'}) {
        my @list =
          $widgets->{'main'}->get_object('iconPlaylist')->get_selected_items;
        if (defined $list[0]) {
            my $model =
              $widgets->{'main'}->get_object('iconPlaylist')->get_model;
            my $iter = $model->get_iter($list[0]);
            preview_display("display", $model->get($iter, 2), undef, FALSE);
        }
    } else {
        my $selection =
          $widgets->{'main'}->get_object('treePlaylist')->get_selection;
        my ($model, $iter) = $selection->get_selected;
        if ($iter) {

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

#***

#****f* lyricue/preview_display
# NAME
#   preview_display
# SYNOPSIS
#   preview_display ()
# FUNCTION
#   Issues commands to the preview server
# INPUTS
# OUTPUT
#   Updated display
# SOURCE
#
sub preview_display {
    my ($command, $primary, $secondary, $checkret) = @_;
    debug("Preview display");
    if (!defined($secondary)) {
        $secondary = "";
    }
    if (!defined($primary)) {
        $primary = "";
    }
    $primary   =~ s/:/#SEMI#/g;
    $secondary =~ s/:/#SEMI#/g;
    debug("Preview command: " . $command . ":" . $primary . ":" . $secondary);

    my $display = 0;
    if (scalar keys %previews == 0) {
        debug("No preview displays running");
        return;
    }
    foreach my $display (keys %previews) {
        my @display_items = split(/:/, $display);
        debug("Updating Preview " . $display);
        if (
            my $server = IO::Socket::INET->new(
                Proto    => "tcp",
                PeerAddr => $display_items[0],
                PeerPort => $display_items[1]
            )
          )
        {
            print $server toutf(
                $command . ":" . $primary . ":" . $secondary . "\n");
            if ($checkret) {
                if (defined(my $status = <$server>)) {
                    $status = fromutf($status);
                    chomp($status);
                }
            }

        }
    }
}

#***

#****f* lyricue/open_dialogColour
# NAME
#   open_dialogColour
# SYNOPSIS
#   open_dialogColour($widget, $event, $item)
# FUNCTION
#   Load a new dialog to select a colour
# INPUTS
#   $widget - Calling widget
#   $event - how we where called
#   $item - What we want to set the colour for
# OUTPUT
#
# SOURCE
#
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 = load_window("dialogColour");
    $colourxml->get_object('dialogColour')
      ->set_title(
        fromutf(gettext("Select ")) . $item . fromutf(gettext(" Colour")));
    $colourxml->get_object('dialogColour')->{'user_data'} = $item;

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

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

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

#***

#****f* lyricue/open_dialogFont
# NAME
#   open_dialogFont
# SYNOPSIS
#   open_dialogFont ($widget, $event, $font)
# FUNCTION
#   Load a new dialog to select a colour
# INPUTS
#   $widget - Calling widget
#   $event - how we where called
#   $font - What we want to set the colour for
# OUTPUT
#
# SOURCE
#
sub open_dialogFont {
    my ($widget, $event, $font) = @_;
    debug("Opening Font dialog");
    $widgets->{'font'} = load_window("dialogFont");
    my $fontname = $config->{$font};
    $widgets->{'font'}->get_object('selectFont')->set_font_name($fontname);
    $widgets->{'font'}->get_object('buttonFontOK')
      ->signal_connect("clicked", "apply_font", $font);
    $widgets->{'font'}->get_object('dialogFont')->show;
    return TRUE;
}

#***

#****f* 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_object('dialogColour')->{'user_data'};
    my $colour = $colourxml->get_object('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_object('entryPrefShadowColour')
          ->set_text($color2);
        $widgets->{'prefs'}->get_object('entryPrefShadowColour')
          ->set_text($color2);
    } elsif ($item eq "Font") {
        $widgets->{'prefs'}->get_object('entryPrefColour')->set_text($color2);
    } elsif ($item eq "ImageShadow") {
        $widgets->{'image'}->get_object('entryImageShadowColour')
          ->set_text($color2);
        $widgets->{'image'}->get_object('colorbuttonShadowColour')
          ->set_color(Gtk2::Gdk::Color->parse($color2));
        change_colour_media();
    } elsif ($item eq "ImageFont") {
        $widgets->{'image'}->get_object('entryImageFontColour')
          ->set_text($color2);
        $widgets->{'image'}->get_object('colorbuttonFontColour')
          ->set_color(Gtk2::Gdk::Color->parse($color2));
        change_colour_media();
    }
    close_dialog($widget);
}

#***

#****f* 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_object('selectFont')->get_font_name);
    $widgets->{'prefs'}->get_object('entryPref' . $type)
      ->set_text($widgets->{'font'}->get_object('selectFont')->get_font_name);
    close_dialog($widget);
}

#***

#****f* 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
#   $event
#   $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)) {
        my $begin = 0;
        my $end   = 0;
        if ($event->type eq 'button-press') {
            if (   ($globals->{'verseEnd'} == 0)
                && ($globals->{'verseStart'} != 0))
            {
                if ($number < $globals->{'verseStart'}) {
                    $globals->{'verseEnd'}   = $globals->{'verseStart'};
                    $globals->{'verseStart'} = $number;
                } else {
                    $globals->{'verseEnd'} = $number;
                }
                debug(  "End "
                      . $globals->{'verseStart'} . ","
                      . $globals->{'verseEnd'});
                $begin = $globals->{'verseStart'};
                $end   = $globals->{'verseEnd'};
            } else {
                debug("Start " . $globals->{'verseStart'});
                $globals->{'verseStart'} = $number;
                $globals->{'verseEnd'}   = 0;
                $begin                   = $globals->{'verseStart'};
                $end                     = $globals->{'verseStart'};
            }
            my $book =
              $widgets->{'main'}->get_object('entryNavVerse')
              ->get_active_text();
            my $verse = "";
            $book =~ s/:.*$//g;
            $verse = $book . ":" . $begin . "-" . $end;
            $widgets->{'main'}->get_object('entryNavVerseText')
              ->set_text($verse);
        } elsif ($event->type eq 'enter-notify') {
            $begin = 0;
            if (   (defined $globals->{'verseStart'})
                && ($globals->{'verseStart'} != 0)
                && ($globals->{'verseEnd'} == 0))
            {
                if ($number < $globals->{'verseStart'}) {
                    $begin = $number;
                    $end   = $globals->{'verseStart'};
                } else {
                    $begin = $globals->{'verseStart'};
                    $end   = $number;
                }
            }
        }
        if ($begin != 0) {
            foreach my $num (1 .. $max) {
                my $button = "button" . ($num - 1);
                if (($num >= $begin) && ($num <= $end)) {
                    $widgets->{'bibleBrowser'}{$button}->set_active(TRUE);
                } else {
                    $widgets->{'bibleBrowser'}{$button}->set_active(FALSE);
                }
            }
            return TRUE;
        }
    }
    return FALSE;
}

#***

#****f* 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_object('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_object('treeImage')
          ->get_model->get_iter($newsel);
        my $data =
          $widgets->{'image'}->get_object('treeImage')
          ->get_model->get($iter, 1);
        my ($type, $id) = split /;/, $data;
        my $tmp = 0;
        foreach $tmp (@selecteditems) {
            my $iter2 =
              $widgets->{'image'}->get_object('treeImage')
              ->get_model->get_iter($tmp);
            my $data2 =
              $widgets->{'image'}->get_object('treeImage')
              ->get_model->get($iter2, 1);
            if (!defined $selectedimages{$data2}) {
                $data = $data2;
                ($type, $id) = split /;/, $data;
            }
        }
        %selectedimages = ();
        foreach $tmp (@selecteditems) {
            my $iter2 =
              $widgets->{'image'}->get_object('treeImage')
              ->get_model->get_iter($tmp);
            my $id2 =
              $widgets->{'image'}->get_object('treeImage')
              ->get_model->get($iter2, 1);
            $selectedimages{$id2} = TRUE;
        }

        if ($globals->{'bg_previews'}) {
            debug("sub change_preview: create_pixbuf");
            my $scaled = create_pixbuf($data, 480, 360);
            if ($scaled) {
                my ($row, $query);
                if ($type eq "db") {
                    $query =
                      "SELECT textcolour, shadowcolour FROM media WHERE id=\""
                      . $id . "\"";
                } else {
                    $query =
"SELECT textcolour, shadowcolour FROM media WHERE format=\"file\" AND category=\""
                      . $id . "\"";
                }
                my $sth = do_query($mediaDbh, $query, FALSE);
                $row = $sth->fetchrow_hashref();
                if ($row) {
                    $widgets->{'image'}->get_object('buttonImageDelete')
                      ->set_sensitive(TRUE);
                    $widgets->{'image'}->get_object('buttonImageChange')
                      ->set_sensitive(TRUE);
                    $widgets->{'image'}->get_object('hboxImageColour')
                      ->show_all;
                } else {
                    $row->{'textcolour'}   = "Default";
                    $row->{'shadowcolour'} = "Default";
                }

                #    $widgets->{'image'}->get_object('buttonImageDelete')
                #      ->set_sensitive(FALSE);
                #    $widgets->{'image'}->get_object('buttonImageChange')
                #      ->set_sensitive(FALSE);
                #    $widgets->{'image'}->get_object('hboxImageColour')->hide;
                #}
                my $fg_span = "";
                my $bg_span = "";
                if (   ($row->{'textcolour'} ne "")
                    && ($row->{'textcolour'} ne "NULL")
                    && ($row->{'textcolour'} ne "Default"))
                {
                    $widgets->{'image'}->get_object('entryImageFontColour')
                      ->set_text($row->{'textcolour'});
                    $widgets->{'image'}->get_object('colorbuttonFontColour')
                      ->set_color(
                        Gtk2::Gdk::Color->parse($row->{'textcolour'}));
                    $fg_span = "<span color=\"" . $row->{'textcolour'} . "\">";
                } else {
                    $widgets->{'image'}->get_object('entryImageFontColour')
                      ->set_text("Default");
                    $widgets->{'image'}->get_object('colorbuttonFontColour')
                      ->set_color(Gtk2::Gdk::Color->parse($config->{'Colour'}));
                    $fg_span = "<span color=\"" . $config->{'Colour'} . "\">";
                }
                if (   ($row->{'shadowcolour'} ne "")
                    && ($row->{'shadowcolour'} ne "NULL")
                    && ($row->{'shadowcolour'} ne "Default"))
                {
                    $widgets->{'image'}->get_object('entryImageShadowColour')
                      ->set_text($row->{'shadowcolour'});
                    $widgets->{'image'}->get_object('colorbuttonShadowColour')
                      ->set_color(
                        Gtk2::Gdk::Color->parse($row->{'shadowcolour'}));
                    $bg_span =
                      "<span color=\"" . $row->{'shadowcolour'} . "\">";
                } else {
                    $widgets->{'image'}->get_object('entryImageShadowColour')
                      ->set_text("Default");
                    $widgets->{'image'}->get_object('colorbuttonShadowColour')
                      ->set_color(
                        Gtk2::Gdk::Color->parse($config->{'ShadowColour'}));
                    $bg_span =
                      "<span color=\"" . $config->{'ShadowColour'} . "\">";
                }

                # Add text overlay to $scaled
                my $pixmap = $scaled->render_pixmap_and_mask(127);
                my $pango_layout =
                  $widgets->{'image'}->get_object('imageImage')
                  ->create_pango_layout("");
                $pango_layout->set_font_description(
                    Gtk2::Pango::FontDescription->from_string(
                        $config->{'Main'}
                    )
                );
                my $gc = Gtk2::Gdk::GC->new($pixmap);
                $pango_layout->set_markup(
                    $bg_span . fromutf(gettext("Song Contents")) . "</span>");
                my ($text_w, $text_h) = $pango_layout->get_pixel_size();
                my $text_x = (480 - $text_w) / 2;
                my $text_y = (360 - $text_h) / 2;
                $pixmap->draw_layout(
                    $gc,
                    $text_x + 2,
                    $text_y + 2,
                    $pango_layout
                );
                $pango_layout->set_markup(
                    $fg_span . fromutf(gettext("Song Contents")) . "</span>");
                $pixmap->draw_layout($gc, $text_x, $text_y, $pango_layout);
                $widgets->{'image'}->get_object('imageImage')
                  ->set_from_pixmap($pixmap, undef);
                $widgets->{'image'}->get_object('imageImage')->{user_data} =
                  $data;
            }
        }
    }
    return TRUE;
}

#***

#****f* 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_object('imageImage')->{user_data};
    if ($image) {
        my $scaled = create_pixbuf(
            $image,
            $globals->{'icon_width'},
            $globals->{'icon_height'}
        );
        if ($scaled) {
            $widgets->{'prefs'}->get_object('imagePrefBG')
              ->set_from_pixbuf($scaled);
            $widgets->{'prefs'}->get_object('imagePrefBG')->{user_data} =
              $image;
        }
    }
    close_dialog($widget);
}

#***

#****f* lyricue/print_songs
# NAME
#   print_songs
# SYNOPSIS
#   print_songs ()
# FUNCTION
#   Print a html formatted list of available songs
# 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'}, TRUE);
    my $query = "SELECT COUNT(id) FROM lyricMain WHERE id > 0;";
    my $sth   = do_query($lyricDbh, $query, FALSE);
    my @totals = $sth->fetchrow_array();

    $query =
"SELECT title,book,songnum,artist FROM lyricMain WHERE id > 0 ORDER BY title;";
    $sth = do_query($lyricDbh, $query, FALSE);

    print "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n";
    print "<HTML>\n<HEAD><TITLE>Lyricue: "
      . fromutf(gettext("Available songs"))
      . "</TITLE>\n";
    print
"<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">\n";
    print "<style type=\"text/css\">\n";
    print
"body {font-family: 'Droid Sans', 'Ubuntu', 'Canarell', sans; color: #555;}\n";
    print "#wrapper {max-width: 800px; margin: 0 auto; text-align: center;}\n";
    print
"table {width: 100%; border-collapse: collapse; box-shadow: 0 0 10px #aaa; margin: 20px 0;}\n";
    print
"td, th {padding: 8px; vertical-align: middle; border: 1px solid #ddd;}\n";
    print "tr:nth-child(even) {background-color: #eee;}\n";
    print "th {background-color: #eee;}\n";
    print "td:first-child {text-align: left;}\n";
    print "</style>\n</head>\n";
    print "<body><div id=wrapper>\n";
    print "<H1>Lyricue: " . fromutf(gettext("Available songs")) . "</H1>\n";
    print "<H2>"
      . gettext("Total number of songs available") . " : "
      . $totals[0]
      . "</H2>\n";
    print "<table>\n";
    print "<TR><TH>" . fromutf(gettext("Song Name")) . "</TH>";
    print "<TH>" . fromutf(gettext("Song Book")) . "</TH>";
    print "<TH>" . fromutf(gettext("Song Number")) . "</TH>";
    print "<TH>" . fromutf(gettext("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</div>\n</body>\n</html>\n";
    $lyricDbh->disconnect;
    exit;
}

#***

#****f* lyricue/backdrop_clicked
# NAME
#   backdrop_clicked
#   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, $event) = @_;
    debug("Backdrop clicked");

    # Right-click menu
    if ($event->button == 3) {
        popup_backdrop_menu($event);
        return TRUE;
    }
    if ($event->type eq '2button-press') {
        my $selected = "";
        my @list     = $widget->get_selected_items;
        if (defined $list[0]) {
            my $model = $widget->get_model;
            my $iter  = $model->get_iter($list[0]);
            $selected = $model->get($iter, 2);
        }
        if ($selected eq "") {
            return;
        }

        #$widget->unselect_all;

        if (@ASSOCIATE) {
            associate_bg($selected);
        } else {
            debug("Changing backdrop to " . $selected);
            if ($globals->{'bg_previews'}) {
                my $prevFile = $selected;
                my $scaled   = create_pixbuf(
                    $widgets->{'buttonCurr'}->{user_data},
                    $globals->{'icon_width'},
                    $globals->{'icon_height'}
                );

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

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

#***

#****f* lyricue/backdrop_preview_clicked
# NAME
#   backdrop_preview_clicked
# SYNOPSIS
#   backdrop_preview_clicked($widget)
# FUNCTION
#   Changing backdrop to
# INPUTS
#   $widget -
# OUTPUT
# SOURCE
#
sub backdrop_preview_clicked {
    my ($widget) = @_;
    my $selected = $widget->{user_data};
    debug("Changing backdrop to " . $selected);
    if ($globals->{'bg_previews'}) {
        my $prevFile = $selected;
        my $scaled   = create_pixbuf(
            $widgets->{'buttonCurr'}->{user_data},
            $globals->{'icon_width'},
            $globals->{'icon_height'}
        );

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

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

#***

#****f* lyricue/do_add_image
# NAME
#   do_add_image
# SYNOPSIS
#   do_add_image ()
# FUNCTION
#   Add an image to the playlist
# OUTPUT
#   Image added to playlist
# SOURCE
#
sub do_add_image {
    my ($widget) = @_;
    debug("do add image");
    my $selection = $widgets->{'image'}->get_object('treeImage')->get_selection;
    my @list      = $selection->get_selected_rows();
    my $model     = $widgets->{'image'}->get_object('treeImage')->get_model();
    my $playlist =
      $widgets->{'image'}->get_object('optionImageSublist')
      ->get_menu->get_active->{'user_data'};
    foreach (@list) {
        my $image = $model->get($model->get_iter($_), 1);
        add_image_item($playlist, $image);
    }
    close_dialog($widget);
    update_playlist();
}

#***

#****f* lyricue/add_item
# NAME
#   add_item
# SYNOPSIS
#   add_item($playlist, $type, $data)
# FUNCTION
#           debug($image);
# INPUTS
#   $playlist -
#   $type
#   $data
# OUTPUT
# SOURCE
#
sub add_item {
    my ($playlist, $type, $data) = @_;
    my $playorder = 1;

    my $query = "SELECT MAX(playorder) FROM playlist";
    my $sth = do_query($lyricDbh, $query, FALSE);

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

    debug($data);
    $query =
        "INSERT INTO playlist (playorder, playlist, type, data) VALUES ("
      . $playorder . ", "
      . $playlist . ", \""
      . $type
      . "\", \""
      . $data . "\")";
    $sth = do_query($lyricDbh, $query, FALSE);

    #if ($type eq "imag") {
    #    if ($data =~ /^dir;/) {
    #        $data =~ s/^dir;//g;
    #        $type="file";
    #    }
    #}
    if (($type eq "file") || ($type eq "imag")) {
        if ($type eq "file") {
            $data = "dir;" . $data;
        }
        my $scaled = create_pixbuf(
            $data,
            $globals->{'snapshot_width'},
            $globals->{'snapshot_height'}
        );
        if ($scaled) {
            my $snapshot = $scaled->save_to_buffer("jpeg");
            $sth = $lyricDbh->prepare(
                q{UPDATE playlist SET snapshot = ? WHERE playorder=?})
              || database_failure($errorcodes->{'sqlprepare'},
                $! . "\nSQL: " . $query);
            my $rv = $sth->execute($snapshot, $playorder)
              || database_failure($errorcodes->{'sqlexecute'},
                $! . "\nSQL: " . $query);
        }
    }
    $playorder++;
    return $playorder;
}

#***
#****f* lyricue/add_image_item
# NAME
#   add_image_item
# SYNOPSIS
#   add_image_item($playlist, $image)
# FUNCTION
#           debug($image);
# INPUTS
#   $playlist -
#    $image -
# OUTPUT
# SOURCE
#
sub add_image_item {
    my ($playlist, $image) = @_;
    add_item($playlist, "imag", $image);
}

#***

#****f* lyricue/show_about
# NAME
#   show_about
# SYNOPSIS
#   chose_about()
# FUNCTION
#   Open the About dialog
# OUTPUT
#   About dialog
# SOURCE
#
sub show_about {
    debug("Showing About dialog");
    my $xml       = load_window("windowAbout");
    my $abouttext = $xml->get_object('labelVersion')->get_text();
    $abouttext =~ s/#VERSION#/$globals->{'version'}/g;
    $xml->get_object('labelVersion')->set_text($abouttext);
    $xml->get_object('windowAbout')->show();
}

#***

#****f* lyricue/choose_playlist
# NAME
#   choose_playlist
# SYNOPSIS
#   choose_playlist ( )
# FUNCTION
#   Open a dialog to choose your main playlist
# OUTPUT
#   Search dialog
# SOURCE
#
sub choose_playlist {
    debug("Choose playlist");
    $widgets->{'main'}->get_object('vboxChoosePlay')->show_all;
    $widgets->{'main'}->get_object('scrollIconPlaylist')->hide;
    $widgets->{'main'}->get_object('scrollPlaylist')->hide;
    $widgets->{'main'}->get_object('toolbarPlaylist')->hide;
    $widgets->{'main'}->get_object('checkPreviews')->hide;
    update_cplayclist();
}

#***

#****f* lyricue/close_playlist_chooser
# NAME
#   close_playlist_chooser
# SYNOPSIS
#   close_playlist_chooser ( )
# FUNCTION
#   Close the playlist chooser
# SOURCE
#
sub close_playlist_chooser {
    debug("Close playlist chooser");
    $widgets->{'main'}->get_object('vboxChoosePlay')->hide;
    $widgets->{'main'}->get_object('checkPreviews')->show_all;
    if ($globals->{'image_playlist'}) {
        $widgets->{'main'}->get_object('scrollIconPlaylist')->show_all;
        $widgets->{'main'}->get_object('scrollPlaylist')->hide;
        $widgets->{'main'}->get_object('toolbarPlaylist')->hide;
        $widgets->{'main'}->get_object('frameMainLeft')->hide;
    } else {
        $widgets->{'main'}->get_object('scrollIconPlaylist')->hide;
        $widgets->{'main'}->get_object('scrollPlaylist')->show_all;
        $widgets->{'main'}->get_object('toolbarPlaylist')->show_all;
        $widgets->{'main'}->get_object('frameMainLeft')->show_all;
    }
}

#***

#****f* lyricue/create_dialog_prefs
# NAME
#   create_dialog_prefs
# SYNOPSIS
#   create_dialog_prefs()
# FUNCTION
#   Open the preferences dialog
# OUTPUT
#   preferences dialog
# SOURCE
#
sub create_dialog_prefs {
    debug("Creating preferences dialog");
    $config = load_config($globals->{'profile'});
    if (defined $widgets->{'prefs'}) {
        close_dialog($widgets->{'prefs'}->get_object('dialogPrefs'));
    }
    $widgets->{'prefs'} = load_window("dialogPrefs");
    $widgets->{'prefs'}->get_object('spinPrefHeight')
      ->set_value($config->{'Height'});
    $widgets->{'prefs'}->get_object('spinPrefWidth')
      ->set_value($config->{'Width'});
    $widgets->{'prefs'}->get_object('spinPrefOverscanV')
      ->set_value($config->{'OverscanV'});
    $widgets->{'prefs'}->get_object('spinPrefOverscanH')
      ->set_value($config->{'OverscanH'});
    $widgets->{'prefs'}->get_object('checkPrefXinerama')
      ->set_active($config->{'Xinerama'});
    $widgets->{'prefs'}->get_object('checkPrefMouse')
      ->set_active($config->{'IgnoreMouse'});
    $widgets->{'prefs'}->get_object('entryPrefWindowOffset')
      ->set_text($config->{'GeometryOverride'});

    if ($config->{'VerticalLocation'}) {
        $widgets->{'prefs'}->get_object('comboPrefVertical')
          ->prepend_text($config->{'VerticalLocation'});
    }
    $widgets->{'prefs'}->get_object('comboPrefVertical')->set_active(0);
    if ($config->{'HorizontalLocation'}) {
        $widgets->{'prefs'}->get_object('comboPrefHorizontal')
          ->prepend_text($config->{'HorizontalLocation'});
    }
    $widgets->{'prefs'}->get_object('comboPrefHorizontal')->set_active(0);
    if ($config->{'Justification'}) {
        $widgets->{'prefs'}->get_object('comboPrefJustification')
          ->prepend_text($config->{'Justification'});
    }
    $widgets->{'prefs'}->get_object('comboPrefJustification')->set_active(0);
    if ($config->{'DefaultTransition'}) {
        $widgets->{'prefs'}->get_object('comboPrefDefTrans')
          ->prepend_text($config->{'DefaultTransition'});
    }
    $widgets->{'prefs'}->get_object('comboPrefDefTrans')->set_active(0);

    $widgets->{'prefs'}->get_object('entryPrefHeader')
      ->set_text($config->{'Header'});
    $widgets->{'prefs'}->get_object('entryPrefMain')
      ->set_text($config->{'Main'});
    $widgets->{'prefs'}->get_object('entryPrefFooter')
      ->set_text($config->{'Footer'});
    $widgets->{'prefs'}->get_object('entryPrefOSD')->set_text($config->{'OSD'});
    $widgets->{'prefs'}->get_object('entryPrefColour')
      ->set_text($config->{'Colour'});
    $widgets->{'prefs'}->get_object('entryPrefShadowColour')
      ->set_text($config->{'ShadowColour'});
    $widgets->{'prefs'}->get_object('spinPrefShadow')
      ->set_value($config->{'ShadowSize'});
    $widgets->{'prefs'}->get_object('checkPrefLoop')
      ->set_active($config->{'Loop'});
    $widgets->{'prefs'}->get_object('checkPrefExpandNew')
      ->set_active($config->{'ExpandNew'});
    $widgets->{'prefs'}->get_object('checkPrefAudit')
      ->set_active($config->{'Audit'});
    $widgets->{'prefs'}->get_object('checkPrefView')
      ->set_active($config->{'DynamicPreview'});
    $widgets->{'prefs'}->get_object('checkPrefMiniview')
      ->set_active($config->{'Miniview'});
    my $scaled = create_pixbuf(
        $config->{'BGImage'},
        $globals->{'icon_width'},
        $globals->{'icon_height'}
    );

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

    my (@list);
    my $bibles = $config->{'Bibles'};
    my $store  = $widgets->{'prefs'}->get_object('comboPrefBible')->get_model();
    if ($store) {
        $store->clear();
    } else {
        $store = Gtk2::ListStore->new('Glib::String', 'Glib::String');
        $widgets->{'prefs'}->get_object('comboPrefBible')->set_model($store);
    }
    my $iter_sel;
    foreach (sort keys %$bibles) {
        my $bible = $_ . ":" . $bibles->{$_};
        my $btext = $bibles->{$_};
        $btext =~ s/^.*;//g;
        my $iter = $store->append;
        $store->set($iter, 0, $btext, 1, $bible);
        if ($bible eq $config->{'DefBible'}) {
            $iter_sel = $iter;
        }
    }
    if (defined $iter_sel) {
        $widgets->{'prefs'}->get_object('comboPrefBible')
          ->set_active_iter($iter_sel);
    }
    my $cell = Gtk2::CellRendererText->new;
    $widgets->{'prefs'}->get_object('comboPrefBible')->pack_start($cell, TRUE);
    $widgets->{'prefs'}->get_object('comboPrefBible')
      ->add_attribute($cell, text => 0);
    $widgets->{'prefs'}->get_object('entryPrefSpecialSong')
      ->set_text($config->{'SpecialSong'});
    $widgets->{'prefs'}->get_object('entryPrefSpecialImage')
      ->set_text($config->{'SpecialImage'});
    $widgets->{'prefs'}->get_object('entryPrefSpecialBack')
      ->set_text($config->{'SpecialBack'});
    $config->{'ImageDirectory'} =~ s/^\~/$ENV{'HOME'}/;
    $widgets->{'prefs'}->get_object('filePrefSpecialImagedir')
      ->set_label($config->{'ImageDirectory'});
    $widgets->{'prefs'}->get_object('filePrefSpecialImagedir')->{'user_data'} =
      "img";
    $config->{'BGDirectory'} =~ s/^\~/$ENV{'HOME'}/;
    $widgets->{'prefs'}->get_object('filePrefSpecialBGdir')
      ->set_label($config->{'BGDirectory'});
    $widgets->{'prefs'}->get_object('filePrefSpecialBGdir')->{'user_data'} =
      "bg";

    # Profiles
    my $renderer = Gtk2::CellRendererText->new;
    $widgets->{'prefs'}->get_object('comboPrefsProfile')
      ->pack_start($renderer, TRUE);
    $widgets->{'prefs'}->get_object('comboPrefsProfile')
      ->add_attribute($renderer, text => 0);
    my $profilestore = Gtk2::ListStore->new('Glib::String');
    $widgets->{'prefs'}->get_object('comboPrefsProfile')
      ->set_model($profilestore);

    my $query = "SELECT DISTINCT profile FROM config WHERE profile != ''";
    my $sth   = do_query($lyricDbh, $query, FALSE);
    my $combo_model = Gtk2::ListStore->new('Glib::String');
    $profilestore->set($profilestore->append, 0, gettext("Create New Profile"));
    while (my @row = $sth->fetchrow_array()) {
        my $iter = $profilestore->append;
        $profilestore->set($iter, 0, $row[0]);
        $combo_model->set($combo_model->append, 0, $row[0]);
        if ($row[0] eq $globals->{'profile'}) {
            $widgets->{'prefs'}->get_object('comboPrefsProfile')
              ->set_active_iter($iter);
        }
    }


    $widgets->{'prefs'}->get_object('entryPrefHeader')
      ->signal_connect("button_press_event", "open_dialogFont", "Header");
    $widgets->{'prefs'}->get_object('entryPrefMain')
      ->signal_connect("button_press_event", "open_dialogFont", "Main");
    $widgets->{'prefs'}->get_object('entryPrefFooter')
      ->signal_connect("button_press_event", "open_dialogFont", "Footer");
    $widgets->{'prefs'}->get_object('entryPrefOSD')
      ->signal_connect("button_press_event", "open_dialogFont", "OSD");
    $widgets->{'prefs'}->get_object('entryPrefColour')
      ->signal_connect("button_press_event", "open_dialogColour", "Font");
    $widgets->{'prefs'}->get_object('entryPrefShadowColour')
      ->signal_connect("button_press_event", "open_dialogColour", "Shadow");
    $widgets->{'prefs'}->get_object('comboPrefsProfile')
      ->signal_connect("changed", "change_pref_profile", "prefs");
}

#***

#****f* lyricue/change_windowoffset
# NAME
#   change_windowoffset
# SYNOPSIS
#   change_windowoffset
# FUNCTION
#   Show window for offset setting
# INPUTS
# OUTPUT
#   Application loaded on second head
# SOURCE
#
#***
sub change_windowoffset {
    debug("Showing offset window");
    my $offsetxml = load_window("dialogWindowOffset");
    $offsetxml->get_object('dialogWindowOffset')
      ->parse_geometry(
        $widgets->{'prefs'}->get_object('entryPrefWindowOffset')->get_text);
    my $confirm = $offsetxml->get_object('dialogWindowOffset')->run();
    if ($confirm == 1) {
        my ($x, $y) =
          $offsetxml->get_object('dialogWindowOffset')->get_position;
        $widgets->{'prefs'}->get_object('entryPrefWindowOffset')
          ->set_text("+" . $x . "+" . $y);
    }
    close_dialog($offsetxml->get_object('dialogWindowOffset'));
}

#***

#****f* lyricue/change_pref_profile
# NAME
#   change_pref_profile
# SYNOPSIS
#   change_pref_profile($widget)
# FUNCTION
#   Change preferences profile
# INPUTS
#   $widget - Calling widget
# OUTPUT
#   Stuff shown or hidden
# SOURCE
#
sub change_pref_profile {
    my ($widget, $caller) = @_;
    debug("Changing preferences profile");
    my $newprofile = gettext("Create New Profile");
    if ($caller eq "prefs") {
        my $hoststore =
          $widgets->{'prefs'}->get_object('comboPrefsProfile')->get_model();
        $newprofile =
          $widgets->{'prefs'}->get_object('comboPrefsProfile')
          ->get_active_text();
    }
    if ($newprofile eq gettext("Create New Profile")) {
        my $newxml = load_window("dialogPromptEntry");
        $newxml->get_object('dialogPromptEntry')
          ->set_title(fromutf(gettext("Create new profile")));
        $newxml->get_object('labelPromptE')
          ->set_text(fromutf(gettext("Name of profile")));

        my $response = $newxml->get_object('dialogPromptEntry')->run();
        if ($response eq "ok") {
            $globals->{'profile'} =
              $newxml->get_object('entryPromptE')->get_text();
        }
        close_dialog($newxml->get_object('dialogPromptEntry'));
    } else {
        $globals->{'profile'} = $newprofile;
    }
    create_dialog_prefs();
}

#***

#****f* lyricue/change_pref_xinerama
# NAME
#   change_pref_xinerama
# SYNOPSIS
#   change_pref_xinerama($widget)
# FUNCTION
#   Show/Hide xinerama-specific bits
# INPUTS
#   $widget - Calling widget
# OUTPUT
#   Stuff shown or hidden
# SOURCE
#
sub change_pref_xinerama {
    my ($widget) = @_;
    debug("Hide/Show xinerama stuff");
    foreach my $name ('buttonPrefWindowOffset', 'labelPrefWindowOffset',
        'entryPrefWindowOffset')
    {
        if ($widget->get_active) {
            $widgets->{'prefs'}->get_object($name)->show;
        } else {
            $widgets->{'prefs'}->get_object($name)->hide;
        }
    }
}

#***

#****f* 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 = "";
    $command = $app;
    debug("Executing app: " . $command);
    if ($config->{'Xinerama'}) {
        if ($command =~ /;$/) {
            $command =~ s/;$//;
            system($command . " &");
        } else {
            my $geom = "+"
              . ($widgets->{'main'}->get_object('windowMain')
                  ->get_screen->get_width - 100)
              . "+100";
            if ($config->{'GeometryOverride'}) {
                $geom = $config->{'GeometryOverride'};
            }
            system($command. " --geometry " . $geom . " &");
        }
    } else {
        system("DISPLAY=:0.1 " . $command . " &");
    }
}

#***

#****f* 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(@_);
}

#***

#****f* 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...");
}

#***

#****f* 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]);
        if ((!defined $config->{'DefBible'}) || ($config->{'DefBible'} eq "")) {
            $config->{'DefBible'} =
              $line[1] . ":" . $line[0] . ";" . $widget->get('label');
            debug("No default bible set previously, setting to "
                  . $config->{'DefBible'});
            write_config();
        }
    }
}

#***

#****f* lyricue/do_change_bible
# NAME
#   do_change_bible
# SYNOPSIS
#   do_change_bible ($type, $bibledb)
# FUNCTION
#   Change currently used bible
# INPUTS
#   $type - bible type (sword/db)
#   $bibledb - Bible name
# OUTPUT
#   Calls update_playlist
# SOURCE
#
sub do_change_bible {
    my ($type, $bibledb) = @_;
    debug("Change bible");
    $globals->{'bibledb'} = $bibledb;
    if (!$globals->{'usesword'}) {
        if ($bibleDbh != NULL) {
            $bibleDbh->disconnect;
            $bibleDbh = NULL;
        }
    }
    if ($type eq "db") {
        $globals->{'usesword'} = FALSE;
        my ($table, $dbname) = split(/@/, $globals->{'bibledb'}, 2);
        if ((!(defined $dbname)) || ($dbname eq "")) {
            $dbname               = $table;
            $table                = "verse";
            $globals->{'bibledb'} = $table . "@" . $dbname;
        }
        $bibleDbh =
          db_connect($dbname, $errorcodes->{'bibledbopen'} . $dbname, FALSE);
    } else {
        $globals->{'usesword'} = TRUE;
    }
    update_display("change_to_db", $bibledb, $type);
    preview_display("change_to_db", $bibledb, $type, "MINI");
}

#***

#****f* 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();
}

#***

#****f* lyricue/export_song
# NAME
#   export_song
# SYNOPSIS
#   export_song ()
# FUNCTION
#   Show a dialog to choose where to save an exported song to
# OUTPUT
#   File dialog displayed
# SOURCE
#
sub export_song {
    debug("Export song");
    my $filexml = load_window("dialogFileChooser");
    $filexml->get_object('buttonFileOK')
      ->signal_connect("clicked", "complete_export_song", $filexml);
    $filexml->get_object('dialogFileChooser')
      ->set_title(gettext("Enter filename for export"));
    $filexml->get_object('dialogFileChooser')->show_all();
}

#***

#****f* 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
#   $filexml - 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_object('dialogFileChooser')->get_filename;
    close_dialog($widget);
    debug(" to file: " . $export);

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

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

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

        push @pages, $tmppage;
    }
    my $song = {
        'name' => [$widgets->{'add'}->get_object('entryEditName')->get_text()],
        'number' =>
          [$widgets->{'add'}->get_object('entryEditNumber')->get_text()],
        'book' => [$widgets->{'add'}->get_object('entryEditBook')->get_text()],
        'artist' =>
          [$widgets->{'add'}->get_object('entryEditArtist')->get_text()],
        'keywords' =>
          [$widgets->{'add'}->get_object('entryEditKeywords')->get_text()],
        'copyright' =>
          [$widgets->{'add'}->get_object('entryEditCopyright')->get_text()],
        'page' => \@pages
    };

    my $out = {'song' => [$song]};

    my $writer = XML::Simple->new();
    open my $fh, ">" . $export . "";
    $writer->XMLout($out, OutputFile => $fh, RootName => 'lyricue');
    close $fh;

}

#***

#****f* lyricue/do_adv_search
# NAME
#   do_adv_search
# SYNOPSIS
#   do_adv_search ()
# FUNCTION
#   Do an advanced search where it searchs lyrics instead of song titles
# OUTPUT
#   List of suitable songs
# SOURCE
#
sub do_adv_search {
    my $search_text =
      $widgets->{'search'}->get_object('entrySearchSongs')->get_text();
    if ($search_text ne "") {
        debug("Searching for " . $search_text);
        my $store = $widgets->{'search'}->get_object('treeSearch')->get_model();
        if ($store) {
            $store->clear;
        } else {
            $store = Gtk2::ListStore->new(
                'Glib::String', 'Glib::String',
                'Glib::String', 'Glib::String',
                'Glib::String'
            );
            $widgets->{'search'}->get_object('treeSearch')->set_model($store);
            my $column = Gtk2::TreeViewColumn->new_with_attributes(
                fromutf(gettext("Title")),
                Gtk2::CellRendererText->new, text => 0);
            $widgets->{'search'}->get_object('treeSearch')
              ->append_column($column);
            $column = Gtk2::TreeViewColumn->new_with_attributes(
                fromutf(gettext("Artist")),
                Gtk2::CellRendererText->new, text => 1);
            $widgets->{'search'}->get_object('treeSearch')
              ->append_column($column);
            $column = Gtk2::TreeViewColumn->new_with_attributes(
                fromutf(gettext("Book")),
                Gtk2::CellRendererText->new, text => 2);
            $widgets->{'search'}->get_object('treeSearch')
              ->append_column($column);
            $column = Gtk2::TreeViewColumn->new_with_attributes(
                fromutf(gettext("Song No")),
                Gtk2::CellRendererText->new, text => 3);
            $widgets->{'search'}->get_object('treeSearch')
              ->append_column($column);
        }
        my $query =
"SELECT id,title,songnum,book,artist,count(id) as count FROM lyricMain,page WHERE lyricMain.id=page.songid AND (page.lyrics LIKE \"%"
          . $search_text
          . "%\" OR title LIKE \"%"
          . $search_text
          . "%\" OR artist LIKE \"%"
          . $search_text
          . "%\" OR keywords LIKE \"%"
          . $search_text
          . "%\") GROUP BY id ORDER BY count DESC";
        $sth = do_query($lyricDbh, $query, FALSE);
        while ($row = $sth->fetchrow_hashref()) {
            my $iter = $store->append;
            $store->set(
                $iter,             0, $row->{'title'}, 1,
                $row->{'artist'},  2, $row->{'book'},  3,
                $row->{'songnum'}, 4, $row->{'id'}
            );
        }
    }
}

#***

#****f* 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_object('treeSearch')->get_selection;
    my ($model, $iter) = $selection->get_selected;
    if ($iter) {
        my $songid = $model->get($iter, 4);
        debug("Songid \"" . $songid . "\" selected");
        my $query =
            "SELECT lyrics FROM page WHERE songid="
          . $songid
          . " ORDER BY pagenum";
        $sth = do_query($lyricDbh, $query, FALSE);
        my $lyrics = "";

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

#***

#****f* 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 ($source, $placement, $dest) = @_;
    debug("Moving " . $source . " to " . $placement . " " . $dest);
    my $query = "SELECT playlist FROM playlist WHERE playorder=" . $source;
    my $sth = do_query($lyricDbh, $query, FALSE);
    my @playlist = $sth->fetchrow_array;

    # Get list of items in this playlist
    $query =
        "SELECT playorder FROM playlist WHERE playlist="
      . $playlist[0]
      . " ORDER BY playorder";
    $sth = do_query($lyricDbh, $query, FALSE);
    my @items = ();
    while (my @row = $sth->fetchrow_array) {
        push @items, $row[0];
    }

    my $sourceitem = 0;
    my $destitem   = 0;
    foreach my $i (0 .. @items - 1) {
        debug($i . "-" . $items[$i]);
        if ($items[$i] == $source) {
            $sourceitem = $i;
        }
        if ($items[$i] == $dest) {
            $destitem = $i;
        }
    }
    debug($sourceitem . ":" . $destitem);

    if ($placement eq "before") {
        $destitem--;
        if ($destitem < 0) {
            $destitem = 0;
        }
    } elsif ($placement eq "after") {
        $destitem++;
    }
    $dest = $items[$destitem];
    if (!defined($dest)) {
        return;
    }
    debug($source . ":" . $dest);

    while ($source != $dest) {
        debug("Moving " . $source . " to " . $dest);
        if ($source < $dest) {
            start_transaction();
            {
                renumber_item($source,                 -1);
                renumber_item($items[$sourceitem + 1], $source);
                renumber_item(-1,                      $items[$sourceitem + 1]);
                $lyricDbh->commit();
            };
            end_transaction($@);
            $sourceitem++;
        } else {
            start_transaction();
            {
                renumber_item($source,                 -1);
                renumber_item($items[$sourceitem - 1], $source);
                renumber_item(-1,                      $items[$sourceitem - 1]);
            };
            end_transaction($@);
            $sourceitem--;
        }
        $source = $items[$sourceitem];
    }
    update_playlist($dest);
}

#***

#****f* lyricue/move_item_up
# NAME
#   move_item_up
# SYNOPSIS
#   move_item_up ()
# FUNCTION
#   Move item in the playlist up
# OUTPUT
#   Calls move_item to do it
# SOURCE
#
sub move_item_up {
    my $selection =
      $widgets->{'main'}->get_object('treePlaylist')->get_selection;
    my ($model, $iter) = $selection->get_selected;
    if ($iter) {
        my $item = $model->get($iter, 2);
        move_item($item, 'before', $item);
    }
}

#***

#****f* lyricue/move_item_down
# NAME
#   move_item_down
# SYNOPSIS
#   move_item_down ()
# FUNCTION
#   Move item in the playlist down
# OUTPUT
#   Calls move_item to do it
# SOURCE
#
sub move_item_down {
    my $selection =
      $widgets->{'main'}->get_object('treePlaylist')->get_selection;
    my ($model, $iter) = $selection->get_selected;
    if ($iter) {
        my $item = $model->get($iter, 2);
        move_item($item, 'after', $item);
    }
}

#***

#****f* 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;
    $sth = do_query($lyricDbh, $query, FALSE);
    $row = $sth->fetchrow_hashref();

    $query =
      "UPDATE playlist SET playorder=" . $after . " WHERE playorder=" . $before;
    $sth = do_query($lyricDbh, $query, FALSE);

    $query =
        "UPDATE associations SET playlist="
      . $after
      . " WHERE playlist="
      . $before;
    $sth = do_query($lyricDbh, $query, FALSE);
}

#***

#****f* 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_object('entryEditCopyright')
      ->set_text("Preset" . $preset);
}

#***

#****f* lyricue/debug
# NAME
#   debug
# SYNOPSIS
#   debug ($text)
# FUNCTION
#   Always write to logfile
#   Print $text if $globals->{'debugging'} is set
# INPUTS
#   $text - the text to output
# OUTPUT
#   Text to Logfile and possible STDERR
# SOURCE
#
sub debug {
    my $text = shift;
    chomp($text);
    if ($text) {
        my ($sec, $min, $hour, undef) = localtime(time);
        $text =
          sprintf("%02d:%02d:%02d|INTERFACE: %s\n", $hour, $min, $sec, $text);
        print LOG $text;
        if ($globals->{'debugging'}) {
            print STDERR $text;
        }
    }
    return TRUE;
}

#***

#****f* lyricue/qdebug
# NAME
#   qdebug
# SYNOPSIS
#   qdebug ($text)
# FUNCTION
#   Debug data
# INPUTS
#   $text - the text to output
# OUTPUT
#   Call to debug
# SOURCE
#
sub qdebug {
    if ($globals->{'debugging'} == 2) {
        debug(@_);
    }
}

#***

#****f* lyricue/select_playlist
# NAME
#   select_playlist
# SYNOPSIS
#   select_playlist ($widget)
# FUNCTION
#   Select the main playlist
# INPUTS
#   $widget - calling widget
# OUTPUT
#   Closed selection dialog
# SOURCE
#
sub select_playlist {
    my ($widget) = @_;
    debug("Selecting a main playlist");

    my $playlist   = "";
    my $playlistid = 0;
    my $selection =
      $widgets->{'main'}->get_object('treeChoosePlay')->get_selection;
    if ($selection) {
        my ($m, $i) = $selection->get_selected;
        if ($m) {
            $playlist   = $m->get($i, 0);
            $playlistid = $m->get($i, 1);
        }
    }

    if ($playlist eq "") {
        return;
    }
    if ($playlistid) {
        $widgets->{'main'}->get_object('labelCurrentPlaylist')
          ->set_text($playlist);
        $widgets->{'main'}->get_object('labelCurrentPlaylist')->{user_data} =
          $playlistid;
        update_playlist();
        close_playlist_chooser();
    }
}

#***

#****f* lyricue/select_playlist_click
# NAME
#   select_playlist_click
# SYNOPSIS
#   select_playlist_click ($widget, $event)
# FUNCTION
#   Select the main playlist on double-click
# INPUTS
#   $widget - calling widget
#   $event - calling event
# OUTPUT
#   Closed selection dialog
# SOURCE
#
sub select_playlist_click {
    my ($widget, $event) = @_;
    debug("select_playlist_click");
    if ($event->type eq '2button-press') {
        select_playlist($widget);
    }
}

#***

#****f* 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");
    my $playlist = "";
    my $newxml   = load_window("dialogPromptEntry");
    $newxml->get_object('dialogPromptEntry')
      ->set_title(fromutf(gettext("Create new playlist")));
    $newxml->get_object('labelPromptE')
      ->set_text(fromutf(gettext("Name of playlist")));

    my $response = $newxml->get_object('dialogPromptEntry')->run();
    if ($response eq "ok") {
        $playlist = $newxml->get_object('entryPromptE')->get_text();
    }
    close_dialog($newxml->get_object('dialogPromptEntry'));
    if ($playlist eq "") {
        return;
    }

    my $query = "SELECT MAX(id)+1 FROM playlists";
    my $sth   = do_query($lyricDbh, $query, FALSE);
    my @row = $sth->fetchrow_array();
    $query =
        "INSERT INTO playlists (id,title, profile) VALUES ("
      . $row[0] . ", \""
      . $playlist
      . "\", \""
      . $globals->{'profile'} . "\")";
    $sth = do_query($lyricDbh, $query, FALSE);
    $query =
        "SELECT id FROM playlists WHERE title LIKE \""
      . $playlist
      . "\" AND profile=\""
      . $globals->{'profile'} . "\"";
    $sth = do_query($lyricDbh, $query, FALSE);
    @row = $sth->fetchrow_array();

    if ($row[0]) {
        $widgets->{'main'}->get_object('labelCurrentPlaylist')
          ->set_text($playlist);
        $widgets->{'main'}->get_object('labelCurrentPlaylist')->{user_data} =
          $row[0];
        update_playlist();
        close_playlist_chooser();
    }
}

#***

#****f* lyricue/copy_playlist
# NAME
#   copy_playlist
# SYNOPSIS
#   copy_playlist ($widget)
# FUNCTION
#   Copy a playlist
# INPUTS
#   $widget - Calling widget
# OUTPUT
#   Closed
# SOURCE
sub copy_playlist {
    my ($widget) = @_;
    debug("Copying playlist");
    my $playlist   = "";
    my $playlistid = 0;
    my $selection =
      $widgets->{'main'}->get_object('treeChoosePlay')->get_selection;
    if ($selection) {
        my ($m, $i) = $selection->get_selected;
        if ($m) {
            $playlist   = $m->get($i, 0);
            $playlistid = $m->get($i, 1);
        }
    }

    if ($playlistid > 0) {
        my $copyxml   = load_window("dialogPromptEntry");
        my $labelText = fromutf(gettext("Copying ")) . $playlist;
        $copyxml->get_object('dialogPromptEntry')->set_title($labelText);
        $copyxml->get_object('labelPromptE')->set_text($labelText);
        $copyxml->get_object('labelPromptE')->{user_data} = $playlistid;
        $copyxml->get_object('entryPromptE')->{user_data} = $playlist;
        $copyxml->get_object('entryPromptE')->set_text($playlist);
        $copyxml->get_object('buttonPromptEOK')
          ->signal_connect("clicked", "do_copy_playlist", $copyxml);
    }
}

#***

#****f* 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");
    my $playlist   = "";
    my $playlistid = 0;
    my $selection =
      $widgets->{'main'}->get_object('treeChoosePlay')->get_selection;
    if ($selection) {
        my ($m, $i) = $selection->get_selected;
        if ($m) {
            $playlist   = $m->get($i, 0);
            $playlistid = $m->get($i, 1);
        }
    }

    if ($playlistid > 0) {
        my $renamexml = load_window("dialogPromptEntry");
        my $labelText = fromutf(gettext("Renaming ")) . $playlist;
        $renamexml->get_object('dialogPromptEntry')->set_title($labelText);
        $renamexml->get_object('labelPromptE')->set_text($labelText);
        $renamexml->get_object('labelPromptE')->{user_data} = $playlistid;
        $renamexml->get_object('entryPromptE')->set_text($playlist);
        $renamexml->get_object('buttonPromptEOK')
          ->signal_connect("clicked", "do_rename_playlist", $renamexml);
    }
}

#***

#****f* lyricue/do_copy_playlist
# NAME
#   do_copy_playlist
# SYNOPSIS
#   do_copy_playlist ($widget, $copyxml)
# FUNCTION
#   Copy a playlist
# INPUTS
#   $widget - Calling widget
#   $copyxml - Dialog widgets
# OUTPUT
#   Closed
# SOURCE
#
sub do_copy_playlist {
    my ($widget, $copyxml) = @_;
    debug("Do copy playlist");
    my $playlist       = $copyxml->get_object('entryPromptE')->get_text;
    my $playlistid_src = $copyxml->get_object('labelPromptE')->{user_data};

    if ($playlist eq $copyxml->get_object('entryPromptE')->{user_data}) {
        debug("Name unchanged");
        return;
    }
    my $query =
"SELECT id FROM playlists LEFT JOIN playlist ON playlist.data=playlists.id AND playlist.data NOT LIKE '%-%' AND (type='play' OR type='sub') WHERE data IS NULL AND playlists.id > 0 AND title='"
      . $playlist
      . "' and profile='"
      . $globals->{'profile'}
      . "' ORDER BY id";
    my $sth = do_query($lyricDbh, $query, FALSE);
    if ($sth->fetchrow_array()) {
        debug("Duplicate");
        update_cplayclist();
        close_dialog($widget);
    } else {
        debug("Copying playlist");
        my $query = "SELECT MAX(id)+1 FROM playlists";
        my $sth   = do_query($lyricDbh, $query, FALSE);
        my @row            = $sth->fetchrow_array();
        my $playlistid_dst = $row[0];
        $widgets->{'main'}->get_object('labelCurrentPlaylist')
          ->set_text($playlist);
        $widgets->{'main'}->get_object('labelCurrentPlaylist')->{user_data} =
          $playlistid_dst;

        # iterate through source playlist and create new one
        copy_playlist2($playlistid_src, $playlistid_dst, $playlist);

        # Load the new playlist
        update_playlist();
        close_playlist_chooser();
    }
}

sub copy_playlist2 {
    my ($playlistid_src, $playlistid_dst, $playlist) = @_;
    my $query =
        "INSERT INTO playlists (id,title,profile) VALUES ("
      . $playlistid_dst . ", \""
      . $playlist
      . "\", \""
      . $globals->{'profile'} . "\")";
    my $sth = do_query($lyricDbh, $query, FALSE);

    $query =
        "SELECT playorder,data,type,transition FROM playlist WHERE playlist="
      . $playlistid_src
      . " ORDER BY playorder";

    $sth = do_query($lyricDbh, $query, FALSE);
    while (my $row = $sth->fetchrow_hashref()) {
        my $query2 = "SELECT MAX(playorder) FROM playlist";
        my $sth2   = do_query($lyricDbh, $query2, FALSE);
        my @row2      = $sth2->fetchrow_array();
        my $playorder = $row2[0] + 1;

        my $data            = $row->{'data'};
        my $transition      = $row->{'transition'};
        my $playlistid_dst2 = 0;
        my $newplaylist     = "";
        if ($row->{'type'} eq "play") {    # Playlist
            my $query3 = "SELECT MAX(id)+1 FROM playlists";
            my $sth3   = do_query($lyricDbh, $query3, TRUE);
            my @row3 = $sth3->fetchrow_array();
            $playlistid_dst2 = $row3[0];
            $query3 = "SELECT title FROM playlists WHERE id=" . $row->{'data'};
            $sth3   = do_query($lyricDbh, $query3, TRUE);
            @row3        = $sth3->fetchrow_array();
            $newplaylist = $row3[0];
            $data        = $playlistid_dst2;
        }

        $query2 =
"INSERT INTO playlist (playorder, playlist, data, type, transition) VALUES ("
          . $playorder . ", "
          . $playlistid_dst . ", '"
          . $data . "', '"
          . $row->{'type'} . "', "
          . $transition . ")";
        $sth2 = do_query($lyricDbh, $query2, FALSE);

        if ($row->{'type'} eq "play") {    # Playlist
            my $playlistid_src2 = $row->{'data'};
            copy_playlist2($playlistid_src2, $playlistid_dst2, $newplaylist);
        }
    }
}

#***

#****f* 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_object('entryPromptE')->get_text
      . "\" WHERE id="
      . $renamexml->get_object('labelPromptE')->{user_data};
    my $sth = do_query($lyricDbh, $query, FALSE);
    update_cplayclist();
    close_dialog($widget);
}

#***

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

    my $playlist = "";
    my $selection =
      $widgets->{'main'}->get_object('treeChoosePlay')->get_selection;
    if ($selection) {
        my ($m, $i) = $selection->get_selected;
        if ($m) {
            $playlist = $m->get($i, 1);
        }
    }

    if ($playlist eq "") {
        return;
    }

    my $query = "DELETE FROM playlists WHERE id=" . $playlist;
    my $sth = do_query($lyricDbh, $query, FALSE);

    $query =
      "DELETE FROM playlist WHERE data=" . $playlist . " AND type=\"play\"";
    $sth = do_query($lyricDbh, $query, FALSE);

    $query = "SELECT playorder FROM playlist WHERE playlist=" . $playlist;
    $sth = do_query($lyricDbh, $query, FALSE);
    while (my @row = $sth->fetchrow_array()) {
        remove_single_item($row[0]);
    }

    update_cplayclist();
}

#***

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

    my $query =
"SELECT title,id FROM playlists LEFT JOIN playlist ON playlist.data=playlists.id AND playlist.data NOT LIKE '%-%' AND (type='play' OR type='sub') WHERE data IS NULL AND playlists.id > 0 AND profile = '"
      . $globals->{'profile'}
      . "' ORDER BY id";
    my $sth = do_query($lyricDbh, $query, FALSE);
    my @row;
    while (@row = $sth->fetchrow_array()) {
        my $iter = $store->append;
        $store->set($iter, 0, fromutf($row[0]), 1, $row[1]);
    }
}

#***

#****f* lyricue/copy_item
# NAME
#   copy_item
# SYNOPSIS
#   copy_item ()
# FUNCTION
#   Copy the selected item in the playlist
# OUTPUT
#   Extra item in the playlist
# SOURCE
#
sub copy_item {
    my $selection =
      $widgets->{'main'}->get_object('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   = do_query($lyricDbh, $query, FALSE);
        my $row = $sth->fetchrow_hashref();

        if ($row->{'type'} ne "play") {
            my $query = "SELECT MAX(playorder) FROM playlist";
            my $sth   = do_query($lyricDbh, $query, FALSE);
            my @row2 = $sth->fetchrow_array();
            $row2[0]++;

            $query =
                "INSERT INTO playlist (playorder,playlist,data,type) VALUES ("
              . $row2[0] . ", "
              . $row->{'playlist'} . ", \""
              . $row->{'data'}
              . "\", \""
              . $row->{'type'} . "\")";
            $sth = do_query($lyricDbh, $query, FALSE);
        }

        update_playlist();
    }
}

#***

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

    my $selection =
      $widgets->{'main'}->get_object('treePlaylist')->get_selection;
    my ($model, $iter) = $selection->get_selected;
    if ($iter) {
        my $loopxml = load_window("dialogLoop");
        $loopxml->get_object('dialogLoop')->{user_data} = $model->get($iter, 2);
        $loopxml->get_object('buttonLoopOK')
          ->signal_connect('clicked', "establish_timer", $loopxml);
    }
}

#***

#****f* lyricue/establish_timer
# NAME
#   establish_timer
# SYNOPSIS
#   establish_timer ($widget, $loopxml)
# FUNCTION
#   Create a new timer
# OUTPUT
#   Gtk timer set
# SOURCE
#
sub establish_timer {
    my ($widget, $loopxml) = @_;

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

    my $seconds = $loopxml->get_object('spinLoopSeconds')->get_value_as_int();
    my $milliseconds =
      $loopxml->get_object('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_object('dialogLoop')->{user_data};
    my $sth = do_query($lyricDbh, $query, FALSE);
    my @row    = $sth->fetchrow_array();
    my $parent = $row[0];

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

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

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

#***

#****f* lyricue/update_loop
# NAME
#   update_loop
# SYNOPSIS
#   update_loop($data)
# FUNCTION
#   Loop triggered
# INPUTS
#   $data -
# OUTPUT
# SOURCE
#
sub update_loop {
    my ($data) = @_;
    debug("Loop triggered");
    update_display($$data[0], $$data[1], $$data[2]);
    return TRUE;
}

#***

#****f* lyricue/load_config
# NAME
#   load_config
# SYNOPSIS
#   load_config()
# FUNCTION
#   Loading Preferences
# INPUTS
# OUTPUT
# SOURCE
#
sub load_config {
    my ($hostname) = @_;
    my ($conf);
    debug("Loading Preferences for $hostname");
    my $bibleCount = 0;
    $conf->{'Apps'}->{gettext("Load Lyricue Display")} = "lyricue_display";
    $conf->{'Apps'}->{gettext("Close Lyricue Display")} =
      "lyricue_remote close";
    $conf->{'Width'}  = 0;
    $conf->{'Height'} = 0;

    my $query = "SELECT config_key, config_value FROM config WHERE profile='"
      . $hostname . "'";
    $sth = do_query($lyricDbh, $query, FALSE);
    my @row = $sth->fetchrow_array();
    if (!defined $row[0]) {
        firstrun_wizard();
        Gtk2->main();
        $conf = load_config($globals->{'profile'});
        load_profile_menu();
        return $conf;
    }
    $sth = do_query($lyricDbh, $query, FALSE);
    while ($row = $sth->fetchrow_hashref()) {
        $conf->{$row->{'config_key'}} = $row->{'config_value'};
    }

    $conf->{'BibleCount'} = $bibleCount;
    $conf->{'Bibles'}     = get_bibles();
    if (!defined $conf->{'HighlightColour'}) {
        $conf->{'HighlightColour'} = "yellow";
    }
    if (!defined $conf->{'TrayIcons'}) {
        $conf->{'TrayIcons'} = "1";
    }
    if ($conf->{'TrayIcons'} ne "1") {
        $globals->{'trayicon'} = FALSE;
    }
    $conf->{'LoopText'} = "";
    if (defined $conf->{'Loop'}) {
        if ($conf->{'Loop'} eq "1") {
            $conf->{'LoopText'} = "loop";
        }
    }
    if (defined $conf->{'BGImage'} && !($conf->{'BGImage'} =~ /;/)) {
        $conf->{'BGImage'} = "db;" . $conf->{'BGImage'};
    }
    if (defined $conf->{'ImageDirectory'} && !($conf->{'ImageDirectory'})) {
        $conf->{'ImageDirectory'} =
          Glib::get_user_special_dir('pictures') . "/";
    }
    if (defined $conf->{'ImageDirectory'} && !($conf->{'BGDirectory'})) {
        $conf->{'BGDirectory'} = Glib::get_user_special_dir('pictures') . "/";
    }

    if (!defined $conf->{'DefaultTransition'}) {
        $conf->{'DefaultTransition'} = "Fade";
    }
    if (   (!defined $conf->{'DBHost'})
        || ($globals->{'mysqlhost'} ne "localhost"))
    {
        $conf->{'DBHost'} = $globals->{'mysqlhost'};
    } else {
        $globals->{'mysqlhost'} = $conf->{'DBHost'};
    }

    # Set icon sizes according to aspect ratio
    if (($conf->{'Width'} != 0) && ($conf->{'Height'} != 0)) {
        $globals->{'display_aspect'} = $conf->{'Width'} / $conf->{'Height'};
        $globals->{'icon_height'} =
          $globals->{'icon_width'} / $globals->{'display_aspect'};
        $globals->{'thumb_height'} =
          $globals->{'thumb_width'} / $globals->{'display_aspect'};
        $globals->{'snapshot_height'} =
          $globals->{'snapshot_width'} / $globals->{'display_aspect'};
    }

    if (defined $widgets->{'main'}) {
        $widgets->{'main'}->get_object('windowMain')->set_title(gettext("Lyricue")." - ".$globals->{'profile'});
    }
    return $conf;
}

#***

#***

#****f* lyricue/save_state
# NAME
#   save_state
# SYNOPSIS
#   save_state()
# FUNCTION
#
# INPUTS
# OUTPUT
# SOURCE
#
sub save_state {
    $config->{'FrameLeft'} =
      $widgets->{'main'}->get_object('hpanedMainLeft')->get_position();
    $config->{'FrameRight'} =
      $widgets->{'main'}->get_object('hpanedMainRight')->get_position();
    my ($width, $height) =
      $widgets->{'main'}->get_object('windowMain')->get_size();
    if (defined $widgets->{'main'}->get_object('vpanedMain')) {
        $config->{'FrameMain'} =
          ($height -
              $widgets->{'main'}->get_object('vpanedMain')->get_position());
    }
}

#***

#****f* lyricue/save_and_close_prefs
# NAME
#   save_and_close_prefs
# SYNOPSIS
#   save_and_close_prefs()
# FUNCTION
#   Save and close preferences
# INPUTS
# OUTPUT
# SOURCE
#
sub save_and_close_prefs {
    debug("Save and close preferences");
    save_preferences();
    $globals->{'configured'} = TRUE;
    close_dialog($widgets->{'prefs'}->get_object('dialogPrefs'));
}

#***

#****f* lyricue/save_preferences
# NAME
#   save_preferences
# SYNOPSIS
#   save_preferences()
# FUNCTION
#   Saving preferences
# INPUTS
# OUTPUT
# SOURCE
#
sub save_preferences {
    debug("Saving preferences");
    $config->{'Main'} =
      $widgets->{'prefs'}->get_object('entryPrefMain')->get_text();
    $config->{'Header'} =
      $widgets->{'prefs'}->get_object('entryPrefHeader')->get_text();
    $config->{'Footer'} =
      $widgets->{'prefs'}->get_object('entryPrefFooter')->get_text();
    $config->{'OSD'} =
      $widgets->{'prefs'}->get_object('entryPrefOSD')->get_text();
    $config->{'Colour'} =
      $widgets->{'prefs'}->get_object('entryPrefColour')->get_text();
    $config->{'ShadowColour'} =
      $widgets->{'prefs'}->get_object('entryPrefShadowColour')->get_text();
    $config->{'ShadowSize'} =
      $widgets->{'prefs'}->get_object('spinPrefShadow')->get_value();
    $config->{'Height'} =
      $widgets->{'prefs'}->get_object('spinPrefHeight')->get_value();
    $config->{'Width'} =
      $widgets->{'prefs'}->get_object('spinPrefWidth')->get_value();
    $config->{'OverscanH'} =
      $widgets->{'prefs'}->get_object('spinPrefOverscanH')->get_value();
    $config->{'OverscanV'} =
      $widgets->{'prefs'}->get_object('spinPrefOverscanV')->get_value();
    $config->{'Loop'} =
      $widgets->{'prefs'}->get_object('checkPrefLoop')->get_active() ? 1 : 0;
    $config->{'ExpandNew'} =
      $widgets->{'prefs'}->get_object('checkPrefExpandNew')->get_active()
      ? 1
      : 0;
    $config->{'Audit'} =
      $widgets->{'prefs'}->get_object('checkPrefAudit')->get_active() ? 1 : 0;
    $config->{'DynamicPreview'} =
      $widgets->{'prefs'}->get_object('checkPrefView')->get_active() ? 1 : 0;
    $config->{'Miniview'} =
      $widgets->{'prefs'}->get_object('checkPrefMiniview')->get_active()
      ? 1
      : 0;
    $config->{'Xinerama'} =
      $widgets->{'prefs'}->get_object('checkPrefXinerama')->get_active()
      ? 1
      : 0;
    $config->{'IgnoreMouse'} =
      $widgets->{'prefs'}->get_object('checkPrefMouse')->get_active()
      ? 1
      : 0;
    $config->{'GeometryOverride'} =
      $widgets->{'prefs'}->get_object('entryPrefWindowOffset')->get_text();
    my $set = "";

    foreach my $value ("Top", "Bottom", "Centre") {
        if (
            fromutf(gettext($value)) eq ucfirst(
                $widgets->{'prefs'}->get_object('comboPrefVertical')
                  ->get_active_text()
            )
          )
        {
            $set = $value;
        }
    }
    if ($set eq "") {
        $set =
          $widgets->{'prefs'}->get_object('comboPrefVertical')
          ->get_active_text();
    }
    $config->{'VerticalLocation'} = $set;
    $set = "";
    my $set2 = "";
    foreach my $value ("Left", "Right", "Centre") {
        if (
            fromutf(gettext($value)) eq ucfirst(
                $widgets->{'prefs'}->get_object('comboPrefHorizontal')
                  ->get_active_text()
            )
          )
        {
            $set = $value;
        }
        if (
            fromutf(gettext($value)) eq ucfirst(
                $widgets->{'prefs'}->get_object('comboPrefJustification')
                  ->get_active_text()
            )
          )
        {
            $set2 = $value;
        }
    }
    if ($set eq "") {
        $set =
          $widgets->{'prefs'}->get_object('comboPrefHorizontal')
          ->get_active_text();
    }
    if ($set2 eq "") {
        $set2 =
          $widgets->{'prefs'}->get_object('comboPrefJustification')
          ->get_active_text();
    }
    $config->{'HorizontalLocation'} = $set;
    $config->{'Justification'}      = $set2;
    $set                            = "";
    foreach my $value ("Fade", "None") {
        if (
            fromutf(gettext($value)) eq ucfirst(
                $widgets->{'prefs'}->get_object('comboPrefDefTrans')
                  ->get_active_text()
            )
          )
        {
            $set = $value;
        }
    }
    if ($set eq "") {
        $set =
          $widgets->{'prefs'}->get_object('comboPrefDefTrans')
          ->get_active_text();
    }
    $config->{'DefaultTransition'} = $set;

    $config->{'BGImage'} =
      $widgets->{'prefs'}->get_object('imagePrefBG')->{user_data};
    $config->{'SpecialSong'} =
      $widgets->{'prefs'}->get_object('entryPrefSpecialSong')->get_text();
    $config->{'SpecialImage'} =
      $widgets->{'prefs'}->get_object('entryPrefSpecialImage')->get_text();
    $config->{'SpecialBack'} =
      $widgets->{'prefs'}->get_object('entryPrefSpecialBack')->get_text();
    $config->{'ImageDirectory'} =
      $widgets->{'prefs'}->get_object('filePrefSpecialImagedir')->get_label();
    $config->{'ImageDirectory'} =~ s/^\~/$ENV{'HOME'}/;
    $config->{'BGDirectory'} =
      $widgets->{'prefs'}->get_object('filePrefSpecialBGdir')->get_label();
    $config->{'BGDirectory'} =~ s/^\~/$ENV{'HOME'}/;
    $config->{'TrayIcons'} = $config->{'TrayIcons'};

    my $iter =
      $widgets->{'prefs'}->get_object('comboPrefBible')->get_active_iter;
    if (defined $iter) {
        $config->{'DefBible'} =
          $widgets->{'prefs'}->get_object('comboPrefBible')
          ->get_model->get($iter, 1);
    } else {
        debug("No default bible selected");
    }

    save_state();
    write_config();

    init_preview();
    init_miniview();

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

#***

#****f* lyricue/write_config
# NAME
#   write_config
# SYNOPSIS
#   write_config()
# FUNCTION
#   Writing preferences
# INPUTS
# OUTPUT
# SOURCE
#
sub write_config {
    debug("Writing preferences");

    # Backup config table
    my $query =
      "DELETE FROM config_old WHERE profile='" . $globals->{'profile'} . "'";
    my $sth = do_query($lyricDbh, $query, FALSE);
    $query = "INSERT config_old SELECT * FROM config WHERE profile='"
      . $globals->{'profile'} . "'";
    $sth = do_query($lyricDbh, $query, FALSE);
    $query = "DELETE FROM config WHERE profile='" . $globals->{'profile'} . "'";
    $sth   = do_query($lyricDbh, $query, FALSE);

    foreach (sort keys(%$config)) {
        if ((!/^Bible/) && (!/^App/) && (!/^LoopText/)) {
            $query =
"INSERT INTO config ( profile, config_key, config_value ) VALUES (\""
              . $globals->{'profile'}
              . "\", \""
              . $_
              . "\", \""
              . $config->{$_} . "\")";
            $sth = do_query($lyricDbh, $query, FALSE);
        }
    }
    foreach (keys $config->{'Apps'}) {
        $query =
          "INSERT INTO config ( profile, config_key, config_value ) VALUES ( \""
          . $globals->{'profile'}
          . "\", \"App\", \""
          . $_ . ";"
          . $config->{'Apps'}->{$_} . "\")";
        $sth = do_query($lyricDbh, $query, FALSE);
    }
    preview_display("reconfig", "", "", "MINI");
    update_display("reconfig", "", "");
    update_display("status",   "", "");
}

#***

# Add the filename to the playlist
#***

#****f* lyricue/file_ok_sel
# NAME
#   file_ok_sel
# SYNOPSIS
#   file_ok_sel($widget, $filexml)
# FUNCTION
#   Add filename to playlist
# INPUTS
#   $widget -
#    $filexml -
# OUTPUT
# SOURCE
#
sub file_ok_sel {
    my ($widget, $filexml) = @_;
    debug("Add filename to playlist");
    my $main_playlist =
      $widgets->{'main'}->get_object('labelCurrentPlaylist')->{user_data};
    my @filenames = $filexml->get_object('dialogFileChooser')->get_filenames;
    foreach (@filenames) {
        do_add_file($_, $main_playlist);
    }
    update_playlist();
    close_dialog($widget);
}

#***

#  Add the filename to the playlist
#***

#****f* lyricue/do_add_file
# NAME
#   do_add_file
# SYNOPSIS
#   do_add_file($file, $playlist)
# FUNCTION
#   do_add_file: file=
# INPUTS
#   $file -
#    $playlist -
# OUTPUT
# SOURCE
#
sub do_add_file {
    my ($file, $playlist) = @_;
    debug("do_add_file: file=" . $file);
    if (   ($file =~ /ppt$/i)
        || ($file =~ /odp$/i)
        || ($file =~ /pptx$/)
        || ($file =~ /pdf$/))
    {
        my $dialog =
          Gtk2::MessageDialog->new($widgets->{'main'}->get_object('windowMain'),
            'destroy-with-parent', 'info', 'none',
            gettext("Importing presentation"));
        $dialog->show_all();
        do_pending();
        my $tmpdir = tempdir("lyricue-XXXX", DIR => "/var/tmp", CLEANUP => 0);
        my $target = "";
        if (!($file =~ /pdf$/) && ($globals->{'unoconv'} ne "")) {

            # Add presentation
            debug("Adding ppt/odp presentation");
            $target = $file;
            $target =~ s/^.*\///g;
            $target =~ s/'//g;       # fix handling of ' in filename

            # Add 'ppt' to end of 'pptx' files so unoconv will handle them
            if ($target =~ /pptx$/) {
                $target .= ".ppt";
            }
            my $command =
              "cp \"" . $file . "\" \"" . $tmpdir . "/" . $target . "\"";
            debug($command);
            system($command);
            do_pending();

            # Convert to PDF using OpenOffice
            $command =
                $globals->{'unoconv'}
              . " -f pdf \""
              . $tmpdir . "/"
              . $target . "\"";
            debug($command);
            system($command);

            do_pending();
            my $ret = opendir DIR, $tmpdir;
            if ($ret) {
                foreach my $filename (sort readdir(DIR)) {
                    if ($filename =~ /\.pdf$/) {
                        $file = $tmpdir . "/" . $filename;
                    }
                }
                closedir DIR;
            }
        }
        if ($globals->{'convert'} ne "") {
            if (($file =~ /pdf$/)) {

                # Convert to PNG using ImageMagick
                my $command =
                    $globals->{'convert'}
                  . " -density 150 \""
                  . $file
                  . "\" -resize "
                  . $config->{'Width'} . "x"
                  . $config->{'Height'} . " \""
                  . $tmpdir
                  . "/img.png\"";
                debug($command);
                system($command);

                do_pending();

                # Import directory
                my $ret = opendir DIR, $tmpdir;
                if ($ret) {
                    foreach my $filename (sort readdir(DIR)) {
                        if (($filename =~ /\.pdf$/) || ($filename eq $target)) {
                            unlink $tmpdir . "/" . $filename;
                        } elsif ($filename =~ /^img.*png$/) {
                            my $count = $filename;
                            $count =~ s/^.*[^0-9]([0-9]*)\..*$/$1/g;
                            $count++;
                            rename $tmpdir . "/" . $filename,
                              $tmpdir . "/page_" . $count . ".png";
                        }
                    }
                    closedir DIR;
                }

                if ($target eq "") {
                    $target = $file;
                    $target =~ s/^.*\///g;
                }
                do_add_directory($tmpdir, $target);
                $dialog->destroy;
                return;
            }
        }
    }
    if (($file ne "") && ((-r $file) || ($file =~ /:\/\//))) {
        add_item($playlist, "file", $file);
    }
}

#***

#****f* 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);
}

#***

#***

#****f* lyricue/new_sublist_from_xml
# NAME
#   new_sublist_from_xml
# SYNOPSIS
#   new_sublist_from_xml($widget, $sublistxml)
# FUNCTION
#
# INPUTS
#   $widget -
#    $sublistxml -
# OUTPUT
# SOURCE
#
sub new_sublist_from_xml {
    my ($widget, $sublistxml) = @_;
    new_sublist($sublistxml->get_object('entryPromptE')->get_text());
    close_dialog($sublistxml->get_object('dialogPromptEntry'));
}

#***

#****f* 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
#   Returns sublist id
# SOURCE
#
sub new_sublist {
    my ($sublist) = @_;
    debug("new sublist");
    my ($row, $sth, $rv, $parentid);

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

    #Find out the id for the playlists table
    my $playlistsid = 1;
    $query = "SELECT MAX(id) FROM playlists";
    $sth   = do_query($lyricDbh, $query, FALSE);
    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   = do_query($lyricDbh, $query, FALSE);
    if ($row = $sth->fetchrow_hashref()) {
        if ($row->{'MAX(playorder)'}) {
            $playorder = $row->{'MAX(playorder)'} + 1;
        }
    }

    #create sublist playlist
    $query =
        "INSERT INTO playlists (id, title, profile, ref) VALUES ("
      . $playlistsid . " ,'"
      . $sublist . "','"
      . $globals->{'profile'} . "','')";
    $sth = do_query($lyricDbh, $query, FALSE);

    #link sublist to main playlist
    $query =
      "INSERT INTO playlist (playorder,playlist,type,data,transition) VALUES ("
      . $playorder . ", "
      . $parentid
      . ", \"sub\","
      . $playlistsid . " ,0)";
    $sth = do_query($lyricDbh, $query, FALSE);

    update_playlist();
    return $playlistsid;
}

#***

#****f* 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_object('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;
        my $sth = do_query($lyricDbh, $query, FALSE);

        update_playlist();
    }
}

#***

#****f* 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'";

    my $sth = do_query($lyricDbh, $query, FALSE);
    while ($row = $sth->fetchrow_hashref()) {
        @kiddies = (@kiddies, $row->{'data'});
        @kiddies = (@kiddies, find_more_children($row->{'data'}));
    }

    return @kiddies;
}

#***

#****f* 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   = do_query($lyricDbh, $query, FALSE);

    $query =
"INSERT INTO associations (id,playlist,imagename, absoluteparent) VALUES(0, "
      . $ASSOCIATE[0] . ", '"
      . $imagefile . "',"
      . $ASSOCIATE[1] . ")";
    $sth = do_query($lyricDbh, $query, FALSE);

    update_playlist();
    @ASSOCIATE = ();
}

#***

#****f* 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_object('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_object('labelCurrentPlaylist')->{user_data}
        );
        debug("Waiting for an image click in order to associate");
    }

}

#***

#****f* 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_object('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 = do_query($lyricDbh, $query, FALSE);

        update_playlist();
    }
}

#***

#****f* 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_object('labelCurrentPlaylist')->{user_data};

    my $query = "DELETE FROM associations WHERE absoluteparent=" . $parentid;
    my $sth = do_query($lyricDbh, $query, FALSE);

    update_playlist();
}

#***

#***

#****f* lyricue/init_mainWindow
# NAME
#   init_mainWindow
# SYNOPSIS
#   init_mainWindow()
# FUNCTION
#   Initializing main window
# INPUTS
# OUTPUT
# SOURCE
#
sub init_mainWindow {
    debug("Initializing main window");

    # Setup file chooser

    # Setup bible browser
    biblebrowser_init();

    # Load bg thumbnails
    my $scaled = create_pixbuf(
        $config->{'BGImage'},
        $globals->{'icon_width'},
        $globals->{'icon_height'}
    );
    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_object('vboxCurrentBG')
          ->pack_start($widgets->{'buttonCurr'}, FALSE, FALSE, 0);
        $widgets->{'pixmapCurr'}->show();
        $widgets->{'buttonCurr'}->show();
        $widgets->{'buttonCurr'}
          ->signal_connect("clicked", "backdrop_preview_clicked");
    }
    $scaled = create_pixbuf(
        $config->{'BGImage'},
        $globals->{'icon_width'},
        $globals->{'icon_height'}
    );
    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_object('vboxPrevBG')
          ->pack_start($widgets->{'buttonPrev'}, FALSE, FALSE, 0);
        $widgets->{'pixmapPrev'}->show();
        $widgets->{'buttonPrev'}->show();
        $widgets->{'buttonPrev'}
          ->signal_connect("clicked", "backdrop_preview_clicked");
    }

    # Update list of background/image directories
    bgdir_list("bg");
    bgdir_list("img");

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

    # Update the bible menu
    load_biblemenu();

    # Update the profiles menu
    load_profile_menu();

    # Update the application menu
    my $menutop2 = Gtk2::Menu->new();
    my $group2   = 0;
    foreach (keys $config->{'Apps'}) {
        my $appMenuItem = Gtk2::MenuItem->new_with_label($_);
        $appMenuItem->signal_connect("activate", "execute_app",
            $config->{'Apps'}->{$_});
        $appMenuItem->show;
        $menutop2->append($appMenuItem);
    }
    $widgets->{'main'}->get_object('applications1')->set_submenu($menutop2);

    # DRAG and DROP
    $widgets->{'main'}->get_object('treeAvailable')
      ->drag_source_set(['button1_mask'], ['copy'],
        {'target' => "STRING", 'flags' => [], 'info' => 0});
    $widgets->{'main'}->get_object('iconviewBack')
      ->drag_source_set(['button1_mask'], ['copy'],
        {'target' => "STRING", 'flags' => [], 'info' => 0});
    $widgets->{'main'}->get_object('iconviewImage')
      ->drag_source_set(['button1_mask'], ['copy'],
        {'target' => "STRING", 'flags' => [], 'info' => 0});
    $widgets->{'main'}->get_object('treePlaylist')->drag_source_set(
        'button1_mask',
        ['move', 'copy'],
        {'target' => "STRING", 'flags' => [], 'info' => 0}
    );
    $widgets->{'main'}->get_object('treePlaylist')->drag_dest_set(
        'all',
        ['copy', 'move'],
        {'target' => "STRING", 'flags' => [], 'info' => 0}
    );

    init_preview();
    init_miniview();

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

    do_pending();

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

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

#***

#****f* lyricue/bgdir_list
# NAME
#   bgdir_list
# SYNOPSIS
#   bgdir_list($type)
# FUNCTION
#   Loading ".$type." directory list
# INPUTS
#   $type -
# OUTPUT
# SOURCE
#
sub bgdir_list {
    my ($type) = @_;
    debug("Loading " . $type . " directory list");
    my $counter = 0;
    my $cat     = "";
    my $store   = Gtk2::ListStore->new('Glib::String', 'Glib::String');
    my $iter    = $store->append;
    $store->set($iter, 0, fromutf(gettext("Select Category")), 1, "");

    my ($combo, $special, $dirname);
    if ($type eq "bg") {
        $combo   = $widgets->{'main'}->get_object('comboBGDirList');
        $special = $config->{'SpecialBack'};
        $dirname = $config->{'BGDirectory'};
    } else {
        $combo   = $widgets->{'main'}->get_object('comboImgDirList');
        $special = $config->{'SpecialImage'};
        $dirname = $config->{'ImageDirectory'};
    }
    $combo->clear();
    $combo->set_model($store);
    my $renderer = Gtk2::CellRendererText->new;
    $combo->pack_start($renderer, TRUE);
    $combo->add_attribute($renderer, text => 0);
    my @dbcategories  = ();
    my @dircategories = ();

    if ($special ne "") {
        my ($type, $id) = split /;/, $special;
        if (!defined $id) {
            $id   = $type;
            $type = "db";
        }
        $id = "00000" . $id;
        if ($type eq "db") {
            push @dbcategories, $id;
        } else {
            push @dircategories, $id;
        }
    }

    # Add categories from mediaDb
    my $query =
        "SELECT DISTINCT category FROM media WHERE type=\""
      . $type
      . "\" AND category !=\""
      . $special
      . "\" ORDER BY category";
    my $sth = do_query($mediaDbh, $query, FALSE);
    while ($row = $sth->fetchrow_hashref()) {
        push @dbcategories, $row->{'category'};
    }

    foreach $cat (sort @dbcategories) {
        $cat =~ s/^00000//g;
        debug("Adding image dir button for db-" . $cat);
        my $iter = $store->append;
        $store->set($iter, 0, $cat, 1, "db");

    }

    # Add categories from directories
    my (@files, $file);
    $dirname =~ s/^\~/$ENV{'HOME'}/;
    my $ret = opendir DIR, $dirname;
    if ($ret) {
        foreach $file (sort readdir(DIR)) {
            if (!($file =~ /^\./) && (-d $dirname . "/" . $file)) {
                push @dircategories, $file;
            }
        }
        closedir DIR;
    }

    # Add a blank entry for files not in a subdirectory
    push @dircategories, "/";

    foreach $cat (sort @dircategories) {
        $cat =~ s/^00000//g;
        debug("Adding image dir button for dir-" . $cat);
        my $iter = $store->append;
        $store->set($iter, 0, $cat, 1, "dir");
    }
    $combo->set_active(0);
}

#***

#****f* lyricue/bgdir_change
# NAME
#   bgdir_change
# SYNOPSIS
#   bgdir_change ($directory)
# FUNCTION
#   Clears background image preview buttions
#   Calls imgdir_load to create new buttons
# INPUTS
#   $category - background image category to load images from
#   $type - 'dir' or 'db'
# OUTPUT
#   Updated background preview buttons
# SOURCE
#
sub bgdir_change {
    my ($widget) = @_;
    my $iter = $widget->get_active_iter;
    my ($category, $type) = $widget->get_model->get($iter, 0, 1);
    debug(  "Changing background source category to "
          . $category
          . " of type "
          . $type);
    debug("Destroying old images");
    $globals->{'category'} = $category;
    imgdir_load($widgets->{'main'}->get_object('iconviewBack'),
        "bg", $category, $type);
    debug("Background category change complete");
}

#***
#****f* lyricue/imgdir_change
# NAME
#   imgdir_change
# SYNOPSIS
#   imgdir_change ($directory)
# FUNCTION
#   Clears image preview buttions
#   Calls imgdir_load to create new buttons
# INPUTS
#   $category - image category to load images from
#   $type - 'dir' or 'db'
# OUTPUT
#   Updated background preview buttons
# SOURCE
#
sub imgdir_change {
    my ($widget) = @_;
    my $iter = $widget->get_active_iter;
    my ($category, $type) = $widget->get_model->get($iter, 0, 1);
    debug(
        "Changing image source category to " . $category . " of type " . $type);
    debug("Destroying old images");
    $globals->{'category'} = $category;
    imgdir_load($widgets->{'main'}->get_object('iconviewImage'),
        "img", $category, $type);
    debug("Image category change complete");
}

#***

#****f* lyricue/imgdir_load
# NAME
#   imgdir_load
# SYNOPSIS
#   imgdir_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 imgdir_load {
    my ($iconview, $section, $category, $type) = @_;
    my $counter = 0;

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

    $iconview->hide();
    my $store =
      Gtk2::ListStore->new('Glib::String', 'Gtk2::Gdk::Pixbuf', 'Glib::String');
    $iconview->set_model($store);
    $iconview->set_text_column(0);
    $iconview->set_pixbuf_column(1);

    if ($type eq "db") {
        my $query =
            "SELECT id,description FROM media WHERE category=\""
          . $category
          . "\" AND type=\""
          . $section
          . "\" ORDER BY description";
        my $sth = do_query($mediaDbh, $query, FALSE);
        while ($row = $sth->fetchrow_hashref()) {

            do_pending();
            my $iter = $store->append;
            $store->set($iter, 0, $row->{'description'}, 2,
                $type . ";" . $row->{'id'});
        }
    } else {
        my $dirname = "";
        if ($section eq "bg") {
            $dirname = $config->{'BGDirectory'} . "/" . $category;
        } else {
            $dirname = $config->{'ImageDirectory'} . "/" . $category;
        }
        my (@files, $file);
        my $ret = opendir DIR, $dirname;
        if ($ret) {
            foreach $file (sort readdir(DIR)) {
                if ((!($file =~ /^\./)) && (!(-d $dirname . "/" . $file))) {
                    do_pending();
                    my $iter = $store->append;
                    $file = fromutf($file);
                    $store->set($iter, 0, $file, 2,
                        $type . ";" . $dirname . "/" . $file);
                }
            }
            closedir DIR;
        }
    }

    $iconview->show();
    $globals->{'thumb_idle'} = Glib::Idle->add(\&imgdir_thumbnails, $iconview);
}

#***

#****f* lyricue/imgdir_thumbnails
# NAME
#   imgdir_thumbnails
# SYNOPSIS
#   imgdir_thumbnails ()
# FUNCTION
#   Load thumbnails for imagdir
# INPUTS
#
# OUTPUT
#   Thumbnails
# SOURCE
#
sub imgdir_thumbnails {
    my ($iconview) = @_;
    debug("Loading thumbnails");
    my $store = $iconview->get_model;
    my $iter  = $store->get_iter_first;
    while (defined $iter) {
        my $id = $store->get_value($iter, 2);
        my $scaled = create_pixbuf(
            $id,
            $globals->{'icon_width'},
            $globals->{'icon_height'}
        );
        if ($scaled) {
            $store->set($iter, 1, $scaled);
        }
        my $nextiter = $store->iter_next($iter);
        $iter = $nextiter;
        do_pending();
    }
}

#***

#****f* lyricue/playlist_thumbnails
# NAME
#   playlist_thumbnails
# SYNOPSIS
#   playlist_thumbnails()
# FUNCTION
#   Load thumbnails for image playlist
# INPUTS
#
# OUTPUT
#   Thumbnails
# SOURCE
#
sub playlist_thumbnails {
    my ($iconview) = @_;
    debug("Loading Playlist thumbnails");
    my $playlist =
      $widgets->{'main'}->get_object('labelCurrentPlaylist')->{user_data};
    preview_display("plsnapshot", $playlist, "", "MINIRET");
    my $store = $iconview->get_model;
    my $iter  = $store->get_iter_first;
    while (defined $iter) {
        if (!($store->get_value($iter, 0) =~ /^<b>/)) {
            my $id = "thmb;" . $store->get_value($iter, 2);
            my $scaled = create_pixbuf(
                $id,
                $globals->{'snapshot_width'},
                $globals->{'snapshot_height'}
            );
            if ($scaled) {
                $store->set($iter, 1, $scaled);
            }
        }
        my $nextiter = $store->iter_next($iter);
        $iter = $nextiter;
        do_pending();
    }
}

#***

#****f* 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});
            }

        }
    }
}

#***

#****f* 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'} = load_window("dialogSearch");
    my @target_table = ({'target' => "STRING", 'flags' => [], 'info' => 0},);
    $widgets->{'search'}->get_object('treeSearch')
      ->drag_source_set(['button1_mask'], ['copy'], @target_table);
    $widgets->{'main'}->get_object('treePlaylist')
      ->drag_dest_set('all', ['copy'], @target_table);
    my $search_text = $widgets->{'main'}->get_object('entrySearch')->get_text();
    $widgets->{'search'}->get_object('entrySearchSongs')
      ->set_text($search_text);
    do_adv_search();
}

#***

#***

#****f* lyricue/add_file
# NAME
#   add_file
# SYNOPSIS
#   add_file()
# FUNCTION
#   Open a file dialog
# INPUTS
# OUTPUT
# SOURCE
#
sub add_file {
    debug("Open a file dialog");
    my $filexml = load_window("dialogFileChooser");
    $filexml->get_object('dialogFileChooser')
      ->set_preview_widget(Gtk2::Image->new());
    $filexml->get_object('dialogFileChooser')->set_preview_widget_active(FALSE);
    $filexml->get_object('dialogFileChooser')
      ->signal_connect("update-preview", "update_file_preview");
    $filexml->get_object('buttonFileOK')
      ->signal_connect("clicked", "file_ok_sel", $filexml);
    $filexml->get_object('dialogFileChooser')->show_all();
}

sub update_file_preview {
    my ($chooser) = @_;
    my $image     = $chooser->get_preview_widget;
    my $filename  = $chooser->get_preview_filename;
    if (defined $filename && ($filename ne "")) {
        my $pixbuf = create_pixbuf(
            "dir;" . $filename,
            $globals->{'thumb_width'},
            $globals->{'thumb_height'}
        );
        if (defined $pixbuf) {
            $image->set_from_pixbuf($pixbuf);
            $chooser->set_preview_widget_active(TRUE);
            return;
        }
    }
    $chooser->set_preview_widget_active(FALSE);
}

#***

#****f* lyricue/add_directory
# NAME
#   add_directory
# SYNOPSIS
#   add_directory()
# FUNCTION
#   Add a directory to the playlist
# INPUTS
# OUTPUT
# SOURCE
#
sub add_directory {
    debug("Add a directory to the playlist");
    my $d = Gtk2::FileChooserDialog->new(
        fromutf(gettext("Choose a Directory")),
        $widgets->{'main'}->get_object('windowMain'), 'select-folder',
        fromutf(gettext("Cancel")) => "cancel",
        fromutf(gettext("OK"))     => "accept",
    );

    my $response = $d->run();
    my $dirname  = $d->get_filename();
    if ("accept" eq $response) {
        debug("Choosing" . $dirname);
    }
    $d->destroy;
    do_add_directory($dirname, "");
}

#***

#****f* lyricue/do_add_directory
# NAME
#   do_add_directory
# SYNOPSIS
#   do_add_directory($dirname,$sublist)
# FUNCTION
#   Sublist name:
# INPUTS
#   $dirname -
#   $sublist -
# OUTPUT
# SOURCE
#
sub do_add_directory {
    my ($dirname, $sublist) = @_;

    my (@files, $file);
    opendir DIR, $dirname || return 0;
    foreach $file (sort readdir(DIR)) {
        my $type = globs($dirname . "/" . $file);
        if (($type =~ /^audio/) || ($type =~ /^video/) || ($type =~ /^image/)) {
            push @files, $dirname . "/" . $file;
        }
    }
    closedir DIR;
    if (@files) {
        if ($sublist eq "") {
            my $sublistxml = load_window("dialogPromptEntry");
            $sublistxml->get_object('dialogPromptEntry')
              ->set_title(fromutf(gettext("Create new sublist")));
            $sublistxml->get_object('labelPromptE')
              ->set_text(fromutf(gettext("Name of sublist")));

            my $response = $sublistxml->get_object('dialogPromptEntry')->run();
            if ($response eq "ok") {
                $sublist = $sublistxml->get_object('entryPromptE')->get_text();
                close_dialog($sublistxml->get_object('dialogPromptEntry'));
            } else {
                close_dialog($sublistxml->get_object('dialogPromptEntry'));
                return;
            }
            debug("Sublist name: " . $sublist);
        }
        my $playlist = new_sublist($sublist);

        @files = sort alphanum @files;

        foreach $file (@files) {
            do_add_file($file, $playlist);
        }
    }
    update_playlist();
}

#***

#****f* lyricue/add_background
# NAME
#   add_background
# SYNOPSIS
#   add_background($widget)
# FUNCTION
#   add background
# INPUTS
#   $widget -
# OUTPUT
# SOURCE
#
sub add_background {
    debug("add background");
    my ($widget) = @_;
    change_bgimage($widget);
}

#***

#****f* lyricue/change_bgimage
# NAME
#   change_bgimage
# SYNOPSIS
#   change_bgimage($widget)
# FUNCTION
#   Change backdrop dialog opened
# INPUTS
#   $widget -
# OUTPUT
# SOURCE
#
sub change_bgimage {
    my ($widget) = @_;
    my $bgimage = "";
    debug("Change backdrop dialog opened");
    $widgets->{'image'} = load_window("dialogImage");
    $widgets->{'image'}->get_object('dialogImage')
      ->set_title(fromutf(gettext("Choose a Background")));
    $widgets->{'image'}->get_object('buttonImageAdd')
      ->signal_connect('clicked', 'import_background');
    $widgets->{'image'}->get_object('hboxImageSublist')->hide;
    $widgets->{'image'}->get_object('dialogImage')->show;

    if ($widgets->{'image'}->get_object('treeImage')->{user_data}
        && ($widgets->{'image'}->get_object('treeImage')->{user_data} eq "load")
      )
    {
        $widgets->{'image'}->get_object('treeImage')->{data} = ();
    } else {
        if (
            defined $widgets->{'prefs'}
            && ($widget eq
                $widgets->{'prefs'}->get_object('buttonPrefBackground'))
          )
        {
            $bgimage =
              $widgets->{'prefs'}->get_object('imagePrefBG')->{user_data};
            $widgets->{'image'}->get_object('buttonImageOK')
              ->set_label(fromutf(gettext("Set as Default")));
            $widgets->{'image'}->get_object('buttonImageOK')
              ->signal_connect('clicked', "set_default_backdrop");
            $widgets->{'image'}->get_object('buttonImageAdd')->hide();
            $widgets->{'image'}->get_object('buttonImageDelete')->hide();
            $widgets->{'image'}->get_object('buttonImageChange')->hide();
        } else {
            $widgets->{'image'}->get_object('buttonImageCancel')->hide();
            $widgets->{'image'}->get_object('buttonImageOK')
              ->signal_connect('clicked', "close_dialog");
            $widgets->{'image'}->get_object('hboxImageColour')->show();
            $widgets->{'image'}->get_object('colorbuttonFontColour')
              ->signal_connect("button_press_event", "open_dialogColour",
                "ImageFont");

            #$widgets->{'image'}->get_object('entryImageFontColour')
            #->signal_connect("changed", "change_colour_media");
            $widgets->{'image'}->get_object('colorbuttonShadowColour')
              ->signal_connect("button_press_event", "open_dialogColour",
                "ImageShadow");

            #$widgets->{'image'}->get_object('entryImageShadowColour')
            #->signal_connect("changed", "change_colour_media");
        }
        $widgets->{'image'}->get_object('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_object('treeImage')->append_column($column);
    }
    update_imagedir("bg", "");
    return TRUE;
}

#***

#****f* 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 = load_window("dialogPromptEntry");
    $sublistxml->get_object('dialogPromptEntry')
      ->set_title(fromutf(gettext("Create new sublist")));
    $sublistxml->get_object('labelPromptE')
      ->set_text(fromutf(gettext("Name of sublist")));
    $sublistxml->get_object('buttonPromptEOK')
      ->signal_connect("clicked", "new_sublist_from_xml", $sublistxml);
}

#***

#***

#****f* lyricue/get_buffer_text
# NAME
#   get_buffer_text
# SYNOPSIS
#   get_buffer_text($widget)
# FUNCTION
#
# INPUTS
#   $widget -
# OUTPUT
# SOURCE
#
sub get_buffer_text {
    my ($widget) = @_;
    return $widget->get_text($widget->get_bounds, FALSE);
}

#***

#****f* lyricue/on_treeAvailable_drag_data_get
# NAME
#   on_treeAvailable_drag_data_get
# SYNOPSIS
#   on_treeAvailable_drag_data_get($widget, $context, $data, $info, $time)
# FUNCTION
#   Dragged from available songs
# INPUTS
#   $widget -
#    $context -
#    $data -
#    $info -
#    $time -
# OUTPUT
# SOURCE
#
sub on_treeAvailable_drag_data_get {
    my ($widget, $context, $data, $info, $time) = @_;
    debug("Dragged from available songs");
    my $selection = $widget->get_selection;
    my @sel       = $selection->get_selected_rows;
    my $model     = $widgets->{'main'}->get_object('treeAvailable')->get_model;
    my $iter      = $model->get_iter($sel[0]);
    if ($iter) {
        $data->set($data->target, 8, $model->get($iter, 3));
    }
}

#***

#****f* lyricue/on_iconviewBack_drag_data_get
# NAME
#   on_iconviewBack_drag_data_get
# SYNOPSIS
#   on_iconviewBack_drag_data_get($widget, $context, $data, $info, $time)
# FUNCTION
#   Dragged from Backgrounds
# INPUTS
#   $widget -
#    $context -
#    $data -
#    $info -
#    $time -
# OUTPUT
# SOURCE
#
sub on_iconviewBack_drag_data_get {
    my ($widget, $context, $data, $info, $time) = @_;
    debug("Dragged from Backgrounds");
    my $selected = "";
    my @list     = $widget->get_selected_items;
    if (defined $list[0]) {
        my $model = $widget->get_model;
        my $iter  = $model->get_iter($list[0]);
        $selected = $model->get($iter, 2);
    }
    if ($selected ne "") {
        $data->set($data->target, 8, "bg:" . $selected);
    }
}

#***

#****f* lyricue/on_iconviewImage_drag_data_get
# NAME
#   on_iconviewImage_drag_data_get
# SYNOPSIS
#   on_iconviewImage_drag_data_get($widget, $context, $data, $info, $time)
# FUNCTION
#   Dragged from Images
# INPUTS
#   $widget -
#    $context -
#    $data -
#    $info -
#    $time -
# OUTPUT
# SOURCE
#
sub on_iconviewImage_drag_data_get {
    my ($widget, $context, $data, $info, $time) = @_;
    debug("Dragged from Images");
    my $selected = "";
    my @list     = $widget->get_selected_items;
    if (defined $list[0]) {
        my $model = $widget->get_model;
        my $iter  = $model->get_iter($list[0]);
        $selected = $model->get($iter, 2);
    }
    if ($selected ne "") {
        $data->set($data->target, 8, "img:" . $selected);
    }
}

#***

#****f* lyricue/on_treeSearch_drag_data_get
# NAME
#   on_treeSearch_drag_data_get
# SYNOPSIS
#   on_treeSearch_drag_data_get($widget, $context, $data, $info, $time)
# FUNCTION
#   Dragged from advanced search
# INPUTS
#   $widget -
#    $context -
#    $data -
#    $info -
#    $time -
# OUTPUT
# SOURCE
#
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, 4));
    }
}

#***

#****f* lyricue/on_treePlaylist_drag_data_get
# NAME
#   on_treePlaylist_drag_data_get
# SYNOPSIS
#   on_treePlaylist_drag_data_get($widget, $context, $data, $info, $time)
# FUNCTION
#   Dragged from playlist
# INPUTS
#   $widget -
#    $context -
#    $data -
#    $info -
#    $time -
# OUTPUT
# SOURCE
#
sub on_treePlaylist_drag_data_get {
    my ($widget, $context, $data, $info, $time) = @_;
    debug("Dragged from Playlist");
    my $selection = $widget->get_selection;
    my ($model, $iter) = $selection->get_selected;
    if ($iter) {
        $data->set($data->target, 8, "song:" . $model->get($iter, 2));
        return TRUE;
    }
    return FALSE;
}

#***

#****f* lyricue/on_treePlaylist_drag_data_received
# NAME
#   on_treePlaylist_drag_data_received
# SYNOPSIS
#   on_treePlaylist_drag_data_received($widget, $context, $x, $y, $data, $info, $time)
# FUNCTION
#   Dropped on playlist
# INPUTS
#   $widget -
#    $context -
#    $x -
#    $y -
#    $data -
#    $info -
#    $time -
# OUTPUT
# SOURCE
#
sub on_treePlaylist_drag_data_received {
    my ($widget, $context, $x, $y, $data, $info, $time) = @_;
    my ($u1, $file);
    debug("Dropped on playlist");

    # Catch duplicate events
    if (defined($globals->{'drop_time'}) && ($globals->{'drop_time'} == $time))
    {
        return;
    }
    $globals->{'drop_time'} = $time;

    if (($data->length >= 0) && ($data->format == 8)) {
        debug("Received: " . $data->data);
        if ($data->data =~ /^file:/i) {
            for my $uri (split(/\n/, $data->data)) {
                $u1 = URI->new($uri);
                debug("received URI: " . $uri);
                $file = fromutf("UTF-8", $u1->file);
                debug("Recieved file: " . $file);
                my $main_playlist =
                  $widgets->{'main'}->get_object('labelCurrentPlaylist')
                  ->{user_data};
                do_add_file($file, $main_playlist);
                update_playlist();
            }
        } elsif ($data->data =~ /^img:/) {
            my $selected = $data->data;
            $selected =~ s/^img://g;
            my $playlist =
              $widgets->{'main'}->get_object('labelCurrentPlaylist')
              ->{user_data};
            add_image_item($playlist, $selected);
            update_playlist();
        } elsif ($data->data =~ /^bg:/) {
            my $selected = fromutf($data->data);
            $selected =~ s/^bg://g;
            my $playlist =
              $widgets->{'main'}->get_object('labelCurrentPlaylist')
              ->{user_data};
            my ($path, $drop);
            ($path, $drop) = $widget->get_dest_row_at_pos($x, $y);
            debug($path . ":" . $drop);
            if ($drop =~ /^into/) {
                my $iter = $widget->get_model->get_iter($path);
                my $item = $widget->get_model->get($iter, 2);
                @ASSOCIATE = ($item, $playlist);
                debug($item . ":" . $playlist . ":" . $selected);
                associate_bg($selected);
                update_playlist();
            }
        } elsif ($data->data =~ /^song:/) {
            my $song = $data->data;
            $song =~ s/^song://g;
            my ($path, $drop) = $widget->get_dest_row_at_pos($x, $y);
            my $iter = $widget->get_model->get_iter($path);
            my $item = $widget->get_model->get($iter, 2);
            debug($song . " - " . $drop . " - " . $item);
            move_item($song, $drop, $item);
        } else {
            if ($data->data != 0) {
                add_single_song($data->data);
                update_playlist();
            }
        }
        $context->finish(1, 0, $time);
        return;
    }
    $context->finish(0, 0, $time);
}

#***

#****f* lyricue/display_message
# NAME
#   display_message
# SYNOPSIS
#   display_message($message, $details)
# FUNCTION
#       debug($message);
# INPUTS
#   $message -
#    $details -
# OUTPUT
# SOURCE
#
sub display_message {
    my ($message, $details) = @_;
    debug($message);
    my $errorxml = load_window("dialogError");
    $errorxml->get_object('labelError')->set_text($message);
    if ((!defined $details) || ($details eq "")) {
        $errorxml->get_object('expanderDetails')->hide();
    } else {
        $errorxml->get_object('labelErrorDetails')->set_text($details);
    }
    my $confirm = $errorxml->get_object('dialogError')->run();
    close_dialog($errorxml->get_object('dialogError'));
}

#***

#****f* lyricue/display_fatal
# NAME
#   display_fatal
# SYNOPSIS
#   display_fatal($message, $error)
# FUNCTION
#
# INPUTS
#   $message -
#    $error -
# OUTPUT
# SOURCE
#
sub display_fatal {
    my ($message, $error) = @_;
    display_error($message, $error, TRUE);
}

sub database_failure {
    my ($message, $error) = @_;
    display_error($message, $error, TRUE);
}

sub display_error {
    my ($message, $error, $fatal) = @_;
    print STDERR "\n\n-------------\n";
    if ($fatal) {
        print STDERR "FATAL ERROR!!\n";
    } else {
        print STDERR "ERROR!!\n";
    }
    print STDERR "-------------\n";
    print STDERR "Error description\n";
    print STDERR $message . "\n";
    print STDERR "------------------\n";
    my $errorxml = load_window('dialogError');
    $errorxml->get_object('labelError')->set_text($message);

    if ($error eq "") {
        $errorxml->get_object('expanderDetails')->hide();
    } else {
        $errorxml->get_object('labelErrorDetails')->set_text($error);
    }
    my $confirm = $errorxml->get_object('dialogError')->run();
    close_dialog($errorxml->get_object('dialogError'));
    print STDERR "Full error message\n";
    print STDERR "------------------\n";
    if ($fatal) {
        die($error);
    }
}

#***

#****f* lyricue/transition_type_changed
# NAME
#   transition_type_changed
# SYNOPSIS
#   transition_type_changed()
# FUNCTION
#   Change the list of transition options
# INPUTS
# OUTPUT
# SOURCE
sub transition_type_changed {
    my ($widget) = @_;
    debug("Transition type changed");
    if ($widget eq $widgets->{'main'}->get_object('radioTransFade')) {
    } elsif ($widget eq $widgets->{'main'}->get_object('radioTransWipe')) {
    } elsif ($widget eq $widgets->{'main'}->get_object('radioTransSlide')) {
        if ($widget->get_active) {
            $widgets->{'main'}->get_object('tableTransDir')->show_all();
        } else {
            $widgets->{'main'}->get_object('tableTransDir')->hide();
        }
    } elsif ($widget eq $widgets->{'main'}->get_object('radioTransRotate')) {
        if ($widget->get_active) {
            $widgets->{'main'}->get_object('tableTransRot')->show_all();
        } else {
            $widgets->{'main'}->get_object('tableTransRot')->hide();
        }
    }
}

sub transition_dir_changed {
    my ($widget) = @_;
    debug("Direction changed");
    if ($widget->get_active == FALSE) {
        return;
    }
    my @dirs = (
        'UpLeft',   'Up',   'UpRight', 'Left', 'None', 'Right',
        'DownLeft', 'Down', 'DownRight'
    );
    if ($widget->get_name() =~ /^toggleIn/) {
        foreach (@dirs) {
            my $w = "toggleIn" . $_;
            if ($widget->get_name eq $w) {
                $widgets->{'main'}->get_object($w)->set_active(TRUE);
            } else {
                $widgets->{'main'}->get_object($w)->set_active(FALSE);
            }
        }
    } elsif ($widget->get_name =~ /^toggleOut/) {
        foreach (@dirs) {
            my $w = "toggleOut" . $_;
            if ($widget->get_name eq $w) {
                $widgets->{'main'}->get_object($w)->set_active(TRUE);
            } else {
                $widgets->{'main'}->get_object($w)->set_active(FALSE);
            }
        }
    }
}

#***

#****f* lyricue/apply_transition_selected
# NAME
#   apply_transition_selected
# SYNOPSIS
#   apply_transition_selected()
# FUNCTION
#   Apply transition to selected items
# INPUTS
# OUTPUT
# SOURCE
#
sub apply_transition_selected {
    debug("Apply transition to selected items");
    if ($widgets->{'main'}->get_object('labelCurrentPlaylist')->{user_data} ==
        -1)
    {
        return;
    }
    my $trans_type = calculate_transition();
    my $selection =
      $widgets->{'main'}->get_object('treePlaylist')->get_selection;
    if ($selection) {
        my @list  = $selection->get_selected_rows();
        my $model = $widgets->{'main'}->get_object('treePlaylist')->get_model();
        foreach (@list) {
            apply_transition($model->get($model->get_iter($_), 2), $trans_type);
        }
    }
    update_playlist();
}

#***

#****f* lyricue/apply_transition_playlist
# NAME
#   apply_transition_playlist
# SYNOPSIS
#   apply_transition_playlist()
# FUNCTION
#   Apply transition to playlist
# INPUTS
# OUTPUT
# SOURCE
#
sub apply_transition_playlist {
    debug("Apply transition to playlist");
    my $playlist =
      $widgets->{'main'}->get_object('labelCurrentPlaylist')->{user_data};
    if ($playlist == -1) {
        return;
    }
    my $trans_type = calculate_transition();
    my $query = "SELECT playorder FROM playlist WHERE playlist=" . $playlist;
    my $sth = do_query($lyricDbh, $query, FALSE);
    while (my @row = $sth->fetchrow_array()) {
        apply_transition($row[0], $trans_type);
    }
    update_playlist();
}

#***

#****f* lyricue/calculate_transition
# NAME
#   calculate_transition
# SYNOPSIS
#   calculate_transition()
# FUNCTION
#
# INPUTS
# OUTPUT
# SOURCE
#
sub calculate_transition {
    debug("Calculating transition code");
    my $trans_type = DEFAULT;
    if ($widgets->{'main'}->get_object('radioTransNone')->get_active()) {
        $trans_type = NOTRANS;
    } elsif ($widgets->{'main'}->get_object('radioTransFade')->get_active()) {
        $trans_type = FADE;
    } elsif ($widgets->{'main'}->get_object('radioTransSlide')->get_active()) {
        $trans_type = SLIDE_TEXT;
    } elsif ($widgets->{'main'}->get_object('radioTransRotate')->get_active()) {
        $trans_type = ROTATE_TEXT;
    } elsif ($widgets->{'main'}->get_object('radioTransRandom')->get_active()) {
        $trans_type = RANDOM;
    }

    my @dirs = (
        'UpLeft',   'Up',   'UpRight', 'Left', 'None', 'Right',
        'DownLeft', 'Down', 'DownRight'
    );
    my $indir  = "";
    my $outdir = "";
    foreach (@dirs) {
        if ($widgets->{'main'}->get_object("toggleIn" . $_)->get_active()) {
            $indir = $_;
        }
        if ($widgets->{'main'}->get_object("toggleOut" . $_)->get_active()) {
            $outdir = $_;
        }
    }

    # Set in direction
    $trans_type = $trans_type << NUM_TRANS;
    $_          = $indir;
    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->{'main'}->get_object('toggleRotX')->get_active()) {
        $trans_type = $trans_type + X_AXIS;
    }
    if ($widgets->{'main'}->get_object('toggleRotY')->get_active()) {
        $trans_type = $trans_type + Y_AXIS;
    }
    if ($widgets->{'main'}->get_object('toggleRotZ')->get_active()) {
        $trans_type = $trans_type + Z_AXIS;
    }

    # Set out direction
    $trans_type = $trans_type << NUM_TRANS;
    $_          = $outdir;
    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->{'main'}->get_object('toggleRotX')->get_active()) {
        $trans_type = $trans_type + X_AXIS;
    }
    if ($widgets->{'main'}->get_object('toggleRotY')->get_active()) {
        $trans_type = $trans_type + Y_AXIS;
    }
    if ($widgets->{'main'}->get_object('toggleRotZ')->get_active()) {
        $trans_type = $trans_type + Z_AXIS;
    }
    return $trans_type;
}

#***

#****f* lyricue/apply_transition
# NAME
#   apply_transition
# SYNOPSIS
#   apply_transition($item, $trans_type)
# FUNCTION
#   Applying transition to
# INPUTS
#   $item -
#    $trans_type -
# OUTPUT
# SOURCE
#
sub apply_transition {
    my ($item, $trans_type) = @_;
    debug("Applying transition to " . $item);

    my $query = "SELECT type,data FROM playlist WHERE playorder=" . $item;
    my $sth = do_query($lyricDbh, $query, FALSE);
    my @row = $sth->fetchrow_array();
    if ($row[0] eq "play") {
        $query =
            "UPDATE playlist SET transition="
          . $trans_type
          . " WHERE playorder="
          . $item
          . " OR playlist="
          . $row[1];
    } else {
        $query =
            "UPDATE playlist SET transition="
          . $trans_type
          . " WHERE playorder="
          . $item;
    }
    $sth = do_query($lyricDbh, $query, FALSE);
}

#***

#****f* lyricue/mod
# NAME
#   mod
# SYNOPSIS
#   mod($inp, $div)
# FUNCTION
#
# INPUTS
#   $inp -
#    $div -
# OUTPUT
# SOURCE
#
sub mod {
    my ($inp, $div) = @_;
    my $rem = (($inp / $div) - (int($inp / $div))) * $div;
    return $rem;
}

#***

#****f* lyricue/user_admin
# NAME
#   user_admin
# SYNOPSIS
#   user_admin()
# FUNCTION
#   User access administration
# INPUTS
# OUTPUT
# SOURCE
#
sub user_admin {
    debug("User access administration");
    $widgets->{'admin'} = load_window('dialogAdmin');
    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_object('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_object('tableAccess')
              ->attach($widgets->{'adminUsername'}[$count],
                0, 1, $count + 2, $count + 3, 'fill', 'expand', 0, 0);

            $widgets->{'adminPlaylist'}[$count] = Gtk2::CheckButton->new();
            $widgets->{'admin'}->get_object('tableAccess')
              ->attach($widgets->{'adminPlaylist'}[$count],
                1, 2, $count + 2, $count + 3, 'fill', 'expand', 0, 0);
            $widgets->{'adminEdit'}[$count] = Gtk2::CheckButton->new();
            $widgets->{'admin'}->get_object('tableAccess')
              ->attach($widgets->{'adminEdit'}[$count],
                2, 3, $count + 2, $count + 3, 'fill', 'expand', 0, 0);
            $widgets->{'adminDelete'}[$count] = Gtk2::CheckButton->new();
            $widgets->{'admin'}->get_object('tableAccess')
              ->attach($widgets->{'adminDelete'}[$count],
                3, 4, $count + 2, $count + 3, 'fill', 'expand', 0, 0);
            $widgets->{'adminDisplay'}[$count] = Gtk2::CheckButton->new();
            $widgets->{'admin'}->get_object('tableAccess')
              ->attach($widgets->{'adminDisplay'}[$count],
                4, 5, $count + 2, $count + 3, 'fill', 'expand', 0, 0);
            $widgets->{'adminAdmin'}[$count] = Gtk2::CheckButton->new();
            $widgets->{'admin'}->get_object('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_object('tableAccess')->show_all();
    }
    my $confirm = $widgets->{'admin'}->get_object('dialogAdmin')->run();
    if ($confirm eq "ok") {
        open(ACCESS, ">" . $globals->{'accessfile'})
          || display_fatal(
            $errorcodes->{'fileopenwrite'} . $globals->{'accessfile'},
            $! . "\nSQL: " . $query);
        my $users =
          ($widgets->{'admin'}->get_object('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_object('dialogAdmin')->destroy();
    $globals->{'access'} = load_access();

}

#***

#****f* lyricue/load_access
# NAME
#   load_access
# SYNOPSIS
#   load_access()
# FUNCTION
#   Loading access settings
# INPUTS
# OUTPUT
# SOURCE
#
sub load_access {
    debug("Loading access settings");
    my $access = "blank";

    # Load the access settings
    if ($^O eq 'MSWin32') {
        $access = "spade";
    } else {
        open(ACCESS, $globals->{'accessfile'})
          || display_fatal(
            $errorcodes->{'fileopenread'} . $globals->{'accessfile'},
            $! . "\nSQL: " . $query);
        my $username = getpwuid($<);
        while (<ACCESS>) {
            chomp;
            if (/^$username/) {
                $access = $_;
                $access =~ s/^.*= *//g;
            }
        }
        close ACCESS;
    }
    if ($access eq "blank") {
        $access = "spade";
    }
    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) {
        if (defined $widgets->{'main'}->get_object($item)) {
            $widgets->{'main'}->get_object($item)->show();
        }
    }

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

#***

#****f* lyricue/add_user
# NAME
#   add_user
# SYNOPSIS
#   add_user()
# FUNCTION
#   Adding a user
# INPUTS
# OUTPUT
# SOURCE
#
sub add_user {
    debug("Adding a user");
    my $userxml = load_window('dialogPromptEntry');
    $userxml->get_object('dialogPromptEntry')
      ->set_title(fromutf(gettext("Add a user")));
    $userxml->get_object('labelPromptE')
      ->set_text(fromutf(gettext("Enter username")));
    my $confirm = $userxml->get_object('dialogPromptEntry')->run();
    if ($confirm eq "ok") {
        my $users =
          ($widgets->{'admin'}->get_object('tableAccess')->get('n-rows') - 2);
        debug("Users :" . $users);
        $widgets->{'admin'}->get_object('tableAccess')->resize($users + 3, 6);
        $widgets->{'adminUsername'}[$users] =
          Gtk2::Label->new($userxml->get_object('entryPromptE')->get_text());
        $widgets->{'admin'}->get_object('tableAccess')
          ->attach($widgets->{'adminUsername'}[$users],
            0, 1, $users + 2, $users + 3, 'fill', 'expand', 0, 0);
        $widgets->{'adminPlaylist'}[$users] = Gtk2::CheckButton->new();
        $widgets->{'admin'}->get_object('tableAccess')
          ->attach($widgets->{'adminPlaylist'}[$users],
            1, 2, $users + 2, $users + 3, 'fill', 'expand', 0, 0);
        $widgets->{'adminEdit'}[$users] = Gtk2::CheckButton->new();
        $widgets->{'admin'}->get_object('tableAccess')
          ->attach($widgets->{'adminEdit'}[$users],
            2, 3, $users + 2, $users + 3, 'fill', 'expand', 0, 0);
        $widgets->{'adminDelete'}[$users] = Gtk2::CheckButton->new();
        $widgets->{'admin'}->get_object('tableAccess')
          ->attach($widgets->{'adminDelete'}[$users],
            3, 4, $users + 2, $users + 3, 'fill', 'expand', 0, 0);
        $widgets->{'adminDisplay'}[$users] = Gtk2::CheckButton->new();
        $widgets->{'admin'}->get_object('tableAccess')
          ->attach($widgets->{'adminDisplay'}[$users],
            4, 5, $users + 2, $users + 3, 'fill', 'expand', 0, 0);
        $widgets->{'adminAdmin'}[$users] = Gtk2::CheckButton->new();
        $widgets->{'admin'}->get_object('tableAccess')
          ->attach($widgets->{'adminAdmin'}[$users],
            5, 6, $users + 2, $users + 3, 'fill', 'expand', 0, 0);
    }
    $widgets->{'admin'}->get_object('tableAccess')->show_all();
    close_dialog($userxml->get_object('dialogPromptEntry'));
}

#***

#****f* lyricue/search_changed
# NAME
#   search_changed
# SYNOPSIS
#   search_changed()
# FUNCTION
#   Search changed
# INPUTS
# OUTPUT
# SOURCE
#
sub search_changed {
    debug("Search changed");
    reset_timer($globals->{'update_timer'});
    $globals->{'update_timer'} = Glib::Timeout->add(500, \&update_available);
}

#***

#****f* lyricue/search_activated
# NAME
#   search_activated
# SYNOPSIS
#   search_activated()
# FUNCTION
#   Search activated
# INPUTS
# OUTPUT
# SOURCE
#
sub search_activated {
    debug("Search activated");
    my $model = $widgets->{'main'}->get_object('treeAvailable')->get_model;
    my $iter  = $model->get_iter_first;
    if (defined $iter) {
        my $songid = $model->get($iter, 3);
        add_single_song($songid);
        update_playlist();
    }
}

#***

#****f* lyricue/navigator_activated
# NAME
#   navigator_activated
# SYNOPSIS
#   navigator_activated()
# FUNCTION
#   Navigator activated
# INPUTS
# OUTPUT
# SOURCE
#
sub navigator_activated {
    debug("Navigator activated");
    my $verse =
      $widgets->{'main'}->get_object('entryNavVerse')->get_active_text();
    my $store = $widgets->{'main'}->get_object('entryNavVerse')->get_model();
    if (!defined $store) {
        $store = Gtk2::ListStore->new('Glib::String');
        $widgets->{'main'}->get_object('entryNavVerse')->set_model($store);
    }
    $store->set($store->prepend, 0, $verse);
    navigator_add();
}

#***

#****f* lyricue/init_preview
# NAME
#   init_preview
# SYNOPSIS
#   init_preview()
# FUNCTION
#   init preview
# INPUTS
# OUTPUT
# SOURCE
#
sub init_preview {
    debug("init preview");

    # Start/stop preview as needed
    if ($config->{'DynamicPreview'}) {
        if (!defined($widgets->{'preview'})) {
            $globals->{'preview_starting'} = 5;
            $widgets->{'main'}->get_object('checkPreview')->set_active(TRUE);
            $widgets->{'main'}->get_object('framePreview')->show();
            $widgets->{'main'}->get_object('framePreview')
              ->set_size_request(100, 75);
            $widgets->{'main'}->get_object('framePreview')
              ->set_params(0.5, 0.5, $config->{'Width'} / $config->{'Height'},
                0);
            $widgets->{'preview'} = Gtk2::Socket->new;
            $widgets->{'preview'}->show;
            $widgets->{'main'}->get_object('framePreview')
              ->add($widgets->{'preview'});
            $widgets->{'preview'}->set_size_request(-1, -1);

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

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

#***

#****f* lyricue/init_miniview
# NAME
#   init_miniview
# SYNOPSIS
#   init_miniview()
# FUNCTION
#   init miniview
# INPUTS
# OUTPUT
# SOURCE
#
sub init_miniview {
    # Start/stop miniview as needed
    if ($config->{'Miniview'}) {
        $widgets->{'main'}->get_object('vboxCurrent')->show();
        $widgets->{'main'}->get_object('checkCurrent')->set_active(TRUE);
        foreach my $display (keys %displays) {
            if (($displays{$display} & GONE) == GONE) {
                close_miniview($display);
                delete $displays{$display};
            } elsif (!defined($widgets->{'miniview'}->{$display})) {
                $widgets->{'miniview'}->{$display} = Gtk2::Socket->new;
                $widgets->{'miniview'}->{$display}->set_size_request(100, 75);
                $widgets->{'miniview_event'}->{$display} = Gtk2::EventBox->new;
                $widgets->{'miniview_frame'}->{$display} = Gtk2::AspectFrame->new($display, 0.5, 0.5, $config->{'Width'} / $config->{'Height'}, FALSE);
                $widgets->{'miniview_frame'}->{$display}->add($widgets->{'miniview'}->{$display});
                $widgets->{'miniview_event'}->{$display}->add($widgets->{'miniview_frame'}->{$display});
                $widgets->{'main'}->get_object('vboxCurrent')->pack_end($widgets->{'miniview_event'}->{$display}, TRUE,TRUE, 0);
                $widgets->{'miniview_event'}->{$display}->show;
                $widgets->{'miniview_frame'}->{$display}->show;
                $widgets->{'miniview'}->{$display}->show;
                $widgets->{'miniview_frame'}->{$display}->set_size_request(-1, -1);
                my $command = sprintf(
                        "%s -r %s -m %d -l %d -p '%s' -t miniview -e '%s'\n",
                        $globals->{'lyricue_server'},
                        $globals->{'mysqlhost'},
                        $widgets->{'miniview'}->{$display}->get_id,
                        $globals->{'preview_port'},
                        $globals->{'profile'},
                        $display
                    );
                debug($command);

                $globals->{'miniview_pid'}->{$display} = fork;
                if ($globals->{'miniview_pid'}->{$display} < 0) {
                   display_fatal(
                       "Unable to start the lyricue server as a miniview window",
                       $! . "\nSQL: " . $query);
                }
                if ($globals->{'miniview_pid'}->{$display} == 0) {
                    exec( $command );
                }

                $widgets->{'miniview'}->{$display}->signal_connect(
                    'plug-removed' => sub {
                        debug("Lyricue miniview died..restarting");
                        $widgets->{'main'}->get_object('vboxCurrent')
                        ->remove($widgets->{'miniview_frame'}->{'$display'});
                        init_miniview();
                        1;
                    }
                );
                $widgets->{'miniview_event'}->{$display}->signal_connect(
                    "button_release_event", "toggle_server_active", $display);
            }
            if ($miniviews{$display}) {
                $miniviews{$display} &= 1; # Clear check bit
            }
        }
    } else {
        if (defined $widgets->{'miniview'}) {
            foreach my $display(keys $widgets->{'miniview'}) {
                close_miniview($display);
            }
        }
        if (defined $widgets->{'main'}) {
            $widgets->{'main'}->get_object('vboxCurrent')->hide();
            $widgets->{'main'}->get_object('checkCurrent')->set_active(FALSE);
        }
    }
}

#***

#****f* lyricue/quick_save
# NAME
#   quick_save
# SYNOPSIS
#   quick_save()
# FUNCTION
#   Quick Save
# INPUTS
# OUTPUT
# SOURCE
#
sub quick_save {
    debug("Quick Save");
    my $buffer    = $widgets->{'main'}->get_object('textQuick')->get_buffer();
    my $songtext  = $buffer->get_text($buffer->get_bounds, FALSE);
    my $playorder = $widgets->{'main'}->get_object('textQuick')->{user_data};
    my $query = "SELECT data,type FROM playlist WHERE playorder=" . $playorder;
    my $sth = do_query($lyricDbh, $query, FALSE);
    my @row = $sth->fetchrow_array();

    if ($row[1] eq "play") {
        $query =
            "SELECT data,type FROM playlist WHERE playlist="
          . $row[0]
          . " ORDER BY playorder";
        $sth = do_query($lyricDbh, $query, FALSE);
        @row = $sth->fetchrow_array();
    }

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

        $query =
            "UPDATE page SET lyrics="
          . $lyricDbh->quote($songtext)
          . " WHERE pageid="
          . $row[0];
        $sth = do_query($lyricDbh, $query, FALSE);
    }
    update_playlist();
}

#****f* lyricue/quick_save_local
# NAME
#   quick_save_local
# SYNOPSIS
#   quick_save_local()
# FUNCTION
#   Quick Save
# INPUTS
# OUTPUT
# SOURCE
#
sub quick_save_local {
    debug("Quick Save Local");
    my $buffer    = $widgets->{'main'}->get_object('textQuick')->get_buffer();
    my $songtext  = $buffer->get_text($buffer->get_bounds, FALSE);
    my $playorder = $widgets->{'main'}->get_object('textQuick')->{user_data};
    my $query = "SELECT data,type FROM playlist WHERE playorder=" . $playorder;
    my $sth = do_query($lyricDbh, $query, FALSE);
    my @row = $sth->fetchrow_array();

    if ($row[1] eq "play") {
        $query =
            "SELECT data,type,playorder FROM playlist WHERE playlist="
          . $row[0]
          . " ORDER BY playorder";
        $sth = do_query($lyricDbh, $query, FALSE);
        @row       = $sth->fetchrow_array();
        $playorder = $row[2];
    }

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

        $query = "SELECT songid FROM page WHERE pageid=" . $row[0];
        $sth = do_query($lyricDbh, $query, FALSE);
        @row = $sth->fetchrow_array();
        my $songid = $row[0];

        $query =
            "INSERT INTO page (pagenum,songid,lyrics) VALUES ( 0,"
          . -$songid . ", "
          . $lyricDbh->quote($songtext) . ")";
        $sth = do_query($lyricDbh, $query, FALSE);

        my $pageid = $lyricDbh->{'mysql_insertid'};
        $query =
            "UPDATE playlist SET type=\"temp\", data=\""
          . $pageid
          . "\" WHERE playorder="
          . $playorder;
        $sth = do_query($lyricDbh, $query, FALSE);
    } elsif ($row[1] eq "temp") {
        if (!defined $row[0]) { $row[0] = ""; }

        $query =
            "UPDATE page SET lyrics="
          . $lyricDbh->quote($songtext)
          . " WHERE pageid="
          . $row[0];
        $sth = do_query($lyricDbh, $query, FALSE);
    }
    update_playlist();
}

# Just put the text in the quickview section onto the screen
#***

#****f* lyricue/quick_show
# NAME
#   quick_show
# SYNOPSIS
#   quick_show()
# FUNCTION
#   Quickshow textarea
# INPUTS
# OUTPUT
# SOURCE
#
sub quick_show {
    debug("Quickshow textarea");
    my $buffer = $widgets->{'main'}->get_object('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);
}

#***

#****f* lyricue/quick_osd
# NAME
#   quick_osd
# SYNOPSIS
#   quick_osd()
# FUNCTION
#   Quickshow OSD
# INPUTS
# OUTPUT
# SOURCE
#
sub quick_osd {
    my ($widget) = @_;
    debug("Update OSD");
    my $songtext = "";
    if (   ($widgets->{'main'}->get_object('buttonQuickOSD')->get_active())
        || ($widget eq $widgets->{'main'}->get_object('entryMainOSD')))
    {
        $songtext = $widgets->{'main'}->get_object('entryMainOSD')->get_text;
        $songtext =~ s/\n/#BREAK#/g;
        $songtext =~ s/:/#SEMI#/g;
    }
    update_display("osd", "default", $songtext);
}

#***

#****f* lyricue/resize_preview
# NAME
#   resize_preview
# SYNOPSIS
#   resize_preview($widget, $event)
# FUNCTION
#   Called when the preview windows are resized
# INPUTS
#   $widget -
#    $event -
# OUTPUT
# SOURCE
#
sub resize_preview {
    my ($widget, $event) = @_;

   #print $event."\n";
   #debug ("Resizing previews");
   #my $pos = $widgets->{'main'}->get_object('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
#***

#****f* lyricue/create_pixbuf
# NAME
#   create_pixbuf
# SYNOPSIS
#   create_pixbuf($data, $width, $height)
# FUNCTION
#
# INPUTS
#   $data -
#    $width -
#    $height -
# OUTPUT
# SOURCE
#
sub create_pixbuf {
    my ($data, $width, $height) = @_;
    my ($pixbuf);
    my ($type, $id) = split /;/, $data;
    if ($type eq "thmb") {
        debug("checking for thumbnail");
        my $query =
          "SELECT snapshot FROM playlist WHERE playorder=\"" . $id . "\"";
        my $sth = do_query($lyricDbh, $query, FALSE);
        my @row = $sth->fetchrow_array();
        if (defined $row[0]) {
            my $pixbuf_loader = Gtk2::Gdk::PixbufLoader->new();
            eval { $pixbuf_loader->write($row[0]); };
            eval { $pixbuf_loader->close() };
            $pixbuf = $pixbuf_loader->get_pixbuf();
        } else {
            return;
        }

        if ($width == 0) {
            return $pixbuf;
        } else {
            return $pixbuf->scale_simple($width, $height, 'nearest');
        }
    } elsif ($type eq "dir") {
        if (-d $id) {
            return;
        }
        if (($width > $globals->{'thumb_width'}) || ($width == 0)) {

            # Bigger than cache value so just send new pixbuf
            eval {
                $pixbuf = Gtk2::Gdk::Pixbuf->new_from_file_at_scale($id, $width,
                    $height, TRUE);
            };

            # Try thumbnailing with video thumbnailer
            my $thumbfile = "/tmp/lyricue-tmpthumb.png";
            if (-e $thumbfile) {
                unlink $thumbfile;
            }
            if ($@) {
                $id =~ s/\/[\/]*/\//g;

                my $command =
                    $globals->{'video-thumbnailer'}
                  . " --time=2 \""
                  . $id . "\" \""
                  . $thumbfile . "\"";
                system($command);
                debug("Command:" . $command);
                if (-e $thumbfile) {
                    $pixbuf = Gtk2::Gdk::Pixbuf->new_from_file($thumbfile);
                    $pixbuf =
                      $pixbuf->scale_simple($width, $height, 'bilinear');
                } else {
                    return undef;
                }
            } else {
                return $pixbuf;
            }
        }
        my $mtime = (stat($id))[9];
        $id =~ s/\/+/\//g;
        my $filename =
          $globals->{'thumbnail_factory'}->lookup("file://" . $id, $mtime);
        if (!defined $filename) {
            if ($globals->{'thumbnail_factory'}
                ->has_valid_failed_thumbnail("file://" . $id, $mtime))
            {
                return undef;
            } else {
                $pixbuf =
                  $globals->{'thumbnail_factory'}
                  ->generate_thumbnail("file://" . $id, $mtime);
                if ($pixbuf) {
                    $globals->{'thumbnail_factory'}
                      ->save_thumbnail($pixbuf, "file://" . $id, $mtime);
                    $pixbuf =
                      $pixbuf->scale_simple($width, $height, 'bilinear');
                } else {

                    # Try thumbnailing with video thumbnailer
                    my $thumbfile = "/tmp/lyricue-tmpthumb.png";
                    if (-e $thumbfile) {
                        unlink $thumbfile;
                    }
                    my $command =
                        $globals->{'video-thumbnailer'}
                      . " --time=2 \""
                      . $id . "\" \""
                      . $thumbfile . "\"";
                    system($command);
                    if (-e $thumbfile) {
                        $pixbuf = Gtk2::Gdk::Pixbuf->new_from_file($thumbfile);
                        $globals->{'thumbnail_factory'}
                          ->save_thumbnail($pixbuf, "file://" . $id, $mtime);
                        $pixbuf =
                          $pixbuf->scale_simple($width, $height, 'bilinear');
                    } else {
                        return undef;
                    }
                }
            }
        } else {
            $pixbuf =
              Gtk2::Gdk::Pixbuf->new_from_file_at_scale($filename, $width,
                $height, TRUE);
        }
        return $pixbuf;
    } else {
        my $query = "SELECT format, description, data FROM media WHERE id=\""
          . $id . "\"";
        my $sth = do_query($mediaDbh, $query, FALSE);
        my $row = $sth->fetchrow_hashref();
        if (defined $row->{'format'}) {
            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();
                }
            }
        } else {
            return NULL;
        }

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

#***

#****f* lyricue/import_image
# NAME
#   import_image
# SYNOPSIS
#   import_image()
# FUNCTION
#
# INPUTS
# OUTPUT
# SOURCE
#
sub import_image {
    import_media("img");
}

#***

#****f* lyricue/import_background
# NAME
#   import_background
# SYNOPSIS
#   import_background()
# FUNCTION
#
# INPUTS
# OUTPUT
# SOURCE
#
sub import_background {
    import_media("bg");
}

#***

#****f* lyricue/import_media
# NAME
#   import_media
# SYNOPSIS
#   import_media($type)
# FUNCTION
#   Import media
# INPUTS
#   $type -
# OUTPUT
# SOURCE
#
sub import_media {
    my ($type) = @_;
    debug("Import media to db of type:" . $type);
    my $filexml = load_window('dialogFileChooser');
    $filexml->get_object('buttonFileOK')
      ->signal_connect("clicked", "select_category", $filexml);
    $filexml->get_object('dialogFileChooser')->{user_data} = $type;
    $filexml->get_object('dialogFileChooser')->set_select_multiple(TRUE);
    $filexml->get_object('dialogFileChooser')->show_all();
}

#***

#****f* lyricue/select_category
# NAME
#   select_category
# SYNOPSIS
#   select_category($widget, $filexml)
# FUNCTION
#   select category
# INPUTS
#   $widget -
#    $filexml -
# OUTPUT
# SOURCE
#
sub select_category {
    my ($widget, $filexml) = @_;
    debug("select category");
    my $hashnum   = 0;
    my @filenames = $filexml->get_object('dialogFileChooser')->get_filenames;
    debug(@filenames);
    my $type = $filexml->get_object('dialogFileChooser')->{user_data};
    close_dialog($filexml->get_object('dialogFileChooser'));
    my $choosexml = load_window('dialogSelectCategory');

    my $query =
        "SELECT DISTINCT category FROM media WHERE type=\""
      . $type
      . "\" ORDER BY category";
    my $sth = do_query($mediaDbh, $query, FALSE);
    my @row;
    my $model = Gtk2::ListStore->new('Glib::String');
    $choosexml->get_object('comboSelectCategory')->set_model($model);
    $choosexml->get_object('comboSelectCategory')->set_text_column(0);

    while (@row = $sth->fetchrow_array()) {
        $choosexml->get_object('comboSelectCategory')->append_text($row[0]);
    }
    my $confirm = $choosexml->get_object('dialogSelectCategory')->run;
    if ($confirm == 1) {
        if ($choosexml->get_object('comboSelectCategory')->get_active_text() eq
            "")
        {
            return;
        }
        debug("Importing media");
        my $category =
          $choosexml->get_object('comboSelectCategory')->get_active_text();
        my $filename = "";
        foreach $filename (@filenames) {
            my $format = $filename;
            $format =~ s/^.*\.//g;
            my $description = $filename;
            $description =~ s/^.*\///g;
            $description =~ s/\..*?$//g;
            my $owner = getpwuid($<);
            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 (?,?,?,?,?,NOW(),?,?)}
            );
            my $rv =
              $sth->execute($category, "", $type, $format, $owner, $description,
                $data);
        }
    }
    close_dialog($choosexml->get_object('dialogSelectCategory'));
    my $category =
      $widgets->{'image'}->get_object('optionImageCategory')->{user_data};
    update_imagedir(
        $widgets->{'image'}->get_object('optionImageCategory')->{'user_data'},
        $category);
}

#***

#****f* lyricue/update_category
# NAME
#   update_category
# SYNOPSIS
#   update_category($selection, $choosexml)
# FUNCTION
#   Selected a category
# INPUTS
#   $selection -
#    $choosexml -
# OUTPUT
# SOURCE
#
sub update_category {
    my ($selection, $choosexml) = @_;
    debug("Selected a category");

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

#***

#****f* lyricue/update_quickedit
# NAME
#   update_quickedit
# SYNOPSIS
#   update_quickedit()
# FUNCTION
#   Updating Quick edit
# INPUTS
# OUTPUT
# SOURCE
#
sub update_quickedit {
    debug("Updating Quick edit");
    my $selection =
      $widgets->{'main'}->get_object('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;
            $sth = do_query($lyricDbh, $query, FALSE);
            my @row = $sth->fetchrow_array();
            if (defined($row[0])) {
                if (($row[0] eq "song") || ($row[0] eq "temp")) {
                    $query =
"SELECT lyrics FROM playlist as pl, page as pa WHERE pl.data=pa.pageid AND pl.playorder="
                      . $plnumber;
                    $sth = do_query($lyricDbh, $query, FALSE);
                    my @row2 = $sth->fetchrow_array();
                    if (!defined $row2[0]) { $row2[0] = ""; }
                    $quicktext = $row2[0];
                    $loop      = 1;
                    $widgets->{'main'}->get_object('textQuick')
                      ->set_wrap_mode('none');
                    $quicktext = $quicktext;
                } elsif ($row[0] eq "play") {
                    $query =
                        "SELECT playorder FROM playlist WHERE playlist="
                      . $row[1]
                      . " ORDER BY playorder";
                    $sth = do_query($lyricDbh, $query, FALSE);
                    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];
                    $sth = do_query($lyricDbh, $query, FALSE);
                    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 = fromutf(`$command`);
                        ($line[0], undef) = split(/\s\d/, $command_out, 2);
                        my @command_lines = split(/$line[0] /, $command_out);
                        my $lineno = 0;
                        foreach $lineno (1 .. @command_lines) {
                            my $line2 = $command_lines[$lineno - 1];
                            chomp($line2);
                            if ($line2 ne "") {
                                if ($lineno != @command_lines) {
                                    $line2 .= "\n";
                                }
                                $quicktext .= $line2;
                            }
                        }
                    } else {
                        my ($table, $dbname) =
                          split(/@/, $globals->{'bibledb'}, 2);
                        $query =
                            "SELECT chapternum,versenum,verse FROM "
                          . $table
                          . " WHERE book=\""
                          . $line[0]
                          . "\" AND chapternum="
                          . $line[1]
                          . " AND versenum>="
                          . $startv
                          . " AND versenum <= "
                          . $endv;
                        $sth = do_query($bibleDbh, $query, FALSE);

                        while (@row = $sth->fetchrow_array()) {
                            $quicktext .=
                              $row[0] . ":" . $row[1] . "   " . $row[2] . "\n";
                        }
                        $quicktext = fromutf($quicktext);
                    }
                    $loop = 1;
                    $widgets->{'main'}->get_object('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;
                }
            } else {
                $loop = 1;
            }
        }

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

#***

#****f* lyricue/media_move
# NAME
#   media_move
# SYNOPSIS
#   media_move()
# FUNCTION
#   Moving media
# INPUTS
# OUTPUT
# SOURCE
#
sub media_move {
    debug("Moving media");
    my $selection = $widgets->{'image'}->get_object('treeImage')->get_selection;
    my @selecteditems = $selection->get_selected_rows();
    my @id            = ();
    my $type =
      $widgets->{'image'}->get_object('optionImageCategory')->{'user_data'};
    if ($selecteditems[0]) {
        foreach (@selecteditems) {
            my $iter =
              $widgets->{'image'}->get_object('treeImage')
              ->get_model->get_iter($_);
            push @id,
              $widgets->{'image'}->get_object('treeImage')
              ->get_model->get($iter, 1);
        }
    }
    if (@id) {
        my $choosexml = load_window('windowChoosePlay');
        $choosexml->get_object('windowChoosePlay')
          ->set_title(fromutf(gettext("Select Category")));
        $choosexml->get_object('buttonLoad')->set_label("Select");
        $choosexml->get_object('buttonDelete')->hide();
        $choosexml->get_object('buttonRename')->hide();
        $choosexml->get_object('buttonLoad')
          ->signal_connect("clicked", "do_move_media", $choosexml);
        $choosexml->get_object('buttonNew')
          ->signal_connect("clicked", "do_move_media", $choosexml);
        $choosexml->get_object('buttonCancel')
          ->signal_connect("clicked", "close_dialog");
        $choosexml->get_object('windowChoosePlay')->show;
        $choosexml->get_object('treeChoosePlay')->{'user_data'}   = $type;
        $choosexml->get_object('windowChoosePlay')->{'user_data'} = \@id;
        my $store = $choosexml->get_object('treeChoosePlay')->get_model();

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

        my $query =
            "SELECT DISTINCT category FROM media WHERE type=\""
          . $type
          . "\" ORDER BY category";
        my $sth = do_query($mediaDbh, $query, FALSE);
        my @row;
        while (@row = $sth->fetchrow_array()) {
            my $iter = $store->append;
            $store->set($iter, 0, $row[0]);
        }
    }
}

#***

#****f* lyricue/do_move_media
# NAME
#   do_move_media
# SYNOPSIS
#   do_move_media($widget, $choosexml)
# FUNCTION
#   Moving media
# INPUTS
#   $widget -
#    $choosexml -
# OUTPUT
# SOURCE
#
sub do_move_media {
    my ($widget, $choosexml) = @_;
    debug("Moving media");
    if ($choosexml->get_object('entryChoosePlay')->get_text() ne "") {
        my $type     = $choosexml->get_object('treeChoosePlay')->{'user_data'};
        my $category = $choosexml->get_object('entryChoosePlay')->get_text();
        my $id = $choosexml->get_object('windowChoosePlay')->{'user_data'};
        foreach (@$id) {
            my $query =
                "UPDATE media SET category=\""
              . $category
              . "\" WHERE id=\""
              . $_ . "\"";
            $sth = do_query($mediaDbh, $query, FALSE);
        }
    }
    close_dialog($widget);
    my $category =
      $widgets->{'image'}->get_object('optionImageCategory')
      ->get_menu->get_active->{user_data};
    update_imagedir(
        $widgets->{'image'}->get_object('optionImageCategory')->{'user_data'},
        $category);
}

#***

#****f* lyricue/media_delete
# NAME
#   media_delete
# SYNOPSIS
#   media_delete()
# FUNCTION
#   Deleting media
# INPUTS
# OUTPUT
# SOURCE
#
sub media_delete {
    debug("Deleting media");
    my $selection = $widgets->{'image'}->get_object('treeImage')->get_selection;
    my @selecteditems = $selection->get_selected_rows();
    my @id            = ();
    if ($selecteditems[0]) {
        my $title = "";
        foreach (@selecteditems) {
            my $iter =
              $widgets->{'image'}->get_object('treeImage')
              ->get_model->get_iter($_);
            push @id,
              $widgets->{'image'}->get_object('treeImage')
              ->get_model->get($iter, 1);
            $title .= "\""
              . $widgets->{'image'}->get_object('treeImage')
              ->get_model->get($iter, 0) . "\", ";
        }
        if ($title ne "") {
            $title =~ s/, $//;
            debug("Deleting $title");
            my $labelText =
              fromutf(gettext("Are you sure you wish to delete "))
              . $title . "\n";
            my $deletexml = load_window('dialogConfirm');
            $deletexml->get_object('labelDelete')->set_text($labelText);
            $deletexml->get_object('dialogConfirm')
              ->set_title(fromutf(gettext("Confirm Delete Image")));
            my $confirm = $deletexml->get_object('dialogConfirm')->run();

            if ($confirm eq "ok") {

                foreach (@id) {
                    $_ =~ s/^db;//g;
                    my $query = "DELETE FROM media WHERE id=" . $_;
                    my $sth = do_query($mediaDbh, $query, FALSE);

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

}

#***

#****f* lyricue/rename_media
# NAME
#   rename_media
# SYNOPSIS
#   rename_media($widget, $id, $newname)
# FUNCTION
#   rename media
# INPUTS
#   $widget -
#    $id -
#    $newname -
# OUTPUT
# SOURCE
#
sub rename_media {
    my ($widget, $id, $newname) = @_;
    debug("rename media");
    my $oldname = $widget->get('text');
    if ($oldname ne $newname) {
        my $iter =
          $widgets->{'image'}->get_object('treeImage')
          ->get_model->get_iter_from_string($id);
        $id =
          $widgets->{'image'}->get_object('treeImage')
          ->get_model->get($iter, 1);
        debug("Renaming media from $oldname to $newname");
        my $query = "UPDATE media SET description=\"$newname\" WHERE id=$id";
        my $sth = do_query($mediaDbh, $query, FALSE);
        update_imagedir(
            $widgets->{'image'}->get_object('optionImageCategory')
              ->{'user_data'},
            $widgets->{'image'}->get_object('optionImageCategory')
              ->get_menu->get_active->{user_data}
        );
    }
}

#***

#****f* lyricue/change_colour_media
# NAME
#   change_colour_media
# SYNOPSIS
#   change_colour_media($widget)
# FUNCTION
#   change colour media
# INPUTS
#   $widget -
# OUTPUT
# SOURCE
#
sub change_colour_media {
    my ($widget) = @_;
    debug("change colour media");
    my $fontcolour =
      $widgets->{'image'}->get_object('entryImageFontColour')->get('text');
    my $shadowcolour =
      $widgets->{'image'}->get_object('entryImageShadowColour')->get('text');
    my $id = $widgets->{'image'}->get_object('imageImage')->{user_data};

    if (defined($id)) {
        my ($type, $id) = split /;/, $id;
        if (!defined $id) {
            $id   = $type;
            $type = "db";
        }
        $id = substr $id, -100;

        if ($type eq "db") {
            debug("change_colour_media: $fontcolour, $shadowcolour, $id");
            if (!($fontcolour =~ /^#/)) {
                $fontcolour = "";
            }
            if (!($shadowcolour =~ /^#/)) {
                $shadowcolour = "";
            }
            my $query =
"UPDATE media SET textcolour=\"$fontcolour\", shadowcolour=\"$shadowcolour\" WHERE id=$id";
            my $sth = do_query($mediaDbh, $query, FALSE);
        } elsif ($type eq "dir") {
            debug("change_colour_media: $fontcolour, $shadowcolour, $id");
            my $query =
              "DELETE FROM media WHERE format=\"file\" AND category=\""
              . $id . "\"";
            my $sth = do_query($mediaDbh, $query, FALSE);
            $query =
"INSERT INTO media (format, category, textcolour, shadowcolour) VALUES (\"file\", \"$id\", \"$fontcolour\", \"$shadowcolour\")";
            $sth = do_query($mediaDbh, $query, FALSE);
        }
    }
    change_preview();
}

#***

#****f* lyricue/restore_db
# NAME
#   restore_db
# SYNOPSIS
#   restore_db()
# FUNCTION
#   Restoring DB
# INPUTS
# OUTPUT
# SOURCE
#
sub restore_db {
    debug("Restoring DB");

    # Get filename of DB
    my $fileDialog = load_window('dialogFileChooser');
    $fileDialog->get_object('dialogFileChooser')
      ->set_title(fromutf(gettext("Select Database Backup file")));
    my $response = $fileDialog->get_object('dialogFileChooser')->run();
    if ($response) {
        my $filename =
          $fileDialog->get_object('dialogFileChooser')->get_filename;
        close_dialog($fileDialog->get_object('dialogFileChooser'));

        my $confirmDialog = load_window('dialogConfirm');
        $confirmDialog->get_object('dialogConfirm')
          ->set_title(fromutf(gettext("Confirm Restore Database")));
        $confirmDialog->get_object('labelDelete')->set_text(
            fromutf(
                gettext(
"WARNING: Restoring this database will overwrite your current database"
                )
            )
        );
        my $confirm = $confirmDialog->get_object('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;
                    $db =~ s/\`//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   = do_query($dbh,$query, TRUE);
                        }
                        my $sth = do_query($dbh,$insert, TRUE);
                    }
                }
            }
        }
        close_dialog($confirmDialog->get_object('dialogConfirm'));
    } else {
        close_dialog($fileDialog->get_object('dialogFileChooser'));
    }
}

#***

#****f* lyricue/import_songs
# NAME
#   import_songs
# SYNOPSIS
#   import_songs($filename)
# FUNCTION
#   Import the songs from a single xml file
# INPUTS
#   $filename - file to import songs from
# OUTPUT
# SOURCE
#
sub import_songs {
    my ($filename) = @_;
    debug("Importing songs from xml file");

    my ($fh);
    if ($filename =~ /[zZ]$/) {
        open $fh, "gzip -dc \"" . $filename . "\" |";
    } else {
        open $fh, $filename;
    }

    my $xml = XMLin(
        $fh,
        ForceArray    => ['song', 'page'],
        SuppressEmpty => '',
        NoAttr        => 1,
        KeyAttr       => []
    );
    close $fh;
    my $songs = $xml->{'song'};

    $widgets->{'select_songs'} = load_window('dialogSelectSongs');

    my $model = Gtk2::ListStore->new(
        'Glib::Boolean', 'Glib::String', 'Glib::String', 'Glib::String',
        'Glib::Uint',    'Glib::Uint'
    );
    my $renderer = Gtk2::CellRendererToggle->new;
    $renderer->signal_connect(
        toggled => sub {
            my ($cell, $path_str, $model) = @_;
            my $path   = Gtk2::TreePath->new_from_string($path_str);
            my $column = 0;
            my $iter   = $model->get_iter($path);
            my ($toggle_item) = $model->get($iter, $column);
            $toggle_item ^= 1;
            debug 'setting ' . $path_str . ' to ' . $toggle_item;

            # set new value
            $model->set($iter, $column, $toggle_item);
        },
        $model
    );
    my $column1 =
      Gtk2::TreeViewColumn->new_with_attributes("", $renderer, active => 0);
    my $column2 =
      Gtk2::TreeViewColumn->new_with_attributes(fromutf(gettext("Title")),
        Gtk2::CellRendererText->new, text => 1);
    my $column3 =
      Gtk2::TreeViewColumn->new_with_attributes(fromutf(gettext("Artist")),
        Gtk2::CellRendererText->new, text => 2);
    my $column4 =
      Gtk2::TreeViewColumn->new_with_attributes(fromutf(gettext("Book")),
        Gtk2::CellRendererText->new, text => 3);
    my $column5 =
      Gtk2::TreeViewColumn->new_with_attributes(fromutf(gettext("Song Number")),
        Gtk2::CellRendererText->new, text => 4);
    $column1->set_resizable(TRUE);
    $column2->set_resizable(TRUE);
    $column3->set_resizable(TRUE);
    $column4->set_resizable(TRUE);
    $column5->set_resizable(TRUE);
    $widgets->{'select_songs'}->get_object('treeSongImport')
      ->append_column($column1);
    $widgets->{'select_songs'}->get_object('treeSongImport')
      ->append_column($column2);
    $widgets->{'select_songs'}->get_object('treeSongImport')
      ->append_column($column3);
    $widgets->{'select_songs'}->get_object('treeSongImport')
      ->append_column($column4);
    $widgets->{'select_songs'}->get_object('treeSongImport')
      ->append_column($column5);

    my @sorted_songs = sort { $a->{'name'} cmp $b->{'name'} } @$songs;
    foreach my $songnum (0 .. (@sorted_songs - 1)) {
        debug("Adding " . $songnum . ":" . $sorted_songs[$songnum]->{'name'});
        my $iter = $model->append;
        $model->set(
            $iter,                               0,
            TRUE,                                1,
            $sorted_songs[$songnum]->{'name'},   2,
            $sorted_songs[$songnum]->{'artist'}, 3,
            $sorted_songs[$songnum]->{'book'},   4,
            $sorted_songs[$songnum]->{'number'}, 5,
            $songnum
        );
    }
    $widgets->{'select_songs'}->get_object('treeSongImport')->set_model($model);
    $widgets->{'select_songs'}->get_object('treeSongImport')
      ->set_headers_clickable(TRUE);
    my $confirm =
      $widgets->{'select_songs'}->get_object('dialogSelectSongs')->run();
    if ($confirm == 1) {
        $model->foreach(\&do_import_songs, $songs);
        update_available();
    }
    close_dialog($widgets->{'select_songs'}->get_object('dialogSelectSongs'));
}

#***

#****f* lyricue/import_selection
# NAME
#   import_selection
# SYNOPSIS
#   import_selection($filename)
# FUNCTION
#   Change the selection in the import dialog
# OUTPUT
sub import_selection {
    my ($widget) = @_;
    debug("Selection changing");
    my $model =
      $widgets->{'select_songs'}->get_object('treeSongImport')->get_model;
    my $toggle = FALSE;
    if ($widget eq $widgets->{'select_songs'}->get_object('buttonImportAll')) {
        $toggle = TRUE;
    }
    $model->foreach(
        sub {
            my ($store, $path, $iter, $toggle) = @_;
            $store->set($iter, 0, $toggle);
        },
        $toggle
    );
}

#***

#****f* lyricue/do_import_songs
# NAME
#   do_import_songs
# SYNOPSIS
#   do_import_songs($store, $path, $iter, $songs)
# FUNCTION
#   Import each individual song if marked for import
# INPUTS
#   $store - List store
#   $path - Path of item being checked
#   $iter - Iter of item
#   $songs - Parsed xml input
# OUTPUT
sub do_import_songs {
    my ($store, $path, $iter, $songs) = @_;
    my $songnum = $store->get($iter, 5);
    if ($store->get($iter, 0)) {
        my $song = @$songs[$songnum];
        debug("Importing " . $song->{'name'});

        # Find next id
        my $query = "SELECT MAX(id)+1 FROM lyricMain WHERE id < 2000000";
        my $sth   = do_query($lyricDbh, $query, FALSE);
        my @row    = $sth->fetchrow_array;
        my $songid = $row[0];
        if ((!defined $songid) || ($songid < 1)) {
            $songid = 1;
        }
        $query =
"INSERT INTO lyricMain ( id, title, songnum, book, artist, keywords, copyright, entered, written ) VALUES ( ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())";
        $sth = $lyricDbh->prepare($query);
        my $rv =
          $sth->execute($songid, $song->{'name'}, $song->{'number'},
            $song->{'book'}, $song->{'artist'}, $song->{'keywords'},
            $song->{'copyright'})
          || database_failure($errorcodes->{'sqlexecute'},
            $! . "\nSQL: " . $query);

        my $pages   = $song->{'page'};
        my $pagenum = 0;
        foreach (@$pages) {
            $pagenum++;
            debug("Page Add : " . $pagenum);
            $query =
                "INSERT INTO page (songid,pagenum,lyrics) VALUES ("
              . $songid . ", "
              . $pagenum . ", "
              . quote($_) . ")";
            $sth = do_query($lyricDbh, $query, FALSE);
        }
    }
}

#***

#****f* lyricue/export_songs
# NAME
#   export_songs
# SYNOPSIS
#   export_songs($filename)
# FUNCTION
#   Export the songs into a single xml file
# INPUTS
#   $filename - file to save songs as
# OUTPUT
# SOURCE
#
sub export_songs {
    my ($filename) = @_;

    my @songs = ();

    my $query = "SELECT * FROM lyricMain";
    my $sth = do_query($lyricDbh, $query, FALSE);
    while (my $row = $sth->fetchrow_hashref()) {
        my $query2 =
            "SELECT lyrics FROM page WHERE songid="
          . $row->{'id'}
          . " ORDER BY pagenum";
        my $sth2 = do_query($lyricDbh,$query2,TRUE);
        my @pages = ();
        while (my $row2 = $sth2->fetchrow_hashref()) {
            push @pages, $row2->{'lyrics'};
        }

        my $song = {
            'name'      => [$row->{'title'}],
            'number'    => [$row->{'songnum'}],
            'book'      => [$row->{'book'}],
            'artist'    => [$row->{'artist'}],
            'keywords'  => [$row->{'keywords'}],
            'copyright' => [$row->{'copyright'}],
            'page'      => \@pages
        };
        push @songs, $song;
    }
    my $out = {'song' => \@songs};
    my $writer = XML::Simple->new();
    open my $fh, "| gzip -c - > \"" . $filename . "\"";
    binmode($fh, ":utf8");
    $writer->XMLout($out, OutputFile => $fh, RootName => 'lyricue');
    close $fh;

}

#***

#****f* lyricue/backup_db
# NAME
#   backup_db
# SYNOPSIS
#   backup_db()
# FUNCTION
#   Backup DB selected
# INPUTS
# OUTPUT
# SOURCE
#
sub backup_db {
    debug("Backup DB selected");
    $widgets->{'backupdb'} = load_window('dialogDatabase');
    $widgets->{'backupdb'}->get_object('entryPrefDBFilename')
      ->set_text($ENV{"HOME"} . "/Lyricue_DB.gz");
    my $response = $widgets->{'backupdb'}->get_object('dialogDatabase')->run();
    if ($response eq "ok") {
        my $filename =
          $widgets->{'backupdb'}->get_object('entryPrefDBFilename')->get_text();
        if (!($filename =~ /\.gz$/)) {
            $filename .= ".gz";
        }
        my $databases = "";
        if ($widgets->{'backupdb'}->get_object('checkLyricdb')->get_active()) {
            $databases .= "lyricDb ";
        }
        if ($widgets->{'backupdb'}->get_object('checkMediadb')->get_active()) {
            $databases .= "mediaDb ";
        }

        my $command =
"mysqldump --skip-extended-insert --no-create-db --no-create-info --complete-insert --user=lyric --password='' --databases "
          . $databases
          . " | gzip -c > "
          . $filename;
        debug($command);
        system $command;
        if ($? == -1) {
            debug("Failed to execute: " . $command);
        } else {
            debug("Backed up to " . $filename);
        }
    }
    close_dialog($widgets->{'backupdb'}->get_object('dialogDatabase'));
}

#***

#****f* lyricue/backup_db_browse
# NAME
#   backup_db_browse
# SYNOPSIS
#   backup_db_browse()
# FUNCTION
#   Browsing for BackupDB filename
# INPUTS
# OUTPUT
# SOURCE
#
sub backup_db_browse {
    debug("Browsing for BackupDB filename");
    my $fileDialog = load_window('dialogFileChooser');
    $fileDialog->get_object('dialogFileChooser')
      ->set_filename(
        $widgets->{'backupdb'}->get_object('entryPrefDBFilename')->get_text());
    my $response = $fileDialog->get_object('dialogFileChooser')->run();
    if ($response) {
        my $filename =
          $fileDialog->get_object('dialogFileChooser')->get_filename;
        if (!($filename =~ /\.gz$/)) {
            $filename .= ".gz";
        }
        $widgets->{'backupdb'}->get_object('entryPrefDBFilename')
          ->set_text($filename);
    }
    close_dialog($fileDialog->get_object('dialogFileChooser'));
}

#***

#****f* lyricue/get_bibles
# NAME
#   get_bibles
# SYNOPSIS
#   get_bibles()
# FUNCTION
#   Getting available bibles
# INPUTS
# OUTPUT
# SOURCE
#
sub get_bibles {
    my $bibles;
    debug("Getting available bibles");

    if ($^O ne '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, $errorcodes->{'bibledbopen'}, FALSE);
        if (defined $db) {
            my @tables = $db->tables;
            my $table;
            foreach (@tables) {
                my $tablename = $_;
                $tablename =~ s/^.*`(.*)`$/$1/g;
                $tablename =~ s/^"(.*)"$/$1/g;
                my $tmpfields =
                  $db->selectall_arrayref("describe " . $tablename);
                if (!defined $tmpfields) {
                    next;
                }
                my @fields = @{$tmpfields};
                my $a      = $fields[0]->[0];
                if ($a eq "verseid") {
                    my $query =
                        "SELECT verse FROM "
                      . $tablename
                      . " WHERE book=\"Bible\";";
                    my $sth = do_query($db,$query, FALSE);
                    my $row = $sth->fetchrow_hashref();
                    if (defined $row->{'verse'}) {
                        $bibles->{$tablename . "@" . $dbname} =
                          "db;" . $row->{'verse'};
                    }
                    $sth->finish;
                }
            }
            $db->disconnect();
        }
    }

    return $bibles;
}

#***

#****f* lyricue/navigator_changed
# NAME
#   navigator_changed
# SYNOPSIS
#   navigator_changed()
# FUNCTION
#   Navigator changed
# INPUTS
# OUTPUT
# SOURCE
#
sub navigator_changed {
    debug("Navigator changed");
    reset_timer($globals->{'nav_update_timer'});
    $globals->{'nav_update_timer'} =
      Glib::Timeout->add(500, \&navigator_update);
}

#***

#****f* lyricue/navigator_update
# NAME
#   navigator_update
# SYNOPSIS
#   navigator_update()
# FUNCTION
#   Updating navigator
# INPUTS
# OUTPUT
# SOURCE
#
sub navigator_update {
    debug("Updating navigator");
    reset_timer($globals->{'nav_update_timer'});
    if ((!defined $globals->{'bibledb'}) || ($globals->{'bibledb'} eq "")) {
        display_message($errorcodes->{'nobible'});
        return;
    }
    my $buffer = Gtk2::TextBuffer->new();
    $buffer->set_text("");
    my $iter = $buffer->get_iter_at_offset(0);
    my $verse =
      $widgets->{'main'}->get_object('entryNavVerse')->get_active_text();
    $verse =~ s/ (\D)/_$1/g;
    my ($book, $chapter, $startverse, $endverse) =
      split(/[ :\-,]/, $verse, 4);

    my $browser = "";
    if (defined $book) {
        $book =~ s/_/ /g;
    } else {
        $book    = "";
        $browser = "init";
        biblebrowser_init();
    }

    if ((!defined $chapter) or ($chapter eq "") or ($chapter =~ /\D/)) {
        $chapter = 1;
        if ($browser eq "") {
            $browser = "chapter";
            biblebrowser_chapter(NULL, $book, "entry");
        }
    }
    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 ($browser eq "") {
        $browser = "verse";
        my $newbook = "";
        if (!$globals->{'usesword'}) {
            my ($table, $dbname) = split(/@/, $globals->{'bibledb'}, 2);
            my $query =
                "SELECT DISTINCT(book) FROM "
              . $table
              . " WHERE book LIKE \""
              . $book . "%\"";
            my $sth = do_query($bibleDbh, $query, FALSE);
            my @bookrow = $sth->fetchrow_array();
            if ((defined $bookrow[0]) && ($book ne "")) {
                $newbook = $bookrow[0];
            }
        } else {
            my $command = sprintf(
                "%s -b %s -e UTF8 -k '%s' 1:1 | head -1",
                $globals->{'diatheke'},
                $globals->{'bibledb'}, $book
            );
            my $command_out = fromutf(`$command`);
            ($newbook, undef) = split(/\s\d/, $command_out, 2);
        }
        my $text =
          $widgets->{'main'}->get_object('entryNavVerse')->get_active_text();
        $text =~ s/$book[ :\-]/$newbook /i;
        $widgets->{'main'}->get_object('entryNavVerseText')->set_text($text);
        $widgets->{'main'}->get_object('entryNavVerseText')->set_position(-1);
        biblebrowser_verse(NULL, 0, "entry");
        foreach my $num ($startverse .. $endverse) {
            my $button = "button" . ($num - 1);
            if (defined $widgets->{'bibleBrowser'}{$button}) {
                $widgets->{'bibleBrowser'}{$button}->set_active(TRUE);
            }
        }
    }
    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
        );
        my $command_out = fromutf(`$command`);
        ($book, undef) = split(/\s\d/, $command_out, 2);
        my @command_lines = split(/$book /, $command_out);
        my $mark          = 0;
        my $lineno        = 0;
        foreach $lineno (1 .. @command_lines) {
            my $line2 = $command_lines[$lineno - 1];
            chomp($line2);
            if ($line2 ne "") {
                if ($lineno != @command_lines) {
                    $line2 .= "\n";
                }
                $buffer->create_mark($mark, $iter, TRUE);
                insert_link($buffer, $iter, $mark, $line2);
                $mark++;
            }
        }
        $buffer->create_mark($mark, $iter, TRUE);

        # remove the trailing bible name
        #$quicktext =~ s/\($globals->{'bibledb'}\)/\n/g;
    } else {
        my ($table, $dbname) = split(/@/, $globals->{'bibledb'}, 2);
        $query =
          "SELECT book FROM " . $table . " WHERE book LIKE \"" . $book . "%\"";
        $sth = do_query($bibleDbh, $query, FALSE);
        my @bookrow = $sth->fetchrow_array();
        if ((defined $bookrow[0]) && ($book ne "")) {
            $book = $bookrow[0];
            my ($table, $dbname) = split(/@/, $globals->{'bibledb'}, 2);
            $query =
                "SELECT book,chapternum,versenum,verse FROM "
              . $table
              . " WHERE book LIKE \""
              . $book
              . "%\" AND chapternum="
              . $chapter
              . " AND versenum>="
              . $startverse
              . " AND versenum <= "
              . $endverse
              . " ORDER BY versenum";
            $sth = do_query($bibleDbh, $query, FALSE);
            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_object('entryNavVerse')->{user_data} =
      $book . " " . $chapter . ":" . $startverse . "-" . $endverse;
    my $oldtext = get_buffer_text(
        $widgets->{'main'}->get_object('textNavView')->get_buffer());
    my $quicktext = get_buffer_text($buffer);
    if ($oldtext ne $quicktext) {
        $widgets->{'main'}->get_object('textNavView')->set_buffer($buffer);
        if ($widgets->{'main'}->get_object('checkNavAuto')->get_active()) {
            navigator_show();
        }
    }
}

#***

#****f* lyricue/navigator_enter
# NAME
#   navigator_enter
# SYNOPSIS
#   navigator_enter()
# FUNCTION
#
# INPUTS
# OUTPUT
# SOURCE
#
sub navigator_enter {
    navigator_update();
    navigator_show();
}

#***

#****f* lyricue/navigator_show
# NAME
#   navigator_show
# SYNOPSIS
#   navigator_show()
# FUNCTION
#   Bible navigator showing on server
# INPUTS
# OUTPUT
# SOURCE
#
sub navigator_show {
    debug("Bible navigator showing on server");
    my $buffer = $widgets->{'main'}->get_object('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_object('entryNavVerse')->{user_data}
      . "#BREAK##BREAK##BREAK#wrap";

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

#***

#****f* lyricue/navigator_add
# NAME
#   navigator_add
# SYNOPSIS
#   navigator_add()
# FUNCTION
#   Adding verses from Bible navigator to playlist
# INPUTS
# OUTPUT
# SOURCE
#
sub navigator_add {
    debug("Adding verses from Bible navigator to playlist");
    my $verse = $widgets->{'main'}->get_object('entryNavVerse')->{user_data};
    $verse =~ s/ (\D)/_$1/;
    my ($book, $chapter, $startverse, $endverse) =
      split(/[ :\-,]/, $verse, 4);
    $book =~ s/_/ /g;
    insert_verse($book, $chapter, $startverse, $endverse);
}

#***

#****f* lyricue/navigator_next
# NAME
#   navigator_next
# SYNOPSIS
#   navigator_next()
# FUNCTION
#   Showing next verse
# INPUTS
# OUTPUT
# SOURCE
#
sub navigator_next {
    debug("Showing next verse");
    my $verse = $widgets->{'main'}->get_object('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;
        }
    }
    if (!$globals->{'usesword'}) {
        my ($table, $dbname) = split(/@/, $globals->{'bibledb'}, 2);
        my $query =
            "SELECT MAX(versenum) FROM "
          . $table
          . " WHERE book=\""
          . $book
          . "\" AND chapternum="
          . $chapter;
        my $sth = do_query($bibleDbh, $query, FALSE);
        my @row = $sth->fetchrow_array();

        if ($endverse > $row[0]) {
            $endverse = $row[0];
        }
        if ($startverse > $row[0]) {
            $startverse = $row[0];
        }
    }

    $verse = $book . " " . $chapter . ":" . $startverse . "-" . $endverse;
    $widgets->{'main'}->get_object('entryNavVerseText')->set_text($verse);
    navigator_update();
    navigator_show();
}

#***

#****f* lyricue/navigator_prev
# NAME
#   navigator_prev
# SYNOPSIS
#   navigator_prev()
# FUNCTION
#   Showing next verse
# INPUTS
# OUTPUT
# SOURCE
#
sub navigator_prev {
    debug("Showing previous verse");
    my $verse = $widgets->{'main'}->get_object('entryNavVerse')->{user_data};
    $verse =~ s/ (\D)/_$1/;
    my ($book, $chapter, $startverse, $endverse) =
      split(/[ :\-,]/, $verse, 4);
    $book =~ s/_/ /g;
    my $loop = TRUE;
    $startverse--;
    if ($startverse < 1) {
        $startverse = 1;
    }
    $endverse = get_max_verse($book, $chapter, $startverse, $endverse);
    $verse = $book . " " . $chapter . ":" . $startverse . "-" . $endverse;
    $widgets->{'main'}->get_object('entryNavVerseText')->set_text($verse);
    navigator_update();
    navigator_show();
}

#***

#****f* lyricue/reset_timer
# NAME
#   reset_timer
# SYNOPSIS
#   reset_timer($timer)
# FUNCTION
#   Cancelling timer
# INPUTS
#   $timer -
# OUTPUT
# SOURCE
#
sub reset_timer {
    my ($timer) = @_;
    if ($timer) {
        debug("Cancelling timer");
        Glib::Source->remove($timer);
        $timer = FALSE;
    }
}

#***

#****f* lyricue/do_pending
# NAME
#   do_pending
# SYNOPSIS
#   do_pending()
# FUNCTION
#
# INPUTS
# OUTPUT
# SOURCE
#
sub do_pending {
    while (Gtk2->events_pending) {
        Gtk2->main_iteration;
    }
}

#***

#****f* lyricue/get_max_verse
# NAME
#   get_max_verse
# SYNOPSIS
#   get_max_verse($book, $chapter, $startverse, $endverse)
# FUNCTION
#
# INPUTS
#   $book -
#    $chapter -
#    $startverse -
#    $endverse -
# OUTPUT
# SOURCE
#
sub get_max_verse {
    my ($book, $chapter, $startverse, $endverse) = @_;

    if ($config->{'Main'} eq "") {
        $config = load_config($globals->{'profile'});
    }
    if ($config->{'Width'} == 0) {
        $config->{'Width'}  = 800;
        $config->{'Height'} = 600;
    }
    debug(  "Get max verse that fits within "
          . $config->{'Width'} . "x"
          . $config->{'Height'});

    my $pageHeight = 0;

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

    if (!$globals->{'usesword'}) {
        my ($table, $dbname) = split(/@/, $globals->{'bibledb'}, 2);

        $query =
            "SELECT * FROM "
          . $table
          . " WHERE book=\""
          . $book
          . "\" AND chapternum="
          . $chapter
          . " AND versenum>="
          . $startverse
          . " AND versenum <= "
          . $endverse
          . " ORDER BY versenum";
        $sth = do_query($bibleDbh, $query, FALSE);
    }

    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
            );
            $line = `$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 + $config->{'OverscanV'}))
        {
            if ($versenum > $startverse) { $versenum--; }
            return $versenum;
        } else {
            $page = $page . "\n" . $line;
        }
    }
    if ($versenum > $endverse) {
        $versenum = $endverse;
    }
    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.
#
#***

#****f* lyricue/insert_link
# NAME
#   insert_link
# SYNOPSIS
#   insert_link($buffer, $iter, $mark, $verse)
# FUNCTION
#
# INPUTS
#   $buffer -
#    $iter -
#    $mark -
#    $verse -
# OUTPUT
# SOURCE
#
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.
#
#***

#****f* lyricue/follow_if_link
# NAME
#   follow_if_link
# SYNOPSIS
#   follow_if_link($text_view, $iter)
# FUNCTION
#   Showing $book $verse
# INPUTS
#   $text_view -
#    $iter -
# OUTPUT
# SOURCE
#
sub follow_if_link {
    my ($text_view, $iter) = @_;
    my $verse = $widgets->{'main'}->get_object('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;
        }
    }
}

#***

#****f* lyricue/navigator_event_after
# NAME
#   navigator_event_after
# SYNOPSIS
#   navigator_event_after($text_view, $event)
# FUNCTION
#   Navigator text clicked
# INPUTS
#   $text_view -
#    $event -
# OUTPUT
# SOURCE
#
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;
}

#***

#****f* lyricue/text_set_cursor_if_appropriate
# NAME
#   text_set_cursor_if_appropriate
# SYNOPSIS
#   text_set_cursor_if_appropriate($text_view, $x, $y)
# FUNCTION
#
# INPUTS
#   $text_view -
#    $x -
#    $y -
# OUTPUT
# SOURCE
#
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.
#
#***

#****f* lyricue/text_motion_notify_event
# NAME
#   text_motion_notify_event
# SYNOPSIS
#   text_motion_notify_event($text_view, $event)
# FUNCTION
#
# INPUTS
#   $text_view -
#    $event -
# OUTPUT
# SOURCE
#
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).
#
#***

#****f* lyricue/text_visibility_notify_event
# NAME
#   text_visibility_notify_event
# SYNOPSIS
#   text_visibility_notify_event($text_view, $event)
# FUNCTION
#
# INPUTS
#   $text_view -
#    $event -
# OUTPUT
# SOURCE
#
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;
}

#***

#****f* lyricue/import_song_from_file
# NAME
#   import_song_from_file
# SYNOPSIS
#   import_song_from_file($filename)
# FUNCTION
#
# INPUTS
#   $filename -
# OUTPUT
# SOURCE
#
sub import_song_from_file {
    my ($filename) = @_;
    import_songs($filename);
}

#***

#****f* lyricue/import_song_songselect
# NAME
#   import_song_songselect
# SYNOPSIS
#   import_song_songselect($filename)
# FUNCTION
#   import songselect song
# INPUTS
#   $filename -
# OUTPUT
# SOURCE
#
sub import_song_songselect {
    my ($filename) = @_;
    debug("import songselect song");
    open(SONG, $filename) || return;
    binmode SONG, ":encoding(utf8)";

    my $hashnum = 0;

    while (<SONG>) {
        $_ =~ s/
//g;
        chomp;
        my @line = split(/=/, $_, 2);
        $_ = $line[0];
        if (/Title/) {
            $widgets->{'add'}->get_object('entryEditName')->set_text($line[1]);
        } elsif (/Author/) {
            $widgets->{'add'}->get_object('entryEditArtist')
              ->set_text($line[1]);
        } elsif (/Copyright/) {
            $widgets->{'add'}->get_object('entryEditCopyright')
              ->set_text($line[1]);
        } elsif (/Themes/) {
            $line[1] =~ s/\/t/ /g;
            $widgets->{'add'}->get_object('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;

}

#***

#****f* lyricue/import_song_opw
# NAME
#   import_song_opw
# SYNOPSIS
#   import_song_opw($filename)
# FUNCTION
#   import opw song
# INPUTS
#   $filename -
# OUTPUT
# SOURCE
#
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 .= fromutf("cp-1252", $_);
    }
    close OPW;

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

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

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

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

    $input =~ /beginregel>(.*)<\/beginregel/;
    $widgets->{'add'}->get_object('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($_);
            }
        }
    }
}

#***

#****f* lyricue/import_song_html
# NAME
#   import_song_html
# SYNOPSIS
#   import_song_html($filename)
# FUNCTION
#   import HTML/Opensong song
# INPUTS
#   $filename -
# OUTPUT
# SOURCE
#
sub import_song_html {
    my ($filename) = @_;
    debug("import HTML/Opensong song");
    my ($name, $author) = "";
    my $input   = "";
    my $page    = "";
    my $hashnum = 0;

    open(HTML, $filename) || die("Unable to open $filename");
    while (<HTML>) {
        chomp;
        if (/div id="title"/) {
            $name = $_;
            $name =~ s/^.*id=\"title\">(.*)<\/div>.*$/$1/g;
            $widgets->{'add'}->get_object('entryEditName')->set_text($name);
        } elsif (/div id="author"/) {
            $author = $_;
            $author =~ s/^.*id="author">(.*)<\/div>.*/$1/g;
            $widgets->{'add'}->get_object('entryEditArtist')->set_text($author);
        } elsif (/div class="heading"/) {

            # new page
            debug("add page");
            if ($page ne "") {
                my $iter = $widgets->{'textAPageB'}{$hashnum}->get_end_iter();
                $widgets->{'textAPageB'}{$hashnum}->insert($iter, $page);
                $page    = "";
                $hashnum = add_page();
            }
        } elsif (/td class="lyrics"/) {
            my $line = $_;
            $line =~ s/^.*class="lyrics">(.*)<\/td>.*/$1/g;
            $line =~ s/&nbsp;/ /g;
            $page .= $line;
        } elsif (/<\/table>/) {
            debug("add line");
            $page .= "
";
        }
    }
    close HTML;
    if ($page ne "") {
        my $iter = $widgets->{'textAPageB'}{$hashnum}->get_end_iter();
        $widgets->{'textAPageB'}{$hashnum}->insert($iter, $page);
        $page = "";
    }
}

####
# Install database functions
####
#***

#****f* lyricue/db_check_app
# NAME
#   db_check_app
# SYNOPSIS
#   db_check_app()
# FUNCTION
#   Checking for database servers
# INPUTS
# OUTPUT
# SOURCE
#
sub db_check_app {
    debug("Checking for database servers");
    my @ary   = DBI->available_drivers(1);
    my $mysql = FALSE;
    foreach (@ary) {
        if ($_ eq "mysql") {
            $mysql = TRUE;
        }
    }
    if (!$mysql) {
        die("No supported DB found");
    }
}

#***

#****f* lyricue/db_get_admin
# NAME
#   db_get_admin
# SYNOPSIS
#   db_get_admin()
# FUNCTION
#   Get the db admin login information
# INPUTS
# OUTPUT
# SOURCE
#
sub db_get_admin {
    debug("Get the db admin login information");

    my $dbh;
    eval {
        $dbh =
          DBI->connect("DBI:mysql:mysql:" . $globals->{'mysqlhost'},
            "root", "");
    };
    if ($dbh) {
        $globals->{'db_adminuser'}     = "root";
        $globals->{'db_adminpassword'} = "";
        return;
    }

    my $adminxml = load_window('dialogAdminLogin');
    my $confirm  = $adminxml->get_object('dialogAdminLogin')->run();
    if ($confirm == 0) {
        $globals->{'db_adminuser'} =
          $adminxml->get_object('entryAdminLogin')->get_text();
        $globals->{'db_adminpassword'} =
          $adminxml->get_object('entryAdminPass')->get_text();
    } else {
        my $confirmxml = load_window('dialogConfirm');
        $confirmxml->get_object('labelDelete')
          ->set_text(
            fromutf(gettext("Are you sure you want to exit Lyricue?")));
        $confirm = $confirmxml->get_object('dialogConfirm')->run();
        close_dialog($adminxml->get_object('dialogAdminLogin'));
        if ($confirm eq "ok") {
            debug("Exiting on request");
            exit 1;
        } else {
            db_get_admin();
            return;
        }

    }
    close_dialog($adminxml->get_object('dialogAdminLogin'));
    eval {
        $dbh = DBI->connect(
            "DBI:mysql:mysql:" . $globals->{'mysqlhost'},
            $globals->{'db_adminuser'},
            $globals->{'db_adminpassword'}
        );
    };

    if (!$dbh) {
        debug("Password incorrect - retry");
        db_get_admin();
    }
}

#***

#****f* lyricue/db_install_user
# NAME
#   db_install_user
# SYNOPSIS
#   db_install_user()
# FUNCTION
#   Install lyric database user
# INPUTS
# OUTPUT
# SOURCE
#
sub db_install_user {
    debug("Install lyric database user");
    my ($dbh);
    db_get_admin();
    eval {
        $dbh = DBI->connect(
            "DBI:mysql:mysql:" . $globals->{'mysqlhost'},
            $globals->{'db_adminuser'},
            $globals->{'db_adminpassword'}
        );
    };
    if ($@) {
        my $labelText = fromutf(

            gettext(
"Unable to login to database as admin, maybe the database is down.\nPlease re-enter your database admin login and retry"
            )
        );
        my $loginxml = load_window('dialogConfirm');
        $loginxml->get_object('dialogConfirm')
          ->set_title(fromutf(gettext("Login Error")));
        $loginxml->get_object('labelDelete')->set_text($labelText);
        my $confirm = $loginxml->get_object('dialogConfirm')->run();
        if ($confirm eq "ok") {
            close_dialog($loginxml->get_object('dialogConfirm'));
            db_install_user();
            return NULL;
        } else {
            display_fatal($errorcodes->{'lyricdbopen'}, $DBI::errstr);
        }
    }
    db_reload();
    my $sth = do_query($dbh,"select * from user where User='lyric'", FALSE);
    if ($sth->rows) {
        debug("User already setup\n");
    } else {
        debug("Creating mysql user..");
        $sth = do_query($dbh, "insert into user set Host='%',User='lyric',Password='',Select_priv='Y',Insert_priv='Y', Update_priv='Y',Delete_priv='Y',Lock_tables_priv='Y'", FALSE);
        $sth = do_query($dbh, "insert into user set Host='localhost',User='lyric',Password='',Select_priv='Y',Insert_priv='Y', Update_priv='Y',Delete_priv='Y',Lock_tables_priv='Y'", FALSE);
        $sth = do_query($dbh,"flush privileges", FALSE);
        debug("Done\n");
    }
    db_check_databases();
    return NULL;
}

#***

#****f* lyricue/db_check_databases
# NAME
#   db_check_databases
# SYNOPSIS
#   db_check_databases()
# FUNCTION
#   Update/install databases
# INPUTS
# OUTPUT
# SOURCE
#
sub db_check_databases {
    debug("Update/install databases");
    my @dbs =
      DBI->data_sources("mysql",
        {"host" => $globals->{'mysqlhost'}, "user" => "lyric"});

    foreach (@dbs) {
        $_ =~ s/^DBI:.*://g;
        $globals->{'db_available_db'}{$_} = TRUE;
    }

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

    if ($globals->{'db_available_db'}{'mediaDb'}) {
        db_updatedb_mediaDb();
    } else {
        debug("Creating mediaDb");
        db_installdb($globals->{'sharedir'} . "mysql/Create_mediaDb.sql",
            "mediaDb");
        db_reload();

        debug("Done\n");
    }
}

#***

#****f* lyricue/db_reload
# NAME
#   db_reload
# SYNOPSIS
#   db_reload()
# FUNCTION
#   Reload db
# INPUTS
# OUTPUT
# SOURCE
#
sub db_reload {
    debug("Reload db");
    if ($globals->{'db_adminuser'} eq "") {
        db_get_admin();
    }
debug("user:".$globals->{'db_adminuser'}." Pass:".$globals->{'db_adminpassword'});
    my $sqldb =
        DBI->connect("DBI:mysql:mysql:" . $globals->{'mysqlhost'}, $globals->{'db_adminuser'}, $globals->{'db_adminpassword'});
    $sqldb->func('reload','admin');
    $sqldb->disconnect();
    debug("reload done");
}

#***

#****f* lyricue/db_installdb
# NAME
#   db_installdb
# SYNOPSIS
#   db_installdb($db_file, $db_name)
# FUNCTION
#   Install db from
# INPUTS
#   $db_file -
#    $db_name -
# OUTPUT
# SOURCE
#
sub db_installdb {
    my ($db_file, $db_name) = @_;
    debug("Install db from " . $db_file);
    if ($globals->{'db_adminuser'} eq "") {
        db_get_admin();
    }
    system( "cat "
          . $db_file
          . " | mysql -f -h "
          . $globals->{'mysqlhost'} . " -u "
          . $globals->{'db_adminuser'}
          . " --password=\""
          . $globals->{'db_adminpassword'}
          . "\"");
}

#***

#****f* lyricue/db_updatedb_mediaDb
# NAME
#   db_updatedb_mediaDb
# SYNOPSIS
#   db_updatedb_mediaDb()
# FUNCTION
#   Update the mediaDb table if needed
# INPUTS
# OUTPUT
# SOURCE
#
sub db_updatedb_mediaDb {
    debug("Update the mediaDb table if needed");
    my $dbh = db_connect($globals->{'mediadb'}, "", TRUE);
    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("Adding fields to mediaDb\n");
        db_installdb($globals->{'sharedir'} . "mysql/Add_mediadb_colour.sql",
            "lyricDb");
        debug("Done\n");
    }
}

#***

#****f* lyricue/db_updatedb_lyricDb
# NAME
#   db_updatedb_lyricDb
# SYNOPSIS
#   db_updatedb_lyricDb()
# FUNCTION
#   Update the lyricDb table if needed
# INPUTS
# OUTPUT
# SOURCE
#
sub db_updatedb_lyricDb {
    debug("Update the lyricDb table if needed");
    my $dbh =
      db_connect($globals->{'lyricdb'}, $errorcodes->{'lyricdbopen'}, TRUE);
    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 (!defined $table->{'config'}) {
        debug("Configuration tables not found\n");
        debug("Upgrading database from < 2.4 to 2.4\n");
        db_installdb($globals->{'sharedir'} . "mysql/Add_config.sql",
            "lyricDb");
        debug("Done\n");
    }
    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/Add_transition.sql",
            "lyricDb");
        debug("Done\n");
    }

    # Increase data size - does nothing if already done - only works for mysql
    $fields = $dbh->selectall_arrayref("describe playlist");
    my $pl;
    foreach (@$fields) {
        $pl->{$_->[0]} = $_->[1];
    }
    if ($pl->{'data'} ne "varchar(256)") {
        db_installdb($globals->{'sharedir'} . "mysql/Expand_data.sql",
            "lyricDb");
    }
    if (!defined $pl->{'snapshot'}) {
        db_installdb($globals->{'sharedir'} . "mysql/Add_snapshot.sql",
            "lyricDb");
    }

    # Add page title
    $fields = $dbh->selectall_arrayref("describe page");
    my $page;
    foreach (@$fields) {
        $page->{$_->[0]} = $_->[1];
    }
    if (!defined $page->{'pagetitle'}) {
        db_installdb($globals->{'sharedir'} . "mysql/Add_pagetitle.sql",
            "lyricDb");
    }

    # Add status table
    if (!defined $table->{'status'}) {
        debug("Status tables not found\n");
        debug("Upgrading database from < 3.6 to 3.6\n");
        db_installdb($globals->{'sharedir'} . "mysql/Add_Profiles.sql",
            "lyricDb");
        debug("Done\n");
    }

    # Add IP column
    $fields = $dbh->selectall_arrayref("describe status");
    my $stat;
    foreach (@$fields) {
        $stat->{$_->[0]} = 1;
    }
    if (!defined $stat->{'ip'}) {
        debug("IP field not found\n");
        debug("Upgrading database to 3.6.7\n");
        db_installdb($globals->{'sharedir'} . "mysql/Add_ip.sql",
            "lyricDb");
        debug("Done\n");
    }

    # Ensure all tables are in UTF8 characterset
    my @toconvert = ();
    foreach my $tname (keys %$table) {
        my $fields = $dbh->selectall_arrayref("show create table " . $tname);
        foreach (@$fields) {
            if (!($_->[1] =~ /CHARSET=utf8/)) {
                push @toconvert, $tname;
            }
        }

    }
    if (@toconvert > 0) {
        if ($globals->{'db_adminuser'} eq "") {
            db_get_admin();
        }
        eval {
            $dbh = DBI->connect(
                "DBI:mysql:mysql:" . $globals->{'mysqlhost'},
                $globals->{'db_adminuser'},
                $globals->{'db_adminpassword'}
            );
        };
        if ($dbh) {
            foreach (@toconvert) {
                debug("Converting " . $_ . " to utf8");
                $dbh->do("ALTER TABLE lyricDb."
                      . $_
                      . " CONVERT TO CHARACTER SET utf8");
            }
        }
    }

}

#***

#****f* lyricue/check_displays
# NAME
#   check_displays
# SYNOPSIS
#   check_displays()
# FUNCTION
#
# INPUTS
# OUTPUT
# SOURCE
#
sub check_displays{
    #debug("Check Displays");

    $local_preview  = FALSE;
    foreach my $display (keys %displays) {
        $displays{$display} |= GONE;
    }
    for (keys %previews) {
        delete $previews{$_};
    }

    my $query = "SELECT host, ip, type, title FROM status WHERE TIMEDIFF(NOW(), lastupdate) < '00:00:02' AND profile='". $globals->{'profile'} . "'";
    $sth = do_query($lyricDbh, $query, TRUE);

    my $status = "0;0;0";
    while ($row = $sth->fetchrow_hashref()) {
        my @keys = split(/:/, $row->{'host'});
        my $index = $row->{'ip'}.":".$keys[1];
        if (($row->{'type'} eq "normal") || ($row->{'type'} eq "simple") || ($row->{'type'} eq "headless")) {
            if (defined $displays{$index}) {
                $displays{$index} &= TRUE;
            } else {
                $displays{$index} = TRUE;
            }
            if ($row->{'title'} =~ /^blank_bg/) {
                if (defined($widgets->{'miniview_frame'}->{$index})) {
                    $widgets->{'miniview_frame'}->{$index}->set_label($row->{'host'}."- blanked");
                }
            } elsif ($row->{'title'} =~ /^blank_text/) {
                if (defined($widgets->{'miniview_frame'}->{$index})) {
                    $widgets->{'miniview_frame'}->{$index}->set_label($row->{'host'}."- text cleared");
                }
            } else {
                if (defined($widgets->{'miniview_frame'}->{$index})) { 
                    $widgets->{'miniview_frame'}->{$index}->set_label($row->{'host'});
                }
                if ($row->{'title'} ne "0;0;0") {
                    $status=$row->{'title'};
                }
            }
        } elsif ($row->{'type'} =~ /^miniview/) {
            my $extra_data = $row->{'type'};
            $extra_data =~ s/^miniview//;
            $miniviews{$extra_data} = TRUE;
        } elsif ($row->{'type'} eq "preview") {
            $previews{$index} = TRUE;
            if ($row->{'host'} =~ /^$globals->{'local_hostname'}/) {
                $local_preview = TRUE;
            }
        }
    }

    my ($position, $duration, $state) = split(/;/, $status, 3);
    my $hscale = $widgets->{'main'}->get_object('hscaleMediaPosition');
    if (defined $globals->{'video_skip_signal'}) {
        $hscale->signal_handler_disconnect($globals->{'video_skip_signal'});
    }
    if ($duration <= 0) {
        $hscale->set_range(0, 1000);
    } else {
        $hscale->set_range(0, $duration);
    }
    $hscale->set_value($position);
    $position = sprintf("%d:%02d", $position / 60, $position % 60);
    $duration = sprintf("%d:%02d", $duration / 60, $duration % 60);
    $widgets->{'main'}->get_object('labelMediaPosition')
      ->set_text($position . "/" . $duration);
    if ($state) {
        $widgets->{'main'}->get_object('buttonMediaPlay')->hide;
        $widgets->{'main'}->get_object('buttonMediaPause')->show_all;
    } else {
        $widgets->{'main'}->get_object('buttonMediaPlay')->show_all;
        $widgets->{'main'}->get_object('buttonMediaPause')->hide;
    }
    do_pending();
    $globals->{'video_skip_signal'} =
      $hscale->signal_connect('value-changed', "video_skip");


    init_miniview();
    if ($config->{'DynamicPreview'} && !$local_preview) {
        $globals->{'preview_starting'}--;
        if ($globals->{'preview_starting'}<1) {
            debug("preview gone");
            $globals->{'preview_starting'}=0;
            if (defined $widgets->{'preview'}) {
                $widgets->{'main'}->get_object('framePreview')
                  ->remove($widgets->{'preview'});
                undef $widgets->{'preview'};
            }
            init_preview();
        }
    }
    return TRUE;
}

#****f* lyricue/check_status
# NAME
#   check_status
# SYNOPSIS
#   check_status()
# FUNCTION
#
# INPUTS
# OUTPUT
# SOURCE
#
sub check_status {

    #debug("Check status");

    # Check for playlist items of simple/normal displays
    my $query =
"SELECT host,p1.playorder AS pl1,p2.playorder AS pl2 FROM playlist AS p1 LEFT JOIN playlist AS p2 ON p1.data=p2.playlist LEFT JOIN status AS st ON st.ref=p2.playorder WHERE (st.type='normal' OR st.type='simple' or st.type='headless') AND st.profile='"
      . $globals->{'profile'} . "'";

    my $sth = do_query($lyricDbh, $query, TRUE);
    undef $globals->{'current_items'};
    while (my $row = $sth->fetchrow_hashref()) {
        $globals->{'current_items'}->{$row->{'pl1'}} = $row->{'host'};
        $globals->{'current_items'}->{$row->{'pl2'}} = $row->{'host'};
    }

    my $model = $widgets->{'main'}->get_object('treePlaylist')->get_model();
    if ($model) {
        $model->foreach(\&update_current);
    }

    check_displays();
    return TRUE;
}

#***

#****f* lyricue/video_skip
# NAME
#   video_skip
# SYNOPSIS
#   video_skip()
# FUNCTION
#   Tell server to skip to position
# INPUTS
# OUTPUT
# SOURCE
#
sub video_skip {
    my ($hscale) = @_;
    debug("Skipping to " . $hscale->get_value());
    update_display("media", "skip", $hscale->get_value());
}

#****f* lyricue/update_current
# NAME
#   update_current
# SYNOPSIS
#   update_current($store, $path, $iter)
# FUNCTION
#
# INPUTS
#   $store -
#    $path -
#    $iter -
# OUTPUT
# SOURCE
#
sub update_current {
    my ($store, $path, $iter) = @_;
    my $playlistid = $store->get($iter, 2);
    if (defined $globals->{'current_items'}->{$playlistid}) {
        $store->set($iter, 1, $config->{'HighlightColour'});
    } else {
        $store->set($iter, 1, undef);
    }
    return FALSE;
}

#***

#****f* lyricue/clear_search
# NAME
#   clear_search
# SYNOPSIS
#   clear_search()
# FUNCTION
#   Clear search entry
# INPUTS
# OUTPUT
# SOURCE
#
sub clear_search {
    debug("Clear search entry");
    $widgets->{'main'}->get_object('entrySearch')->set_text("");
    $widgets->{'main'}->get_object('entrySearch')->grab_focus();
}

#***

#****f* lyricue/db_connect
# NAME
#   db_connect
# SYNOPSIS
#   db_connect($dbname, $dberror, $fatal)
# FUNCTION
#
# INPUTS
#   $dbname -
#    $dberror -
# OUTPUT
# SOURCE
#
sub db_connect {
    my ($dbname, $dberror, $fatal) = @_;
    my ($dbh);
    $dbh =
      DBI->connect("DBI:mysql:database=$dbname;host=$globals->{'mysqlhost'}",
        "lyric", "", {mysql_enable_utf8 => 1, mysql_auto_reconnect => 1})
      || display_error($dberror, $DBI::errstr, $fatal);
    if ($dbh != NULL) {
        $dbh->do('SET NAMES utf8');
    }
    return $dbh;
}

#***

#****f* lyricue/db_restart
# NAME
#   db_restart
# SYNOPSIS
#   db_restart()
# FUNCTION
#   Reconnecting to DBs
# INPUTS
# OUTPUT
# SOURCE
#
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();
}

#***

#****f* lyricue/db_select
# NAME
#   db_select
# SYNOPSIS
#   db_select()
# FUNCTION
#
# INPUTS
# OUTPUT
# SOURCE
#
sub db_select {

    # Open lyricDB, bibleDB and mediaDb
    my $sqldb =
      DBI->connect("DBI:mysql:mysql:" . $globals->{'mysqlhost'}, "lyric", "")
      || db_install_user();
    if (defined $sqldb && ($sqldb!=NULL)) {
        $sqldb->disconnect();
    }
    db_check_databases();
    $lyricDbh =
      db_connect($globals->{'lyricdb'}, $errorcodes->{'lyricdbopen'}, TRUE);
    $mediaDbh =
      db_connect($globals->{'mediadb'}, $errorcodes->{'mediadbopen'}, TRUE);
    use Net::Rendezvous::Publish;
    my $publisher = Net::Rendezvous::Publish->new
          or die "couldn't make a Responder object";
    my $dbhost = $globals->{'mysqlhost'};
    if ($dbhost eq "localhost") {
        $dbhost = hostname();
    }
    my $service = $publisher->publish(
            name => "Lyricue Interface",
            type => '_lyricue._tcp',
            port => 0,
            txt => "database=".$dbhost
    );

    $config->{'Bibles'} = get_bibles();
}

#***

#****f* lyricue/add_to_playlist_search
# NAME
#   add_to_playlist_search
# SYNOPSIS
#   add_to_playlist_search($widget)
# FUNCTION
#   Songid \"" . $songid . "\" selected
# INPUTS
#   $widget -
# OUTPUT
# SOURCE
#
sub add_to_playlist_search {
    my ($widget) = @_;
    my $selection =
      $widgets->{'search'}->get_object('treeSearch')->get_selection;
    my ($model, $iter) = $selection->get_selected;
    if ($iter) {
        my $songid = $model->get($iter, 4);
        debug("Songid \"" . $songid . "\" selected");
        add_single_song($songid);
        update_playlist();
    }
}

#***

#****f* lyricue/on_hboxBackImage_drag_data_received
# NAME
#   on_hboxBackImage_drag_data_received
# SYNOPSIS
#   on_hboxBackImage_drag_data_received($widget, $context, $x, $y, $data, $info, $time)
# FUNCTION
#   Dropped on backgrounds
# INPUTS
#   $widget -
#    $context -
#    $x -
#    $y -
#    $data -
#    $info -
#    $time -
# OUTPUT
# SOURCE
#
sub on_hboxBackImage_drag_data_received {
    debug("Dropped on backgrounds");
    my ($widget, $context, $x, $y, $data, $info, $time) = @_;
    my $category = $globals->{'category'};

    if (($category ne "") && ($data->length >= 0) && ($data->format == 8)) {
        debug("Recieved " . $data->data);
        if ($data->data) {
            my @uris = split(/\n/, $data->data);
            my ($filename, $format, $description);
            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]
            );
            for my $uri (@uris) {
                $filename = URI->new($uri);
                debug("Category: $category, Filename: " . $filename->file);

                $format = $filename->file;
                $format =~ s/^.*\.//g;
                $description = $filename->file;
                $description =~ s/^.*\///g;
                $description =~ s/\..*?$//g;

                open(MEDIA, $filename->file);
                my $filedata = "";

                while (<MEDIA>) {
                    $filedata .= $_;
                }
                close MEDIA;
                debug("Length: " . length($filedata));
                my $sth = $mediaDbh->prepare(
q{INSERT INTO media(category, subcategory, type, format, insertedby, insertdate, description, data) VALUES (?,?,?,?,?,?,?,?)}
                );
                my $rv =
                  $sth->execute($category, "", "bg", $format, $owner, $time,
                    $description, $filedata);
            }
            bgdir_change($widget, $category);
        }

        $context->finish(1, 0, $time);
        return;
    } else {
        debug("global category=$category");
    }
}

#***

#****f* lyricue/alphanum
# NAME
#   alphanum
# SYNOPSIS
#   alphanum()
# FUNCTION
#
# INPUTS
# OUTPUT
# SOURCE
#
sub alphanum {

    # $a and $b are automagically passed to alphanum.
    # A copy of them has to be made, or else we will edit the global $a and $b
    my $a_copy = $a;
    my $b_copy = $b;
    my $n      = 0;
    while ($n == 0) {

        # Get next "chunk"
        # (A chunk is either a group of letters or a group of numbers)

        my ($a_chunk) = $a_copy =~ /([\D]+|[\d]+)/;
        $a_copy =
          substr($a_copy, length($a_chunk), length($a_copy) - length($a_chunk));

        my ($b_chunk) = $b_copy =~ /([\D]+|[\d]+)/;
        $b_copy =
          substr($b_copy, length($b_chunk), length($b_copy) - length($b_chunk));

        # Compare the chunks

        # Case 1: They both contain letters
        if (($a_chunk =~ /\D+/) && ($b_chunk =~ /\D+/)) {
            $n = $a_chunk cmp $b_chunk;
        }

        # Case 2: They both contain numbers
        else {
            if (($a_chunk =~ /\d+/) && ($b_chunk =~ /\d+/)) {
                $n = $a_chunk <=> $b_chunk;
            }

            # Case 3: One has letters, one has numbers; or one is empty
            else {
                $n = $a_chunk cmp $b_chunk;

             # If these are equal, make one (which one is arbitrary) come before
             # the other   (or else we'll be stuck in this "while $n==0" loop)
                if ($n == 0) { $n = 1 }
            }
        }
    }
    return $n;
}

#***

#****f* lyricue/fromutf
# NAME
#   fromutf
# SYNOPSIS
#   fromutf($line)
# FUNCTION
#
# INPUTS
#   $line -
# OUTPUT
# SOURCE
#
sub fromutf {
    my ($line) = @_;
    utf8::decode($line) unless utf8::is_utf8($line);
    return $line;
}

#***

#****f* lyricue/toutf
# NAME
#   toutf
# SYNOPSIS
#   toutf($line)
# FUNCTION
#
# INPUTS
#   $line -
# OUTPUT
# SOURCE
#
sub toutf {
    my ($line) = @_;
    return Encode::encode("utf8", $line);
}

#***

#****f* lyricue/pause_media
# NAME
#   pause_media
# SYNOPSIS
#   pause_media()
# FUNCTION
#   Play/Pause media
# INPUTS
# OUTPUT
# SOURCE
#
sub pause_media {
    debug("Play/Pause media");
    update_display("media", "pause", "");
}

#***

#****f* lyricue/media_v4l
# NAME
#   media_v4l
# SYNOPSIS
#   media_v4l()
# FUNCTION
#   Starting Live Video
# INPUTS
# OUTPUT
# SOURCE
#
sub media_v4l {
    debug("Starting Live Video");
    update_display("backdrop", "uri;v4l2#SEMI#//");
}

#***

#****f* lyricue/media_dvd
# NAME
#   media_dvd
# SYNOPSIS
#   media_dvd()
# FUNCTION
#   Starting DVD Video
# INPUTS
# OUTPUT
# SOURCE
#
sub media_dvd {
    debug("Checking DVD");
    $widgets->{'dialogDvd'} = load_window('dialogDvd');
    my $vbox = $widgets->{'dialogDvd'}->get_object('vboxDvd');
    my $com  = `lsdvd -Op`;

    #my $com = `./lsdvd-test.sh -Op`;
    $com =~ s/^our //g;
    eval $com;

    my $tracks        = $lsdvd{'track'};
    my $longest_track = $lsdvd{'longest_track'};
    if (!defined $longest_track) {
        display_message(
            fromutf(gettext("No DVD titles found")),
            fromutf(
                gettext(
"Unable to open the dvd.  There may be no media in the drive"
                )
            )
        );
        return;
    }
    my $longest =
      Gtk2::RadioButton->new(undef, "Longest title (" . $longest_track . ")");
    $longest->{user_data} = $longest_track;
    $vbox->add($longest);
    $longest->show();
    foreach (@$tracks) {
        my $radio =
          Gtk2::RadioButton->new($longest,
            "Title " . $_->{"ix"} . " - " . sec_to_time($_->{"length"}));
        debug("Title " . $_->{"ix"} . " - " . sec_to_time($_->{"length"}));
        $radio->{user_data} = $_->{"ix"};
        $vbox->add($radio);
        $radio->show();
        if ($_->{"ix"} == $longest_track) {
            select_dvdtitle($longest, $_->{"length"});
        }
        $radio->signal_connect(toggled => \&select_dvdtitle, $_->{"length"});

    }
    $longest->set_active(TRUE);
    $widgets->{'dialogDvd'}->get_object('comboDvdEnd')->set_active(0);
    my $confirm = $widgets->{'dialogDvd'}->get_object('dialogDvd')->run();
    if ($confirm == 0) {
        my $selected = 0;
        my $group    = $longest->get_group;
        my $length   = 0;
        foreach my $r (@$group) {
            if ($r->get_active) {
                $selected = $r->{user_data};
            }
        }
        debug("Adding DVD title:" . $selected);
        my $type =
          $widgets->{'dialogDvd'}->get_object('comboDvdEnd')->get_active;
        my $start = time_to_sec(
            $widgets->{'dialogDvd'}->get_object('entryDvdStart')->get_text);
        my $end = time_to_sec(
            $widgets->{'dialogDvd'}->get_object('entryDvdEnd')->get_text);
        my $total =
          $widgets->{'dialogDvd'}->get_object('entryDvdEnd')->{user_data};
        if ($type == 1) {
            $end = $start + $end;
        }
        if (($start eq 0) && (($end - 1) < $total) && (($end + 1) > $total)) {
            do_add_file("dvd://" . $selected,
                $widgets->{'main'}->get_object('labelCurrentPlaylist')
                  ->{user_data});
        } else {
            do_add_file(
                "dvd://"
                  . $selected . " "
                  . sec_to_time($start) . "-"
                  . sec_to_time($end),
                $widgets->{'main'}->get_object('labelCurrentPlaylist')
                  ->{user_data}
            );
        }
        update_playlist();
    }
    close_dialog($widgets->{'dialogDvd'}->get_object('dialogDvd'));
}

#***

#****f* lyricue/select_dvdtitle
# NAME
#   select_dvdtitle
# SYNOPSIS
#   select_dvdtitle($widget, $length)
# FUNCTION
#   Fill in the start/end times for dvd media
# INPUTS
# OUTPUT
# SOURCE
#

sub select_dvdtitle {
    my ($widget, $length) = @_;
    if ($widget->get_active) {
        $widgets->{'dialogDvd'}->get_object('comboDvdEnd')->set_active(0);
        $widgets->{'dialogDvd'}->get_object('entryDvdStart')
          ->set_text(sec_to_time(0));
        $widgets->{'dialogDvd'}->get_object('entryDvdEnd')
          ->set_text(sec_to_time($length));
        $widgets->{'dialogDvd'}->get_object('entryDvdEnd')->{user_data} =
          $length;
    }
}

#***

#****f* lyricue/sec_to_time
# NAME
#   sec_to_time
# SYNOPSIS
#   sec_to_time($seconds)
# FUNCTION
#   Convert seconds to pretty 0:00:00 format
# INPUTS
# OUTPUT
# SOURCE
#

sub sec_to_time {
    my ($seconds) = @_;
    if (!defined $seconds) {
        $seconds = 0;
    }
    my @parts = gmtime($seconds);
    if ($parts[2] == 0) {
        return sprintf("%d:%02d", @parts[1, 0]);
    } else {
        return sprintf("%d:%02d:%02d", @parts[2, 1, 0]);
    }
}

#***

#****f* lyricue/time_to_sec
# NAME
#   time_to_sec
# SYNOPSIS
#   time_to_sec($time)
# FUNCTION
#   Convert pretty 0:00:00 format to seconds
# INPUTS
# OUTPUT
# SOURCE
#

sub time_to_sec {
    my ($time) = @_;
    if (!defined $time) {
        return 0;
    }
    my @parts = split(/:/, $time);
    if (defined $parts[2]) {
        return ($parts[0] * 3600) + ($parts[1] * 60) + $parts[2];
    } elsif (defined $parts[1]) {
        return ($parts[0] * 60) + $parts[1];
    } else {
        return $parts[0];
    }
}

#***

#****f* lyricue/install_bibles
# NAME
#   install_bibles
# SYNOPSIS
#   install_bibles()
# FUNCTION
#   Loading bible install window
# INPUTS
# OUTPUT
# SOURCE
#
sub install_bibles {
    debug("Loading bible install window");
    $widgets->{'bible'} = load_window("windowBibleManager");
    $widgets->{'bible'}->get_object('buttonBibleSword')->{'user_data'} =
      "http://crosswire.org/sword/modules/ModDisp.jsp?modType=Bibles";
    $widgets->{'bible'}->get_object('buttonBibleDB')->{'user_data'} =
      "http://www.lyricue.org/bibles";
    $widgets->{'bible'}->get_object('windowBibleManager')->show_all();
}

#***

#****f* lyricue/do_install_bible
# NAME
#   do_install_bible
# SYNOPSIS
#   do_install_bible()
# FUNCTION
#   No file selected
# INPUTS
# OUTPUT
# SOURCE
#
sub do_install_bible {
    my $dbfilename =
      $widgets->{'bible'}->get_object('fileBibleInstall')->get_filename();
    close_dialog($widgets->{'bible'}->get_object('windowBibleManager'));
    if (!defined $dbfilename) {
        debug("No file selected");
        return;
    }
    debug("Installing bible from " . $dbfilename);

    my $message = "";

    my $tmpdir = tempdir("lyricue-XXXX", TMPDIR => 1, CLEANUP => 1);
    if ($dbfilename =~ /sql.gz$/i) {

        # Compressed bibleDb
        my $command =
          "gzip -dc \"" . $dbfilename . "\" > " . $tmpdir . "/bibleDb.sql";
        debug($command);
        system($command);
        $dbfilename = $tmpdir . "/bibleDb.sql";
    }
    if ($dbfilename =~ /sql$/i) {

        # Uncompressed bibleDb
        open(DB, $dbfilename);
        debug("bibleDb: " . $dbfilename);
        my $dbname = "bibleDb";
        while (<DB>) {
            if (/^CREATE DATABASE.*;$/) {
                $dbname = $_;
                $dbname =~ s/^CREATE DATABASE (.*);$/$1/g;
                last;
            }
        }
        close DB;
        if ($dbname ne "") {
            db_installdb($dbfilename, $dbname);
            $message = gettext("Bible installed from ") . $dbfilename;
        } else {
            $message = gettext("Unable to load from ") . $dbfilename;
        }
    } elsif ($dbfilename =~ /zip$/i) {

        # Sword bible
        my $command = "unzip \"" . $dbfilename . "\" -d \$HOME/.sword/";
        debug($command);
        system($command);
        $message = "Sword bible extracted to ~/.sword";
    }

    my $donexml = load_window('dialogError');
    $donexml->get_object('labelError')->set_text($message);
    $donexml->get_object('expanderDetails')->hide();
    my $confirm = $donexml->get_object('dialogError')->run();
    close_dialog($donexml->get_object('dialogError'));

    load_biblemenu();
}

#***

#****f* lyricue/load_link
# NAME
#   load_link
# SYNOPSIS
#   load_link($widget)
# FUNCTION
#   Load URL:
# INPUTS
#   $widget -
# OUTPUT
# SOURCE
#
sub load_link {
    my ($widget) = @_;
    debug("Load URL:" . $widget->{'user_data'});
    my $command = "xdg-open " . $widget->{'user_data'};
    system($command);
}

#***

#****f* lyricue/load_biblemenu
# NAME
#   load_biblemenu
# SYNOPSIS
#   load_biblemenu()
# FUNCTION
#   Populate biblemenu
# INPUTS
# OUTPUT
# SOURCE
#
sub load_biblemenu {
    debug("Populate biblemenu");
    my $menutop = Gtk2::Menu->new();
    my $group   = -1;

    my $bibleMenuAdd =
      Gtk2::MenuItem->new_with_label(gettext("Install new bibles"));
    $bibleMenuAdd->signal_connect("activate", "install_bibles");
    $bibleMenuAdd->show();
    $menutop->append($bibleMenuAdd);

    db_check_databases();
    $config->{'Bibles'} = get_bibles();
    my $bibles = $config->{'Bibles'};
    my ($defbible, undef) = split(/\:/, $config->{'DefBible'}, 2);
    my @swordBibles = ();
    my @dbBibles    = ();
    foreach (sort keys %$bibles) {
        if (/@/) {
            push @dbBibles, $_;
        } else {
            push @swordBibles, $_;
        }
    }

    foreach (@dbBibles, "", @swordBibles) {
        if ($_ eq "") {
            my $sep = Gtk2::SeparatorMenuItem->new;
            $sep->show;
            $menutop->append($sep);
        } else {
            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_object('bible1')->set_submenu($menutop);
}

#***

#****f* lyricue/open_dirchooser
# NAME
#   open_dirchooser
# SYNOPSIS
#   open_dirchooser($widget)
# FUNCTION
#   Opening Directory chooser
# INPUTS
#   $widget -
# OUTPUT
# SOURCE
#
sub open_dirchooser {
    my ($widget) = @_;
    debug("Opening Directory chooser");
    my $directoryxml = load_window('dialogDirChooser');
    my $dir          = $widget->get_label();
    $dir =~ s/^~/$ENV{'HOME'}/;
    $directoryxml->get_object('dialogDirChooser')->set_filename($dir);
    my $confirm = $directoryxml->get_object('dialogDirChooser')->run();

    if ($confirm eq "ok") {
        $dir = $directoryxml->get_object('dialogDirChooser')->get_filename();
        $dir =~ s/^$ENV{'HOME'}/~/;
        if ($widget->{'user_data'} eq "img") {
            $widgets->{'prefs'}->get_object('filePrefSpecialImagedir')
              ->set_label($dir);
        } elsif ($widget->{'user_data'} eq "bg") {
            $widgets->{'prefs'}->get_object('filePrefSpecialBGdir')
              ->set_label($dir);
        } elsif ($widget->{'user_data'} eq "fr_img") {
            $widgets->{'firstrunxml'}->get_object('fileFRImagedir')
              ->set_label($dir);
        } elsif ($widget->{'user_data'} eq "fr_bg") {
            $widgets->{'firstrunxml'}->get_object('fileFRBGdir')
              ->set_label($dir);
        }
    }
    close_dialog($directoryxml->get_object('dialogDirChooser'));
}

#***

#****f* lyricue/import_db
# NAME
#   import_db
# SYNOPSIS
#   import_db()
# FUNCTION
#   Import DB selected
# INPUTS
# OUTPUT
# SOURCE
#
sub import_db {
    debug("Import DB selected");
    my $importxml = load_window('dialogImportData');
    my $filter    = Gtk2::FileFilter->new();
    $filter->add_pattern("*.xmlz");
    $filter->add_pattern("*.xml");
    $importxml->get_object('fileImportData')->set_filter($filter);
    my $confirm = $importxml->get_object('dialogImportData')->run();
    debug(":" . $confirm);

    if ($confirm == 0) {
        my $filename = $importxml->get_object('fileImportData')->get_filename();
        if (defined $filename) {
            close_dialog($importxml->get_object('dialogImportData'));
            import_songs($filename);
            return;
        }
    }
    close_dialog($importxml->get_object('dialogImportData'));
}

#***

#****f* lyricue/export_db
# NAME
#   export_db
# SYNOPSIS
#   export_db()
# FUNCTION
#   Export DB selected
# INPUTS
# OUTPUT
# SOURCE
#
sub export_db {
    debug("Export DB selected");
    my $exportxml = load_window('dialogFileChooser');
    my $filter    = Gtk2::FileFilter->new;
    $filter->add_pattern("*.xmlz");
    $exportxml->get_object('dialogFileChooser')->set_filter($filter);
    $exportxml->get_object('dialogFileChooser')->set_filename("lyricue.xmlz");
    $exportxml->get_object('dialogFileChooser')
      ->set_title(gettext("Enter filename for export"));
    my $confirm = $exportxml->get_object('dialogFileChooser')->run();

    if ($confirm) {
        my $filename =
          $exportxml->get_object('dialogFileChooser')->get_filename;

        $filename =~ s/\..*?$//g;
        $filename .= ".xmlz";
        if (defined $filename) {
            export_songs($filename);
        }
        close_dialog($exportxml->get_object('dialogFileChooser'));
    }
}

#***

#****f* lyricue/biblebrowser_init
# NAME
#   biblebrowser_init
# SYNOPSIS
#   biblebrowser_init()
# FUNCTION
#
# INPUTS
# OUTPUT
# SOURCE
#
sub biblebrowser_init {
    my @items = ('Old Testament', 'New Testament');
    biblebrowser_loaditems("book", \@items, 2);
}

#***

#****f* lyricue/biblebrowser_book
# NAME
#   biblebrowser_book
# SYNOPSIS
#   biblebrowser_book()
# FUNCTION
#   Looking up
# INPUTS
# OUTPUT
# SOURCE
#
sub biblebrowser_book {
    my ($widget, $section) = @_;
    debug("Looking up " . $section);
    my @items = ();
    if ($section eq "Old Testament") {
        @items = (
            'Genesis',      'Exodus',        'Leviticus', 'Numbers',
            'Deuteronomy',  'Joshua',        'Judges',    'Ruth',
            '1 Samuel',     '2 Samuel',      '1 Kings',   '2 Kings',
            '1 Chronicles', '2 Chronicles',  'Ezra',      'Nehemiah',
            'Esther',       'Job',           'Psalms',    'Proverbs',
            'Ecclesiastes', 'Song of Songs', 'Isaiah',    'Jeremiah',
            'Lamentations', 'Ezekiel',       'Daniel',    'Hosea',
            'Joel',         'Amos',          'Obadiah',   'Jonah',
            'Micah',        'Nahum',         'Habakkuk',  'Zephaniah',
            'Haggai',       'Zechariah',     'Malachi'
        );
    } else {
        @items = (
            'Matthew',         'Mark',
            'Luke',            'John',
            'Acts',            'Romans',
            '1 Corinthians',   '2 Corinthians',
            'Galatians',       'Ephesians',
            'Philippians',     'Colossians',
            '1 Thessalonians', '2 Thessalonians',
            '1 Timothy',       '2 Timothy',
            'Titus',           'Philemon',
            'Hebrews',         'James',
            '1 Peter',         '2 Peter',
            '1 John',          '2 John',
            '3 John',          'Jude',
            'Revelation'
        );
    }
    biblebrowser_loaditems("chapter", \@items, 3);
}

#***

#****f* lyricue/biblebrowser_chapter
# NAME
#   biblebrowser_chapter
# SYNOPSIS
#   biblebrowser_chapter($widget, $book, $source)
# FUNCTION
#   Looking up book
# INPUTS
#   $widget -
#    $book -
#    $source -
# OUTPUT
# SOURCE
#
sub biblebrowser_chapter {
    my ($widget, $book, $source) = @_;
    debug("Looking up book " . $book);
    my $translated_book = gettext($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 '%s' 1:1",
            $globals->{'diatheke'},
            $globals->{'bibledb'}, $book, $translated_book
        );
        $maxchap = fromutf(`$command`);
        ($book, undef) = split(/\s\d/, $maxchap, 2);

        $command = sprintf(
            "%s -b %s -e UTF8 -k '%s' | grep '^%s'| tail -1",
            $globals->{'diatheke'},
            $globals->{'bibledb'}, $book, $book
        );
        $maxchap = fromutf(`$command`);
        $maxchap =~ s/^$book ([0-9]*):[0-9].*$/$1/g;
        if ($maxchap) {
            $cont = TRUE;
        }
    } else {
        my ($table, $dbname) = split(/@/, $globals->{'bibledb'}, 2);
        my $query =
            "SELECT MAX(chapternum) FROM "
          . $table
          . " WHERE book like \""
          . toutf($book)
          . "%\" or book like \""
          . toutf($translated_book) . "%\"";
        $sth = do_query($bibleDbh, $query, FALSE);
        if (my @row = $sth->fetchrow_array) {
            $cont    = TRUE;
            $maxchap = $row[0];
        }
        $sth->finish;
    }

    if ($cont) {
        my @items = ();
        foreach (1 .. $maxchap) {
            push @items, $_;
        }
        biblebrowser_loaditems("verse", \@items, 8);
        if ($source ne "entry") {
            $widgets->{'main'}->get_object('entryNavVerseText')
              ->set_text($book . " ");
            $widgets->{'main'}->get_object('entryNavVerseText')
              ->set_position(-1);
        }
    }
}

#***

#****f* lyricue/biblebrowser_verse
# NAME
#   biblebrowser_verse
# SYNOPSIS
#   biblebrowser_verse($widget, $chapter, $source)
# FUNCTION
#   biblebrowser_verse called from ".$source.":
# INPUTS
#   $widget -
#    $chapter -
#    $source -
# OUTPUT
# SOURCE
#
sub biblebrowser_verse {
    my ($widget, $chapter, $source) = @_;
    debug("biblebrowser_verse called from " . $source . ":" . $chapter);
    my $book =
      $widgets->{'main'}->get_object('entryNavVerse')->get_active_text();
    $book =~ s/ *$//g;
    if ($chapter == 0) {
        $chapter = $book;
        $chapter =~ s/^.*( [0-9:\-]*)/$1/;
        my $verse = "";
        ($chapter, $verse) = split(/:/, $chapter);
        $book =~ s/ [0-9:\-]*$//;
        if ((!defined $verse) || ((defined $verse) && ($verse eq ""))) {
            $globals->{'verseStart'} = 0;
            $globals->{'verseEnd'}   = 0;
        }
    }
    if ($source ne "entry") {
        $widgets->{'main'}->get_object('entryNavVerseText')
          ->set_text($book . " " . $chapter);
        $widgets->{'main'}->get_object('entryNavVerseText')->set_position(-1);
    }
    debug("Looking up book " . $book . " chapter " . $chapter);
    my $maxverse = 0;
    if ($globals->{'usesword'}) {
        my $command = sprintf(
            "%s -b %s -e UTF8 -k '%s' %d | grep '^%s' | tail -1",
            $globals->{'diatheke'},
            $globals->{'bibledb'}, $book, $chapter, $book
        );
        $maxverse = fromutf(`$command`);
        $maxverse =~ s/^$book [0-9]*:([0-9]*):.*$/$1/g;
    } else {
        my ($table, $dbname) = split(/@/, $globals->{'bibledb'}, 2);
        my $query =
            "SELECT MAX(versenum) FROM "
          . $table
          . " WHERE book=\""
          . toutf($book)
          . "\" AND chapternum=\""
          . $chapter . "\"";
        $sth = do_query($bibleDbh, $query, FALSE);

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

    if ($maxverse) {
        my @items = ();
        foreach (1 .. $maxverse) {
            push @items, $_;
        }
        biblebrowser_loaditems("verses", \@items, 8);
        if ($source ne "entry") {
            $widgets->{'main'}->get_object('entryNavVerseText')
              ->set_text($book . " " . $chapter);
            $widgets->{'main'}->get_object('entryNavVerseText')
              ->set_position(-1);
        }
    }
}

#***

#****f* lyricue/biblebrowser_loaditems
# NAME
#   biblebrowser_loaditems
# SYNOPSIS
#   biblebrowser_loaditems($section, $items, $cols)
# FUNCTION
#   Filling bible section
# INPUTS
#   $section -
#    $items -
#    $cols -
# OUTPUT
# SOURCE
#
sub biblebrowser_loaditems {
    my ($section, $items, $cols) = @_;
    debug("Filling bible section");
    my $buttons = $widgets->{'bibleBrowser'};
    foreach (keys %$buttons) {
        $widgets->{'bibleBrowser'}{$_}->destroy();
        delete $widgets->{'bibleBrowser'}{$_};
    }

    my $size   = @$items;
    my $buffer = Gtk2::TextBuffer->new();
    $widgets->{'main'}->get_object('textBible')->set_buffer($buffer);
    my $sizegroup = Gtk2::SizeGroup->new('both');
    foreach my $itemnum (0 .. ($size - 1)) {
        if ($section eq "verses") {
            $widgets->{'bibleBrowser'}{'button' . $itemnum} =
              Gtk2::ToggleButton->new(@$items[$itemnum]);
            $widgets->{'bibleBrowser'}{'button' . $itemnum}
              ->signal_connect("event", "changeVerseStatus", $size);
            $widgets->{'bibleBrowser'}{'button' . $itemnum}->show();
        } else {
            $widgets->{'bibleBrowser'}{'button' . $itemnum} =
              Gtk2::Button->new(fromutf(gettext(@$items[$itemnum])));
            $widgets->{'bibleBrowser'}{'button' . $itemnum}
              ->signal_connect("clicked", "biblebrowser_" . $section,
                @$items[$itemnum]);
        }
        $sizegroup->add_widget($widgets->{'bibleBrowser'}{'button' . $itemnum});
        $widgets->{'bibleBrowser'}{'button' . $itemnum}->show;
        my $anchor = $buffer->create_child_anchor($buffer->get_end_iter);
        $widgets->{'main'}->get_object('textBible')
          ->add_child_at_anchor($widgets->{'bibleBrowser'}{'button' . $itemnum},
            $anchor);
    }
    $widgets->{'main'}->get_object('textBible')->show_all();
}

#***

#****f* lyricue/firstrun_wizard
# NAME
#   firstrun_wizard
# SYNOPSIS
#   firstrun_wizard()
# FUNCTION
#
# INPUTS
# OUTPUT
# SOURCE
#
sub firstrun_wizard {
    debug("First Run Wizard");
    $config                   = load_config('');
    $globals->{'firstrun'}    = TRUE;
    $widgets->{'firstrunxml'} = load_window('assistantFirstRun');
    $widgets->{'firstrun'} =
      $widgets->{'firstrunxml'}->get_object('assistantFirstRun');
    $widgets->{'firstrun'}->show_all();
}

#***

#****f* lyricue/firstrun_cancel
# NAME
#   firstrun_cancel
# SYNOPSIS
#   firstrun_cancel()
# FUNCTION
#   Cancelled Wizard
# INPUTS
# OUTPUT
# SOURCE
#
sub firstrun_cancel {
    debug("Cancelled Wizard");
    firstrun_close();
}

#***

#****f* lyricue/firstrun_close
# NAME
#   firstrun_close
# SYNOPSIS
#   firstrun_close()
# FUNCTION
#   Closing Wizard
# INPUTS
# OUTPUT
# SOURCE
#
sub firstrun_close {
    debug("Closing Wizard");
    close_dialog($widgets->{'firstrun'});
    if ($globals->{'firstrun'}) {
        Gtk2->main_quit();
    }
}

#***

#****f* lyricue/firstrun_prepare
# NAME
#   firstrun_prepare
# SYNOPSIS
#   firstrun_prepare()
# FUNCTION
#   prepare page:
# INPUTS
# OUTPUT
# SOURCE
#
sub firstrun_prepare {
    debug("Firstrun prepare page:" . $widgets->{'firstrun'}->get_current_page);
    my $page =
      $widgets->{'firstrun'}
      ->get_nth_page($widgets->{'firstrun'}->get_current_page);
    my $font   = "Serif 30";
    my $header = gettext("First-run wizard");

    if ($widgets->{'firstrun'}->get_current_page == 0) {

        # Intro
        $header = gettext("Welcome");
        $widgets->{'firstrun'}->set_page_complete($page, TRUE);
    } elsif ($widgets->{'firstrun'}->get_current_page == 1) {
        $header = gettext("Location");
        $widgets->{'firstrunxml'}->get_object('entryFRHostDB')
          ->set_text($globals->{'mysqlhost'});
        $widgets->{'firstrun'}->set_page_complete($page, TRUE);
    } elsif ($widgets->{'firstrun'}->get_current_page == 2) {
        $globals->{'mysqlhost'} =
          $widgets->{'firstrunxml'}->get_object('entryFRHostDB')->get_text();

        # Database
        $header = gettext("Database login");
        if (defined $lyricDbh) {
            $widgets->{'firstrun'}->set_page_complete($page, TRUE);
            $widgets->{'firstrun'}->set_current_page ($widgets->{'firstrun'}->get_current_page+1);      
        } else {
            firstrun_pw_check();
        }
    } elsif ($widgets->{'firstrun'}->get_current_page == 3) {

        # Server
        $header = gettext("Projector settings");
        $widgets->{'firstrunxml'}->get_object('spinFRWidth')
          ->set_value($config->{'Width'});
        $widgets->{'firstrunxml'}->get_object('spinFRHeight')
          ->set_value($config->{'Height'});
        if ($config->{'VerticalLocation'}) {
            $widgets->{'firstrunxml'}->get_object('comboFRVertical')
              ->prepend_text($config->{'VerticalLocation'});
        }
        $widgets->{'firstrunxml'}->get_object('comboFRVertical')->set_active(0);
        if ($config->{'HorizontalLocation'}) {
            $widgets->{'firstrunxml'}->get_object('comboFRHorizontal')
              ->prepend_text($config->{'HorizontalLocation'});
        }
        $widgets->{'firstrunxml'}->get_object('comboFRHorizontal')
          ->set_active(0);
        if ($config->{'Justification'}) {
            $widgets->{'firstrunxml'}->get_object('comboFRJustification')
              ->prepend_text($config->{'Justification'});
        }
        $widgets->{'firstrunxml'}->get_object('comboFRJustification')
          ->set_active(0);
        $widgets->{'firstrun'}->set_page_complete($page, TRUE);
    } elsif ($widgets->{'firstrun'}->get_current_page == 4) {

        # Fonts
        $header = gettext("Fonts");
        $widgets->{'firstrun'}->set_page_complete($page, TRUE);
        $widgets->{'firstrunxml'}->get_object('fontFRMain')
          ->set_font_name($config->{'Main'});
        $widgets->{'firstrunxml'}->get_object('fontFRHeader')
          ->set_font_name($config->{'Header'});
        $widgets->{'firstrunxml'}->get_object('fontFRFooter')
          ->set_font_name($config->{'Footer'});
        $widgets->{'firstrunxml'}->get_object('fontFROSD')
          ->set_font_name($config->{'OSD'});
    } elsif ($widgets->{'firstrun'}->get_current_page == 5) {

        # Directories
        $header = gettext("Image Directories");
        $widgets->{'firstrunxml'}->get_object('fileFRBGdir')
          ->set_label($config->{'BGDirectory'});
        $widgets->{'firstrunxml'}->get_object('fileFRBGdir')->{'user_data'} =
          "fr_bg";
        $widgets->{'firstrunxml'}->get_object('fileFRImagedir')
          ->set_label($config->{'ImageDirectory'});
        $widgets->{'firstrunxml'}->get_object('fileFRImagedir')->{'user_data'} =
          "fr_img";
        $widgets->{'firstrun'}->set_page_complete($page, TRUE);
    } elsif ($widgets->{'firstrun'}->get_current_page == 6) {

        # Special items
        $header = gettext("Most-used items");
        $widgets->{'firstrunxml'}->get_object('entryFRSong')
          ->set_text($config->{'SpecialSong'});
        $widgets->{'firstrunxml'}->get_object('entryFRImage')
          ->set_text($config->{'SpecialImage'});
        $widgets->{'firstrunxml'}->get_object('entryFRBack')
          ->set_text($config->{'SpecialBack'});
        my $bibles = $config->{'Bibles'};
        my $store = Gtk2::ListStore->new('Glib::String', 'Glib::String');
        $widgets->{'firstrunxml'}->get_object('comboFRBible')->remove_text(0);
        my ($iter, $activeiter);

        foreach (sort keys %$bibles) {
            my ($type, $bible) = split(/;/, $config->{'Bibles'}->{$_});
            $iter = $store->append;
            $store->set($iter, 0, $bible, 1,
                $_ . ":" . $config->{'Bibles'}->{$_});
            if ($config->{'DefBible'} eq $_ . ":" . $config->{'Bibles'}->{$_}) {
                $activeiter = $iter;
            }
        }
        $widgets->{'firstrunxml'}->get_object('comboFRBible')
          ->set_model($store);
        if (defined $activeiter) {
            $widgets->{'firstrunxml'}->get_object('comboFRBible')
              ->set_active_iter($activeiter);
        }
        $widgets->{'firstrun'}->set_page_complete($page, TRUE);
    } elsif ($widgets->{'firstrun'}->get_current_page == 7) {

        # Confirm
        $header = gettext("Confirm");
        $widgets->{'firstrun'}->set_page_complete($page, TRUE);
    }

    $header = fromutf($header);
    $widgets->{'firstrun'}->set_page_title($page, $header);
}

#***

#****f* lyricue/firstrun_pw_changed
# NAME
#   firstrun_pw_changed
# SYNOPSIS
#   firstrun_pw_changed()
# FUNCTION
#   Firstrun login changed
# INPUTS
# OUTPUT
# SOURCE
#
sub firstrun_pw_changed {
    debug("Firstrun login changed");
    reset_timer($globals->{'nav_update_timer'});
    $globals->{'fr_update_timer'} =
      Glib::Timeout->add(500, \&firstrun_pw_check);
}

#***

#****f* lyricue/firstrun_pw_check
# NAME
#   firstrun_pw_check
# SYNOPSIS
#   firstrun_pw_check()
# FUNCTION
#   Checking login
# INPUTS
# OUTPUT
# SOURCE
#
sub firstrun_pw_check {
    debug("Checking login");
    reset_timer($globals->{'fr_update_timer'});
    $globals->{'db_adminuser'} =
      $widgets->{'firstrunxml'}->get_object('entryFRUsername')->get_text();
    $globals->{'db_adminpassword'} =
      $widgets->{'firstrunxml'}->get_object('entryFRPassword')->get_text();
    my $page =
      $widgets->{'firstrun'}
      ->get_nth_page($widgets->{'firstrun'}->get_current_page);
    my ($dbh);
    eval {
        $dbh = DBI->connect(
            "DBI:mysql:mysql:" . $globals->{'mysqlhost'},
            $globals->{'db_adminuser'},
            $globals->{'db_adminpassword'}
        );
    };

    if ($dbh) {
        debug("Login accepted");
        $widgets->{'firstrun'}->set_page_complete($page, TRUE);
        $widgets->{'firstrunxml'}->get_object('labelFRDBCheck')
          ->set_markup(gettext("<i>Username/Password accepted</i>"));
    } else {
        debug("Login failed");
        $widgets->{'firstrun'}->set_page_complete($page, FALSE);
        $widgets->{'firstrunxml'}->get_object('labelFRDBCheck')
          ->set_markup(gettext("<i>Username/Password failed</i>"));
    }
}

#***

#****f* lyricue/firstrun_apply
# NAME
#   firstrun_apply
# SYNOPSIS
#   firstrun_apply()
# FUNCTION
#   Apply Wizard
# INPUTS
# OUTPUT
# SOURCE
#
sub firstrun_apply {
    debug("Apply Wizard");

    # Confirmed changes - so save them

    $config->{'Main'} =
      $widgets->{'firstrunxml'}->get_object('fontFRMain')->get_font_name();
    $config->{'Header'} =
      $widgets->{'firstrunxml'}->get_object('fontFRHeader')->get_font_name();
    $config->{'Footer'} =
      $widgets->{'firstrunxml'}->get_object('fontFRFooter')->get_font_name();
    $config->{'OSD'} =
      $widgets->{'firstrunxml'}->get_object('fontFROSD')->get_font_name();
    $config->{'Height'} =
      $widgets->{'firstrunxml'}->get_object('spinFRHeight')->get_value();
    $config->{'Width'} =
      $widgets->{'firstrunxml'}->get_object('spinFRWidth')->get_value();
    my $set = "";

    foreach my $value ("Top", "Bottom", "Centre") {

        if (
            fromutf(gettext($value)) eq ucfirst(
                $widgets->{'firstrunxml'}->get_object('comboFRVertical')
                  ->get_active_text()
            )
          )
        {
            $set = $value;
        }
    }
    if ($set eq "") {
        $set =
          $widgets->{'firstrunxml'}->get_object('comboFRVertical')
          ->get_active_text();
    }
    $config->{'VerticalLocation'} = $set;
    $set = "";
    my $set2 = "";
    foreach my $value ("Left", "Right", "Centre") {
        if (
            fromutf(gettext($value)) eq ucfirst(
                $widgets->{'firstrunxml'}->get_object('comboFRHorizontal')
                  ->get_active_text()
            )
          )
        {
            $set = $value;
        }
        if (
            fromutf(gettext($value)) eq ucfirst(
                $widgets->{'firstrunxml'}->get_object('comboFRJustification')
                  ->get_active_text()
            )
          )
        {
            $set2 = $value;
        }
    }
    if ($set eq "") {
        $set =
          $widgets->{'firstrunxml'}->get_object('comboFRHorizontal')
          ->get_active_text();
    }
    if ($set2 eq "") {
        $set2 =
          $widgets->{'firstrunxml'}->get_object('comboFRJustification')
          ->get_active_text();
    }
    $config->{'HorizontalLocation'} = $set;
    $config->{'Justification'}      = $set2;

#$config->{'BGImage'} = $widgets->{'prefs'}->get_object('imagePrefBG')->{user_data};
    $config->{'SpecialSong'} =
      $widgets->{'firstrunxml'}->get_object('entryFRSong')->get_text();
    $config->{'SpecialImage'} =
      $widgets->{'firstrunxml'}->get_object('entryFRImage')->get_text();
    $config->{'SpecialBack'} =
      $widgets->{'firstrunxml'}->get_object('entryFRBack')->get_text();
    my $iter =
      $widgets->{'firstrunxml'}->get_object('comboFRBible')->get_active_iter;
    if ($iter) {
        $config->{'DefBible'} =
          $widgets->{'firstrunxml'}->get_object('comboFRBible')
          ->get_model->get($iter, 1);
    }
    $config->{'ImageDirectory'} =
      $widgets->{'firstrunxml'}->get_object('fileFRImagedir')->get_label();
    $config->{'BGDirectory'} =
      $widgets->{'firstrunxml'}->get_object('fileFRBGdir')->get_label();
    $config->{'DBHost'} =
      $widgets->{'firstrunxml'}->get_object('entryFRHostDB')->get_text();
    $globals->{'mysqlhost'} = $config->{'DBHost'};

    db_select();
    write_config();

    if (!$globals->{'firstrun'}) {
        init_preview();
        init_miniview();

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

#***

#****f* lyricue/firstrun_bible
# NAME
# SYNOPSIS
#   firstrun_bible()
# FUNCTION
#
# INPUTS
# OUTPUT
# SOURCE
#
sub firstrun_bible {
}

#***

#****f* lyricue/online-help
# NAME
#   online_help
# SYNOPSIS
#   online_help()
# FUNCTION
#   Load documentation in browser
# SOURCE
#
sub online_help {
    debug("Load Documentation");
    my $command = "xdg-open ghelp:lyricue";
    system($command);
}

#***

#****f* lyricue/start_transaction
# NAME
#   start_transaction
# SYNOPSIS
#   start_transaction()
# FUNCTION
#   Mark the start of a bunch of queries to be run in a single transaction
# SOURCE
#
sub start_transaction {
    my $globals->{'old_re'} = $lyricDbh->{RaiseError};
    $lyricDbh->{RaiseError} = 1;
    $lyricDbh->{AutoCommit} = 0;
}

#***

#****f* lyricue/end_transaction
# NAME
#   end_transaction
# SYNOPSIS
#   end_transaction()
# FUNCTION
#   Mark the end of a bunch of queries to be run in a single transaction
# SOURCE
#
sub end_transaction {
    $lyricDbh->rollback() if $@;
    $lyricDbh->{AutoCommit} = 1;
    $lyricDbh->{RaiseError} = $globals->{'old_re'};
}

#***

#****f* lyricue/clean_database
# NAME
#   clean_database
# SYNOPSIS
#   clean_database()
# FUNCTION
#   Rescan the image/backgrounds directories
#   Cleans up old associations
#   Cleans up left-over playlist items
# SOURCE
#
sub clean_database {
    debug("Cleaning database");
    my $query = "SELECT description,category FROM media WHERE format=\"file\"";
    my $sth = do_query($mediaDbh, $query, FALSE);
    while ($row = $sth->fetchrow_hashref()) {
        my @mystat = stat($row->{'category'});
        if (!(@mystat)) {

            # file no longer exists so remove it from db
            my $query2 = "DELETE FROM media WHERE format=\"file\" AND category="
              . quote($row->{'category'});
            my $sth2 = do_query($mediaDbh, $query2, FALSE);
        }
    }

    # Fix leftover font colours
    $query =
      "UPDATE media SET shadowcolour=\"\" WHERE shadowcolour=\"Default\"";
    $sth = do_query($mediaDbh, $query, FALSE);

    # Remove leftover playlist item from failed re-orders
    $query = "DELETE FROM playlist WHERE playorder=-1";
    $sth = do_query($lyricDbh, $query, FALSE);

    # Remove old status entries
    $query = "DELETE FROM status WHERE TIMEDIFF(NOW(),lastupdate) > '00:10:00'";
    $sth = do_query($lyricDbh, $query, FALSE);

    return FALSE;
}

#***
sub start_editview {
    if (defined($widgets->{'EditPreview'})) {
        stop_editview();
    }
    $widgets->{'EditPreview'} = Gtk2::Socket->new;
    $widgets->{'EditPreview'}->show;
    $widgets->{'EditPreview'}->set_size_request(-1, -1);
    $widgets->{'add'}->get_object('frameEditPreview')->show;
    $widgets->{'add'}->get_object('frameEditPreview')
      ->add($widgets->{'EditPreview'});
    my $command = sprintf(
        "%s -r %s -m %d -l %d -p '%s' -t preview",
        $globals->{'lyricue_server'},      $globals->{'mysqlhost'},
        $widgets->{'EditPreview'}->get_id, $globals->{'preview_port'},
        $globals->{'profile'}
    );
    debug($command);
    $globals->{'editview_pid'} = fork;

    if ($globals->{'editview_pid'} < 0) {
        display_fatal("Unable to start the lyricue server as a preview window",
            $! . "\nSQL: " . $query);
    }
    if ($globals->{'editview_pid'} == 0) {
        exec($command);
    }
    $widgets->{'EditPreview'}->signal_connect(
        'plug-removed' => sub {
            debug("Lyricue edit preview died..restarting\n");
            $widgets->{'main'}->get_object('frameEditPreview')
              ->remove($widgets->{'preview'});
            start_editview();
            1;
        }
    );
}

sub stop_editview {
    if (defined($globals->{'editview_pid'})) {
        kill 9, $globals->{'editview_pid'};
    }
    if (defined($widgets->{'EditPreview'})) {
        $widgets->{'EditPreview'}->destroy;
        undef $widgets->{'EditPreview'};
    }
}

sub toggle_blur {
    debug("Toggle Blur");
    my ($widget) = @_;
    if ($widget->get_active()) {
        update_display("blur", 1);
    } else {
        update_display("blur", 0);
    }
}

sub change_fade {
    debug("Changing fade amount");
    my ($widget) = @_;
    update_display("fade", $widget->get_value());
}

sub start_display {
    debug("Start lyricue display");
    execute_app(
        "",
        $globals->{'lyricue_server'},
        " -p '" . $globals->{'profile'} . "'"
    );
}

#****f* lyricue/export_playlist
# NAME
#   export_playlist
# SYNOPSIS
#   export_playlist ()
# FUNCTION
#   Show a dialog to choose where to save an exported playlist to
# OUTPUT
#   File dialog displayed
# SOURCE
#
sub export_playlist {
    debug("Export playlist");
    my $filexml = load_window('dialogFileChooser');
    $filexml->get_object('buttonFileOK')
      ->signal_connect("clicked", "complete_export_playlist", $filexml);
    my $filter = Gtk2::FileFilter->new;
    $filter->add_pattern("*.pdf");
    $filexml->get_object('dialogFileChooser')->set_filter($filter);
    $filexml->get_object('dialogFileChooser')->set_filename("presentation.pdf");
    $filexml->get_object('dialogFileChooser')
      ->set_title(gettext("Enter filename for export"));
    $filexml->get_object('dialogFileChooser')->show_all();
}

#***

#****f* lyricue/complete_export_playlist
# NAME
#   complete_export_playlist
# SYNOPSIS
#   complete_export_playlist ($widget, $filexml)
# FUNCTION
#   Export a playlist to the chosen filename
# INPUTS
#   $widget - Calling widget
#   $filexml - File dialog
# OUTPUT
#   File containing playlist
# SOURCE
#
sub complete_export_playlist {
    my ($widget, $filexml) = @_;
    debug("Exporting playlist");
    my $export = $filexml->get_object('dialogFileChooser')->get_filename;
    close_dialog($widget);
    my $playlist =
      $widgets->{'main'}->get_object('labelCurrentPlaylist')->{user_data};
    my $tmpdir = tempdir("lyricue-XXXX", DIR => "/var/tmp", CLEANUP => 0);
    update_display("save", $playlist, $tmpdir);
    my $command = "convert \"" . $tmpdir . "/*\" \"" . $export . "\"";
    debug($command);
    system($command);
    $command = "rm -rf \"" . $tmpdir . "\"";
    debug($command);
    system($command);
}

#***

sub gather_debug {
    debug("Saving troubleshooting info");
    my $command = "apport-bug lyricue";
    debug($command);
    system($command);
}

sub load_window {
    my ($ui_name) = @_;
    my $builder = Gtk2::Builder->new();
    $builder->set_translation_domain('lyricue');
    $builder->add_from_file($globals->{'sharedir'} . $ui_name . ".ui");
    $builder->connect_signals(undef);

    return $builder;
}

sub change_notebook_page {
    my ($widget);
    my $page =
      $widgets->{'main'}->get_object('notebookSources')->get_current_page;
    my $current_page =
      $widgets->{'main'}->get_object('notebookSources')->get_nth_page($page);
    if ($current_page == $widgets->{'main'}->get_object('vboxBGDir')) {
        bgdir_list("bg");
    } elsif ($current_page == $widgets->{'main'}->get_object('vboxImgDir')) {
        bgdir_list("img");
    }
}

sub fix_widget_names {
    my @dirs = (
        'UpLeft',   'Up',   'UpRight', 'Left', 'None', 'Right',
        'DownLeft', 'Down', 'DownRight'
    );
    foreach (@dirs) {
        $widgets->{'main'}->get_object("toggleIn" . $_)
          ->set_name("toggleIn" . $_);
        $widgets->{'main'}->get_object("toggleOut" . $_)
          ->set_name("toggleOut" . $_);
    }
}

sub image_playlist_toggle {
    debug("Image Playlist toggled");
    my ($model);
    $globals->{'image_playlist'} =
      $widgets->{'main'}->get_object('checkPreviews')->get_active() ? 1 : 0;
    if ($globals->{'image_playlist'}) {
        $widgets->{'main'}->get_object('scrollIconPlaylist')->show_all;
        $widgets->{'main'}->get_object('scrollPlaylist')->hide;
        $widgets->{'main'}->get_object('toolbarPlaylist')->hide;
        $widgets->{'main'}->get_object('frameMainLeft')->hide;
    } else {
        $widgets->{'main'}->get_object('scrollIconPlaylist')->hide;
        $widgets->{'main'}->get_object('scrollPlaylist')->show_all;
        $widgets->{'main'}->get_object('toolbarPlaylist')->show_all;
        $widgets->{'main'}->get_object('frameMainLeft')->show_all;
    }
    update_playlist();
}

sub load_profile_menu {
    debug("Populate profiles menu");
    my $menutop = Gtk2::Menu->new();
    my $group   = -1;

    my $query = "SELECT DISTINCT profile FROM config WHERE profile != ''";
    $sth = do_query($lyricDbh, $query, FALSE);
    my $selected_profile = NULL;
    while (my @row = $sth->fetchrow_array) {
        my $menuitem = Gtk2::RadioMenuItem->new_with_label($group, $row[0]);
        if ($group == -1) {
            $group = $menuitem->get_group;
        }
        if ($row[0] eq $globals->{'profile'}) {
            $selected_profile = $menuitem;
        }
        $menuitem->signal_connect("toggled", "select_profile", $row[0]);
        $menuitem->show;
        $menutop->append($menuitem);
    }
    my $sep = Gtk2::SeparatorMenuItem->new;
    $sep->show;
    $menutop->append($sep);

    my $menuitem =
      Gtk2::MenuItem->new_with_label(gettext("Create New Profile"));
    $menuitem->signal_connect("activate", "change_pref_profile", "menu");
    $menuitem->show;
    $menutop->append($menuitem);
    $selected_profile->set_active(TRUE);

    $widgets->{'main'}->get_object('profiles1')->set_submenu($menutop);
}

sub select_profile {
    my ($widget, $profile) = @_;
    if ($widget->get_active()) {
        if ($profile ne $globals->{'profile'}) {
            debug(  "Change profile from "
                  . $globals->{'profile'} . " to "
                  . $profile);
            $globals->{'profile'} = $profile;

            update_display("profile",$globals->{'profile'},"");
            preview_display("profile",$globals->{'profile'},"");
            foreach my $display (keys $widgets->{'miniview'}) {
                if (defined $widgets->{'miniview'}->{$display}) {
                    $widgets->{'miniview'}->{$display}->destroy;
                    delete $widgets->{'miniview'}->{$display};
                }
                kill 9, $globals->{'miniview_pid'}->{$display};
            }
            $config = load_config($globals->{'profile'});
            check_displays();
            init_preview();
            init_miniview();
            choose_playlist();
        }
    }
}

sub toggle_miniview {
    my $new_status = FALSE;
    if ( $widgets->{'main'}->get_object('checkCurrent')->get_active()) {
        $new_status=TRUE;
    }
    if ($config->{'Miniview'} != $new_status){
        $config->{'Miniview'} = $new_status;
        init_miniview();
    }
}

sub toggle_preview {
    my $new_status = FALSE;
    if ( $widgets->{'main'}->get_object('checkPreview')->get_active() ) {
        $new_status=TRUE;
    }
    if ($config->{'DynamicPreview'} != $new_status){
        $config->{'DynamicPreview'} = $new_status;
        init_preview();
    }
}

sub toggle_server_active {
    my ($widget, $event, $display) = @_;
    if ($displays{$display} ){
        debug("Set server inactive for ".$display);
        $displays{$display}=FALSE;
        $widgets->{'miniview_frame'}->{$display}->set_label($display." - ".gettext("inactive"));
    } else {
        debug("Set server active for ".$display);
        $displays{$display}=TRUE;
        $widgets->{'miniview_frame'}->{$display}->set_label($display);
    }
}

sub close_miniview {
    my ($display) =@_;
    debug("Removing miniview for ".$display); 
    if (defined $miniviews{$display}) {
        undef $miniviews{$display};
    }
    if (defined $widgets->{'miniview_frame'}->{$display}) {
        $widgets->{'miniview_frame'}->{$display}->destroy;
        undef $widgets->{'miniview_frame'}->{$display};
    }
    if (defined $widgets->{'miniview'}->{$display}) {
        $widgets->{'miniview'}->{$display}->destroy;
        undef $widgets->{'miniview'}->{$display};
    }
    if (defined $globals->{'miniview_pid'}->{$display}) {
        kill 9, $globals->{'miniview_pid'}->{$display};
            undef $globals->{'miniview_pid'}->{$display};
    }
}

sub do_query {
    my ($dbh, $query, $silent) = @_;
    if (!$silent) {
        qdebug($query);
    }
    my $sth = $dbh->prepare($query)
      || database_failure($errorcodes->{'sqlprepare'}, $! . "\nSQL: " . $query);
    my $rv = $sth->execute
      || database_failure($errorcodes->{'sqlexecute'}, $! . "\nSQL: " . $query);
    return $sth;
}

sub load_profile_manager {
    debug("Loading Profile Manager");
    $widgets->{'profile'} = load_window("windowProfileManager");

    my $query = "SELECT DISTINCT profile FROM config WHERE profile != ''";
    my $sth   = do_query($lyricDbh, $query, FALSE);
    my $combo_model = Gtk2::ListStore->new('Glib::String');
    while (my @row = $sth->fetchrow_array()) {
        $combo_model->set($combo_model->append, 0, $row[0]);
    }

    # Profiles tab
    my $model =
      Gtk2::ListStore->new('Glib::String', 'Glib::String', 'Gtk2::ListStore');
    $query = "SELECT host,profile FROM profiles";
    $sth   = do_query($lyricDbh, $query, FALSE);
    while (my @row = $sth->fetchrow_array()) {
        $model->set($model->append, 0, $row[0], 1, $row[1], 2, $combo_model);
    }

    $widgets->{'profile'}->get_object('treeHosts')->set_model($model);
    $widgets->{'profile'}->get_object('treeHosts')
      ->insert_column_with_attributes(
        -1, 'Host', Gtk2::CellRendererText->new,
        text     => 0
      );
    my $combo_renderer = Gtk2::CellRendererCombo->new;
    $combo_renderer->set(
        text_column => 0,     # col in combo model with text to display
        editable    => TRUE
    );
    $combo_renderer->signal_connect(
        edited => sub {
            my ($cell, $text_path, $new_text) = @_;
            $model->set($model->get_iter_from_string($text_path), 1, $new_text);
        }
    );
    $widgets->{'profile'}->get_object('treeHosts')
      ->insert_column_with_attributes(
        -1, 'Profile',
        $combo_renderer,
        text  => 1,
        model => 2

      );
    $widgets->{'profile'}->get_object('treeProfile')->set_model($combo_model);
    $widgets->{'profile'}->get_object('treeProfile')
      ->insert_column_with_attributes(
        -1, 'Profile Name', Gtk2::CellRendererText->new,
        text     => 0
      );
    
    $widgets->{'profile'}->get_object("windowProfileManager")->show_all;
}

sub profile_manager_create {
    debug("Creating new profile");
    my $newxml = load_window("dialogPromptEntry");
    $newxml->get_object('dialogPromptEntry')
      ->set_title(fromutf(gettext("Create new profile")));
    $newxml->get_object('labelPromptE')
      ->set_text(fromutf(gettext("Name of profile")));

    my $response = $newxml->get_object('dialogPromptEntry')->run();
    if ($response eq "ok") {
        my $oldprofile = $globals->{'profile'};
        $globals->{'profile'} =
          $newxml->get_object('entryPromptE')->get_text();
        firstrun_wizard();
        Gtk2->main();
        $globals->{'profile'} = $oldprofile;
        load_config($globals->{'profile'});
    }
    close_dialog($newxml->get_object('dialogPromptEntry'));
    close_dialog($widgets->{'profile'}->get_object('windowProfileManager'));
    load_profile_manager();
}
sub profile_manager_remove {
    debug("Removing profile");
    my $selection = $widgets->{'profile'}->get_object('treeProfile')->get_selection;
    if ($selection) {
        my ($m, $i) = $selection->get_selected;
        if ($m) {
            my $profile = $m->get($i,0);
            my $confirmxml = load_window("dialogConfirm");
            $confirmxml->get_object('labelDelete')->set_text(gettext("Are you sure you wish to remove the profile \"").$profile."\"?");
            $confirmxml->get_object('dialogConfirm')->set_title(gettext("Remove Profile \"").$profile."\"");
            my $confirm = $confirmxml->get_object('dialogConfirm')->run();
            if ($confirm eq "ok") {
                my $query = "DELETE FROM config WHERE profile=\"".$profile."\"";
                do_query($lyricDbh, $query, FALSE);
                $query = "SELECT DISTINCT profile FROM profiles WHERE profile !=\"".$profile."\"";
                my $sth = do_query($lyricDbh, $query, FALSE);
                my @row = $sth->fetchrow_array();
                $query = "UPDATE profiles SET profile=\"".$row[0]."\"";
                do_query($lyricDbh, $query, FALSE);
                close_dialog($confirmxml->get_object('dialogConfirm'));
                close_dialog($widgets->{'profile'}->get_object('windowProfileManager'));
                load_profile_manager();
            } else {
                close_dialog($confirmxml->get_object('dialogConfirm'));
            }
        }
    }
}
sub profile_manager_apply {
    debug("Apply Host<->Profile mapping");
    my $store = $widgets->{'profile'}->get_object('treeHosts')->get_model();
    my $iter  = $store->get_iter_first;
    while (defined $iter) {
        my $host    = $store->get_value($iter, 0);
        my $profile = $store->get_value($iter, 1);
        my $query = "SELECT profile FROM profiles WHERE host='" . $host . "'";
        my $sth   = do_query($lyricDbh, $query, FALSE);
        my @row = $sth->fetchrow_array();
        if (defined $row[0]) {
            if ($row[0] ne $profile) {
                $query =
                    "UPDATE profiles SET profile = '"
                  . $profile
                  . "' WHERE host='"
                  . $host . "'";
                $sth = do_query($lyricDbh, $query, FALSE);
            }
        } else {
            $query =
                "INSERT INTO profiles SET ( profile, host) VALUES ( '"
              . $profile . "', '"
              . $host . "')";
            $sth = do_query($lyricDbh, $query, FALSE);
        }
        $iter = $store->iter_next($iter);
    }
}

sub profile_manager_revert {
    debug("Revert Host<->Profile mapping");
    close_dialog($widgets->{'profile'}->get_object('windowProfileManager'));
    load_profile_manager();
}
