#!/usr/bin/perl
#
# Catorise - create and maintain menus corresponding to
# ~~~~~~~~   package sections.            (c) Andrew Flegg 2010.
#                                         Released under the Artistic Licence.

BEGIN { use lib '/opt/catorise/lib'; }

use strict;
use warnings;
use Config::Tiny;


# -- Read pre-stored menu...
#
my %overrides = ();
if (open(IN, "</opt/catorise/menu")) {
  %overrides = map { chomp; split /:\s*/ } <IN>;
  close(IN);
}


# -- Valid sections...
#
my %validSections = map { $_ => 1} qw(
  desktop development education games graphics other ovi
  multimedia navigation network office science system utilities
  _root
);


# -- Section overrides...
#
my %sectionOverrides = (
  'web'              => 'network',
  'audiovideo'       => 'multimedia',
  'game'             => 'games',
  'settings'         => 'system',
  'utility'          => 'utilities',
);


# -- Index packages as quickly as possible...
#
my %packageIndex = ();
foreach my $line (`fgrep /usr/share/applications/hildon/ /var/lib/dpkg/info/*.list`) {
  chomp($line);
  my ($list, $desktop) = split /:/, $line;
  my ($listPackage) = $list =~ /([^\/]+)\.list$/;

  $packageIndex{$desktop} = $listPackage if $desktop =~ m{^/usr/share/applications/hildon/[^/]+\.desktop$};
}

my %oviApps = ();
if (open(IN, "</var/lib/apt/lists/downloads.maemo.nokia.com_fremantle_ovi_._Packages")) {
  while (<IN>) {
    chomp;
    next unless /^Package: (.*)$/;
    $oviApps{lc($1)} = 'ovi';
  }
  close(IN);
}


# -- Back-up hildon.menu if necessary...
#
my $needRestart = 0;
unless (-f '/etc/xdg/menus/hildon.catorise') {
  system('cp', '/etc/xdg/menus/hildon.menu',
               '/etc/xdg/menus/hildon.catorise');
  $needRestart = 1;
}


# -- Loop over all desktop files...
#
my %sections = ( _root => [] );
my $excludeFromAll = '';
open(OUT, ">/opt/catorise/menu") or die "Unable to store new menu: $!";
$| = 1;
foreach my $desktop (</usr/share/applications/hildon/*.desktop>) {
  $desktop = readlink($desktop) || $desktop;
  next if $desktop =~ /\/catorise-/;
  print '#';

  # Check it's a launcher icon
  my $configFull = Config::Tiny->read($desktop);
  my $config     = $configFull->{'Desktop Entry'};
  next unless $config;
  next if lc($config->{'Type'} || '') ne 'application';
  next if lc($config->{'NoDisplay'} || 'false') eq 'true';

  # Check if there's an override...
  my ($name)  = $desktop =~ /([^\/]+)\.desktop$/;
  my $section = $overrides{$name};

  # Temporary fix for MB#8111
  if (lc($config->{'X-Maemo-Prestarted'} || '') eq 'always') {
    my $shim = "/usr/share/applications/hildon/catorise-$name.desktop";
    unless (-f $shim) {
      foreach my $i (keys(%$configFull)) {
        delete $configFull->{$i} unless $i eq 'Desktop Entry';
      }
      foreach my $i (keys(%$config)) {
        delete $config->{$i} if $i =~ /^(MimeType|X-Maemo|X-Hildon|X-Osso-)/;
      }
      $config->{'Exec'} = "dbus-send --dest=com.nokia.HildonDesktop.AppMgr /com/nokia/HildonDesktop/AppMgr com.nokia.HildonDesktop.AppMgr.LaunchApplication string:$name";
      $configFull->write($shim);
    }

    $name = "catorise-$name";
    $excludeFromAll .= "        <Filename>$name.desktop</Filename>\n";
  }

  # -- Check categories in .desktop...
  #
  if (!$section) {
    foreach my $category (split /\;/, lc($config->{'Categories'} || '')) {
      $category = $sectionOverrides{$category} || $category;
      if ($validSections{$category}) {
        $section = $category;
        last;
      }
    }
  }

  # -- Find using dpkg...
  #
  if (!$section or $section eq 'other') {
    my $package = $packageIndex{$desktop};
    if ($package) {
      $section = (grep { /^Section: / } `dpkg -s "$package"`)[0];
      $section =~ s/.*?: (.+?\/)?//;
      chomp($section);
 
      # Is it Ovi?
      $section = $oviApps{$package} unless $validSections{$section};
    }
  }


  # -- Additional heuristics...
  #
  $section = $sectionOverrides{$section} || $section || '';
  $section = 'other' unless $validSections{$section};

  # Now we've got the section, store it...
  push @{ $sections{$section} }, $name;
  $name =~ s/^catorise-//;
  print OUT "$name: $section\n";
}
close(OUT) or die "Unable to close new menu: $!";


# -- Output the menu...
#
open(OUT, ">/etc/xdg/menus/hildon.menu") or die "Unable to write to menu: $!";
print OUT <<EOH;
<!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
 "http://www.freedesktop.org/standards/menu-spec/menu-1.0.dtd">

<Menu>

  <Name>Main</Name>

  <AppDir>/usr/share/applications/hildon</AppDir>
  <DirectoryDir>/opt/catorise/share</DirectoryDir>
EOH

print OUT "  <Include>\n";
foreach my $v (@{ $sections{'_root'} }) {
  print OUT "    <Filename>$v.desktop</Filename>\n";
}

print OUT "  </Include>\n  <Layout>\n    <Menuname>all</Menuname>\n";
my $menus = '';
foreach my $s (sort keys %sections) {
  next if $s eq '_root';
  print OUT "    <Menuname>$s</Menuname>\n" unless $s eq 'other';

  $menus .= "  <Menu>\n    <Name>$s</Name>\n".
            "    <Directory>$s.directory</Directory>\n    <Include>\n";
  foreach my $v (@{ $sections{$s} }) {
    $menus .= "      <Filename>$v.desktop</Filename>\n";
  }
  $menus .= "    </Include>\n";
  $menus .= "    <Layout>\n      <Filename>ovi.desktop</Filename>\n     <Merge type=\"all\"/>\n    </Layout>\n" if $s eq 'ovi';
  $menus .= "  </Menu>\n";
}
print OUT <<EOF;
    <Menuname>other</Menuname>
    <Merge type="all"/>
  </Layout>
$menus
  <Menu>
    <Name>all</Name>
    <Directory>all.directory</Directory>
    <Include>              
      <All/>               
    </Include>
    <Exclude>
$excludeFromAll
    </Exclude>
  </Menu>
</Menu>                    
EOF
close(OUT) or die "Unable to close menu: $!";


# -- Restart hildon-desktop if necessary...
#
system("killall", "hildon-desktop") if $needRestart;
exit;

