#  Smeg - Simple Menu Editor for GNOME
#
#  Travis Watkins <alleykat@gmail.com>
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
#  (C) Copyright 2005 Travis Watkins

from __future__ import generators
import os, sys
cmddir = os.path.split(sys.argv[0])[0]
prefix = os.path.split(cmddir)[0]
if prefix == '': prefix = '.'
libdir = os.path.join(prefix, 'lib/smeg')
sys.path = [libdir] + sys.path

import xdg.Menu, xdg.Config, xdg.IniFile, xdg.MenuEditor, xdg.BaseDirectory
import xdg.IconTheme
import string, locale, re
import xml.dom.minidom, xml.dom

class MenuHandler(xdg.MenuEditor.MenuEditor):
    def __init__(self, renderer, options):
        menu, menu_path = None, None
        self.desktop_environment = 'GNOME'
        if options.kde:
            self.desktop_environment = 'KDE'

        self.renderer = renderer
        self.setWM(self.desktop_environment)
        xdg.Config.cache_time = 300
        try:
            self.locale = locale.getdefaultlocale()[0]
        except:
            self.locale = None

        if self.desktop_environment == 'KDE':
            menu_path = os.path.join(xdg.BaseDirectory.xdg_config_dirs[1], 'menus', 'kde-applications.menu')
            if not os.access(menu_path, os.R_OK):
                menu_path = None
            else:
                menu_path = 'kde-applications.menu'

        xdg.MenuEditor.MenuEditor.__init__(self, menu_path, root=options.root_mode)
        self.editor = self
        self.getIconThemes()

    def setWM(self, wm):
        xdg.Config.setWindowManager(wm)

    def getIconThemes(self):
        self.theme = ''
        kde_theme, gnome_theme = None, None

        try:
            fd = os.popen3('gconftool-2 -g /desktop/gnome/interface/icon_theme')
            output = fd[1].readlines()
            gnome_theme = output[0].strip()
        except:
            gnome_theme = 'gnome'

        try:
            fd = os.popen3('kde-config --path config')
            output = fd[1].readlines()
            cfgdir, tmp = output[0].split(':', 1)
 
            config = xdg.IniFile.IniFile()
            config.parse(os.path.join(cfgdir, 'kdeglobals'), ['General'])
            theme = config.get('Theme', 'Icons')
            if theme:
                kde_theme = theme
            else:
                kde_theme = 'default.kde'
        except:
            kde_theme = 'default.kde'

        if self.desktop_environment == 'GNOME':
            self.theme = gnome_theme
        elif self.desktop_environment == 'KDE':
            self.theme = kde_theme

    def getIconPath(self, entry, size):
        if isinstance(entry, xdg.Menu.Separator):
            return None
        if isinstance(entry, (str, unicode)):
            icon = entry
        elif isinstance(entry, xdg.Menu.MenuEntry):
            icon = entry.DesktopEntry.getIcon()
        else:
            icon = entry.getIcon()
            if entry == self.editor.menu:
                if self.desktop_environment == 'GNOME':
                    return self.getIconPath('gnome-main-menu', size)
                else:
                    if entry.Directory and entry.Directory == 'kde-main.directory':
                        return self.getIconPath('kmenu', size)
        if '/' in icon:
            if os.access(icon, os.F_OK):
                return icon
            else:
                return None

        if not 'debian' in icon or icon == 'debian-logo':
            if self.desktop_environment == 'KDE' and icon == 'help':
                path = xdg.IconTheme.getIconPath(icon, 32, self.theme)
            else:
                path = xdg.IconTheme.getIconPath(icon, size, self.theme)
            if path != None:
                return path
        if isinstance(entry, xdg.Menu.Menu):
            if self.desktop_environment == 'KDE':
                return self.getIconPath('folder', size)
            return self.getIconPath('gnome-fs-directory', size)
        elif isinstance(entry, xdg.Menu.MenuEntry):
            return self.getIconPath('application-default-icon', size)

    def quit(self):
        nodes = self.editor.doc.getElementsByTagName('Merge')
        for node in nodes:
            parent = node.parentNode
            parent.removeChild(node)
            parent.appendChild(node)
        self.save()
        def remove_whilespace_nodes(node):
            remove_list = []
            for child in node.childNodes:
                if child.nodeType == xml.dom.minidom.Node.TEXT_NODE:
                  child.data = child.data.strip()
                  if not child.data.strip():
                    remove_list.append(child)
                elif child.hasChildNodes():
                    remove_whilespace_nodes(child)
            for node in remove_list:
                node.parentNode.removeChild(node)
            return node
        doc = remove_whilespace_nodes(xml.dom.minidom.parse(self.filename))
        fd = open(self.filename, 'w')
        fd.write(re.sub('\n[\s]*([^\n<]*)\n[\s]*</', '\\1</', doc.toprettyxml().replace('<?xml version="1.0" ?>\n', '')))
        fd.close()

    def loadMenus(self, depth=1, menu=None):
        if not menu:
            menu = self.editor.menu
            self.depths = {0: None}
            self.depths[1] = self.renderer.addMenu(menu, self.depths, depth)

        depth += 1
        for entry in menu.getEntries(True):
            if isinstance(entry, xdg.Menu.Menu):
                if entry.getName() != '':
                    self.depths[depth] = self.renderer.addMenu(entry, self.depths, depth)
                    self.loadMenus(depth, entry)
        depth -= 1

    def loadEntries(self, menu):
        entries = []
        for entry in menu.getEntries(True):
            if isinstance(entry, xdg.Menu.Menu):
                if entry.getName() != '':
                    yield entry
            elif isinstance(entry, xdg.Menu.MenuEntry):
                if '-usercustom' not in entry.DesktopFileID:
                    if entry.Show == 'NoDisplay' or entry.Show == True:
                        entry.Parent = menu
                        yield entry
            elif isinstance(entry, xdg.Menu.Separator):
                entry.Parent = menu
                yield entry

    def copyAppDirs(self, oldparent, newparent, menu=None):
        if menu:
            master = menu.AppDirs
        else:
            master = self.menu.AppDirs
        new_menu = self._MenuEditor__getXmlMenu(newparent.getPath(True, True))
        for appdir in oldparent.AppDirs:
            if appdir not in newparent.AppDirs:
                if menu or appdir not in master:
                    self._MenuEditor__addXmlTextElement(new_menu, 'AppDir', appdir)

    def copyDirectoryDirs(self, oldparent, newparent, menu=None):
        if menu:
            master = menu.DirectoryDirs
        else:
            master = self.menu.DirectoryDirs
        new_menu = self._MenuEditor__getXmlMenu(newparent.getPath(True, True))
        for dirdir in oldparent.DirectoryDirs:
            if dirdir not in newparent.DirectoryDirs:
                if menu or dirdir not in master:
                    self._MenuEditor__addXmlTextElement(new_menu, 'DirectoryDir', dirdir)

    def getAccess(self, entry):
        return self.getAction(entry)

    def toggleVisible(self, entry, visible):
        if visible:
            if isinstance(entry, xdg.Menu.Menu):
                self.saveMenu(entry, nodisplay=True)
            else:
                self.saveEntry(entry, nodisplay=True)
        else:
            if isinstance(entry, xdg.Menu.Menu):
                xml_menu = self._MenuEditor__getXmlMenu(entry.getPath(True, True), False)
                if xml_menu:
                    for node in self._MenuEditor__getXmlNodesByName(['Deleted', 'NotDeleted'], xml_menu):
                        node.parentNode.removeChild(node)
                entry.Deleted = False
                self.saveMenu(entry, nodisplay=False)
            else:
                self.saveEntry(entry, nodisplay=False)

    def moveEntry(self, entry, oldparent, newparent, before=None, after=None, drag=False):
        if newparent.Name == 'Other':
            return False
        if oldparent == newparent and not drag:
            if after:
                if oldparent.Entries.index(entry) == len(oldparent.Entries) - 1:
                    return False
            if before:
                if oldparent.Entries.index(entry) == 0:
                    return False
        self.moveMenuEntry(entry, oldparent, newparent, after, before)
        if oldparent != newparent:
            self.copyAppDirs(oldparent, newparent)
        return True

    def moveMenu(self, menu, oldparent, newparent, before=None, after=None):
        if after:
            if oldparent.Entries.index(menu) == len(oldparent.Entries) - 1:
                return False
        if before:
            if oldparent.Entries.index(menu) == 0:
                return False
        xdg.MenuEditor.MenuEditor.moveMenu(self, menu, oldparent, newparent, after, before)
        if oldparent != newparent:
            self.copyAppDirs(oldparent, newparent, menu)
            self.copyDirectoryDirs(oldparent, newparent, menu)
        return True

    def moveSeparator(self, separator, parent, before=None, after=None):
        if after:
            if parent.Entries.index(separator) == len(parent.Entries) - 1:
                return False
        if before:
            if parent.Entries.index(separator) == 0:
                return False
        xdg.MenuEditor.MenuEditor.moveSeparator(self, separator, parent, after, before)
        return True

    def revertEntry(self, entry):
        self.revertMenuEntry(entry)

    def newEntry(self, parent, name, comment, command, icon, term, after):
        if name != None:
            self.createMenuEntry(parent, name, command, None, comment, icon, term, after=after)

    def newMenu(self, parent, name, comment, icon, after):
        if name != None:
            self.createMenu(parent, name, None, comment, icon, after=after)

    def newSeparator(self, parent, after):
        self.createSeparator(parent, after=after)

    def saveEntry(self, entry, name=None, comment=None, command=None, icon=None, term=None, nodisplay=None):
        self.editMenuEntry(entry, name, None, comment, command, icon, term, nodisplay=nodisplay)
        menu = self._MenuEditor__getXmlMenu(entry.Parent.getPath(True, True))
        appdir_path = os.path.join(xdg.BaseDirectory.xdg_data_dirs[0], 'applications')
        for node in menu.childNodes:
            if node.nodeType == xml.dom.Node.ELEMENT_NODE and node.tagName == 'AppDir':
                for child in node.childNodes:
                    if child.nodeType == xml.dom.Node.TEXT_NODE:
                        if child.nodeValue == appdir_path:
                            return
        self._MenuEditor__addXmlTextElement(menu, 'AppDir', appdir_path)

    def saveMenu(self, menu, name=None, comment=None, icon=None, nodisplay=None):
        xdg.MenuEditor.MenuEditor.editMenu(self, menu, name, None, comment, icon, nodisplay=nodisplay)
        menu = self._MenuEditor__getXmlMenu(menu.getPath(True, True))
        dirdir_path = os.path.join(xdg.BaseDirectory.xdg_data_dirs[0], 'desktop-directories')
        for node in menu.childNodes:
            if node.nodeType == xml.dom.Node.ELEMENT_NODE and node.tagName == 'DirectoryDir':
                for child in node.childNodes:
                    if child.nodeType == xml.dom.Node.TEXT_NODE:
                        if child.nodeValue == dirdir_path:
                            return
        self._MenuEditor__addXmlTextElement(menu, 'DirectoryDir', dirdir_path)

    def deleteEntry(self, entry):
        self.deleteMenuEntry(entry)
