Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Bower components Debian packages RPM packages NuGet packages

beebox / crossover   deb

Repository URL to install this package:

Version: 18.5.0-1 

/ opt / cxoffice / lib / perl / CXMenuMacOSX.pm

# (c) Copyright 2006-2008, 2010, 2014. CodeWeavers, Inc.
package CXMenuMacOSX;
use warnings;
use strict;

use CXLog;
use CXXMLDOM;
use CXPlist;
use CXMenu;
use base "CXMenu";


#####
#
# Folder helper functions
#
#####

sub get_mac_components($$)
{
    my ($self, $components)=@_;

    my $menu=@$components[-1];
    my $mac_root=$self->{$menu->{root}};
    if (!defined $mac_root)
    {
        $mac_root=[{ _mac_key    => "$self->{tag}/",
                     _mac_path   => "$self->{tag}/",
                     description => $ENV{CX_BOTTLE},
                     is_dir      => 1
                   },
                   { _mac_key    => "$menu->{root}/",
                     _mac_path   => "$self->{tag}/$menu->{root}/",
                     is_dir      => 1
                   }
                  ];
        $self->{$menu->{root}}=$mac_root;
    }

    my @mac_components=@$mac_root;
    if (!exists $menu->{_mac_path})
    {
        my $root="$menu->{tag}/$menu->{root}";
        foreach my $component (@$components)
        {
            # The trailing '/' is needed to separate the folder and menu
            # namespaces. It is also what identifies folders in the plist file.
            if ($component->{is_dir})
            {
                $component->{_mac_key}="$component->{name}/";
                $component->{_mac_path}="$root$component->{path}/";
            }
            else
            {
                $component->{_mac_key}="$component->{name}";
                $component->{_mac_path}="$root$component->{path}";
            }
        }
    }
    push @mac_components, @$components;
    return \@mac_components;
}

sub component_to_dict($$)
{
    my ($component, $dict)=@_;

    if ($component->{localize})
    {
        CXPlist::add_key_with_new_tag($dict, "Localize", "true");
    }
    if ($component->{description})
    {
        CXPlist::add_key_with_string($dict, "Description", $component->{description});
    }
    if ($component->{icon})
    {
        my $icon=CXMenu::get_mac_icon_path($component);
        if ($icon)
        {
            CXPlist::add_key_with_string($dict, "Icon", $icon);
            $icon =~ s!^\Q$ENV{WINEPREFIX}\E/!%WINEPREFIX%/!;
            $icon =~ s!^\Q$ENV{CX_ROOT}\E/!%CX_ROOT%/!;
            CXPlist::add_key_with_string($dict, "RobustIcon", $icon);
        }
    }
    if ($component->{command})
    {
        CXPlist::add_key_with_string($dict, "Command", $component->{command});
    }
    if ($component->{rawpath})
    {
        CXPlist::add_key_with_string($dict, "RawPath", $component->{rawpath});
    }
    if ($component->{arch})
    {
        CXPlist::add_key_with_string($dict, "Arch", $component->{arch});
    }
}

sub get_folder($$$$)
{
    my ($self, $parent, $mac_path, $mac_key)=@_;

    if (exists $self->{folders}->{$mac_path})
    {
        return $self->{folders}->{$mac_path};
    }

    my $children=$self->{children}->{$parent};
    if (!defined $children)
    {
        $children=CXPlist::get_value_by_name($parent, "Children");
        $self->{children}->{$parent}=$children;
    }
    my $folder=CXPlist::get_value_by_name($children, $mac_key) if (defined $children);
    $self->{folders}->{$mac_path}=$folder;
    return $folder;
}


#####
#
# XML file creation and parsing
#
#####

sub init_xml($$)
{
    my ($self)=@_;

    return $self->{xml} if (defined $self->{xml});
    $self->{xml}=0;

    my $xml;
    if (-f $self->{xml_file})
    {
        $xml=CXXMLDOM::parse_xml_file($self->{xml_file});
    }
    else
    {
        cxlog("Creating Mac OS X menu plist file\n");
        my $plist="<plist version=\"1.0\">\n" .
                "  <dict>\n" .
                "  </dict>\n" .
                "</plist>\n";
        $xml=CXXMLDOM::parse_xml_string($plist);
    }
    return 0 if (!$xml);

    # Now check that the basic structure of this XML file is correct
    my $plist=find_tag($xml, "plist");
    if (!$plist)
    {
        cxerr("unable to find the top plist tag in '$self->{xml_file}'\n");
        return 0;
    }

    my $tags_root=find_tag($plist, "dict");
    if (!$tags_root)
    {
        cxerr("unable to find the top dict tag in '$self->{xml_file}'\n");
        return 0;
    }

    $self->{xml}=$xml;
    $self->{plist}=$plist;
    $self->{tags_root}=$tags_root;
    $self->{children}->{$tags_root}=$tags_root;
    return 1;
}


#####
#
# Main
#
#####

sub detect($$$$)
{
    my ($class, $cxoptions, $cxconfig, $gui_info)=@_;
    return () if (!$gui_info->{macosx_on});
    # We don't do anything outside bottles
    return () if (!$cxoptions->{desktopdata} or $cxoptions->{ro_desktopdata});

    # Check for XML::DOM support
    my $err=CXXMLDOM::get_xml_load_error();
    if ($err)
    {
        cxerr("unable to use XML::DOM:\n$err\n");
        return  ();
    }

    my $self={
        tag         => $cxoptions->{tag},
        desktopdata => $cxoptions->{desktopdata},
        xml_file    => "$cxoptions->{desktopdata}/cxmenu/cxmenu_macosx.plist"
    };
    bless $self, $class;
    return ($self);
}

sub id($)
{
    return "CXMenuMacOSX/";
}

sub install($$)
{
    my ($self, $components)=@_;
    return 0 if (!$self->init_xml());

    my $in_cache=1;
    my $parent=$self->{tags_root};
    my $mac_components=$self->get_mac_components($components);
    foreach my $component (@$mac_components)
    {
        my $folder;
        $folder=$self->{folders}->{$component->{_mac_path}} if ($in_cache);
        if (!defined $folder)
        {
            $in_cache=undef;
            my $children=$self->{children}->{$parent};
            if (!defined $children)
            {
                $children=CXPlist::get_value_by_name($parent, "Children");
                $children=CXPlist::add_key_with_new_tag($parent, "Children", "dict") if (!defined $children);
                $self->{children}->{$parent}=$children;
            }
            $folder=CXPlist::get_value_by_name($children, $component->{_mac_key});
        }

        if (!defined $folder)
        {
            my $children=$self->{children}->{$parent};
            cxlog("creating $component->{_mac_path}\n");
            $folder=CXPlist::add_key_with_new_tag($children, $component->{_mac_key}, "dict");
            component_to_dict($component, $folder);
            $self->{folders}->{$component->{_mac_path}}=$folder;
        }
        elsif (!$component->{intermediate})
        {
            my $children=$folder->getParentNode();
            cxlog("recreating $component->{_mac_path}\n");

            # Overwrite the old properties
            my $new_folder=create_element($children->getOwnerDocument(), "dict");
            $children->replaceChild($new_folder, $folder);
            component_to_dict($component, $new_folder);

            if ($component->{is_dir})
            {
                # But keep the old children nodes, if any
                my $children=CXPlist::get_value_by_name($folder, "Children");
                if (defined $children)
                {
                    CXPlist::add_key_with_child($new_folder, "Children", $children);
                    $self->{children}->{$new_folder}=$children;
                }
            }

            $folder=$new_folder;
            $self->{folders}->{$component->{_mac_path}}=$folder;
        }
        $parent=$folder;
    }

    # Make sure our brand new (maybe empty) folder will not be
    # 'garbage collected' by finalize()
    my $menu=@$mac_components[-1];
    $self->{status}->{$menu->{_mac_path}}="in-use" if ($menu->{is_dir});

    $self->{modified}=1;
    return 1;
}

sub query($$)
{
    my ($self, $components)=@_;
    return ($self->id(), "") if (!defined $components);
    return "0" if (!$self->init_xml());

    my $parent=$self->{tags_root};
    my $mac_components=$self->get_mac_components($components);
    foreach my $component (@$mac_components)
    {
        my $folder=$self->get_folder($parent, $component->{_mac_path},
                                  $component->{_mac_key});
        return "" if (!defined $folder);
        $parent=$folder;
    }

    return $self->id();
}

sub get_files($$)
{
    my ($self, $components)=@_;
    # The XML file is inside the CrossOver bottle
    # but would not normally get packaged.
    return -f $self->{xml_file} ? [$self->{xml_file}] : [];
}

sub uninstall($$)
{
    my ($self, $components)=@_;
    return 0 if (!$self->init_xml());

    # Get hold of the folder
    my $folder;
    my $parent=$self->{tags_root};
    my $mac_components=$self->get_mac_components($components);
    foreach my $component (@$mac_components)
    {
        $folder=$self->get_folder($parent, $component->{_mac_path},
                                  $component->{_mac_key});
        return 1 if (!defined $folder);
        $parent=$folder;
    }

    # Delete it
    my $mac_path=$mac_components->[-1]->{_mac_path};
    cxlog("Deleting $mac_path\n");
    $parent=$folder->getParentNode();
    my $key=CXPlist::get_key_by_value($folder);
    remove_element($parent, $key);
    remove_element($parent, $folder);
    $self->{folders}->{$mac_path}=undef;
    $self->{modified}=1;

    # Always mark the parent folder for garbage collection
    $self->{garbage_collect}->{$mac_components->[-2]->{_mac_path}}=1;

    return 1;
}

sub removeall($$)
{
    my ($self, $pattern)=@_;

    if (-f $self->{xml_file})
    {
        cxlog("Deleting '$self->{xml_file}'\n");
        if (!unlink $self->{xml_file})
        {
            cxerr("unable to delete '$self->{xml_file}': $!\n");
            return 0;
        }
    }
    delete $self->{xml};

    return 1;
}

sub gc_get_folder_status($$)
{
    my ($self, $mac_path)=@_;

    my $path="";
    my $folder;
    my $parent=$self->{tags_root};
    foreach my $mac_key (split "/", $mac_path)
    {
        $path.="$mac_key/";
        $folder=$self->get_folder($parent, $path, $mac_key);
        if (!$folder)
        {
            cxlog("folder '$mac_path' has already been deleted\n");
            return undef;
        }
        $parent=$folder;
    }

    # Check for sub-folders
    my $children=$self->{children}->{$folder};
    $children=CXPlist::get_value_by_name($folder, "Children") if (!defined $children);
    return ("in-use", $folder) if ($children and find_tag($children, "key"));

    cxlog("mac_path=[$mac_path] empty\n");
    return ("empty", $folder);
}

sub gc_delete_folder($$$)
{
    my ($self, $mac_path, $folder)=@_;

    cxlog("Deleting $mac_path\n");
    my $parent=$folder->getParentNode();
    my $key=CXPlist::get_key_by_value($folder);
    remove_element($parent, $key);
    remove_element($parent, $folder);
    $self->{modified}=1;
}

sub finalize($)
{
    my ($self)=@_;
    return 0 if (!$self->{xml});

    $self->SUPER::finalize();

    if ($self->{modified})
    {
        if (find_tag($self->{tags_root}, "key"))
        {
            return CXXMLDOM::save_xml_file($self->{xml_file}, $self->{xml},
                "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n");
        }
        else
        {
            # The file is empty so let's just delete it
            if (-f $self->{xml_file})
            {
                cxlog("Deleting '$self->{xml_file}'\n");
                if (!unlink $self->{xml_file})
                {
                    cxerr("unable to delete '$self->{xml_file}': $!\n");
                    return 0;
                }
            }
            # Delete the parent directory if it is empty
            CXUtils::garbage_collect_subdirs($self->{desktopdata}, "/cxmenu", 1);
        }
    }
    else
    {
        cxlog("XML file '$self->{xml_file}' not modified\n");
    }
    return 1;
}

return 1;