#!/usr/bin/env python
# -*- coding: utf-8 -*-

#***************************************************************************
#*                                                                         *
#*   Copyright (c) 2015 Yorik van Havre <yorik@uncreated.net>              *
#*                                                                         *
#*   This program is free software; you can redistribute it and/or modify  *
#*   it under the terms of the GNU Lesser General Public License (LGPL)    *
#*   as published by the Free Software Foundation; either version 2 of     *
#*   the License, or (at your option) any later version.                   *
#*   for detail see the LICENCE text file.                                 *
#*                                                                         *
#*   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 Library General Public License for more details.                  *
#*                                                                         *
#*   You should have received a copy of the GNU Library 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                                                                   *
#*                                                                         *
#***************************************************************************

from __future__ import print_function

__title__="FreeCAD Addon Manager Module"
__author__ = "Yorik van Havre","Jonathan Wiedemann","Kurt Kremitzki"
__url__ = "http://www.freecadweb.org"

"""
FreeCAD Addon Manager Module

It will fetch its contents from https://github.com/FreeCAD/FreeCAD-addons
You need a working internet connection, and optionally the GitPython package
installed.
"""

import os
import re
import shutil
import stat
import sys
import tempfile

import addonmanager_utilities as utils
from addonmanager_utilities import translate # this needs to be as is for pylupdate
from addonmanager_workers import *

def QT_TRANSLATE_NOOP(ctx,txt):
    return txt

## \defgroup ADDONMANAGER AddonManager
#  \ingroup ADDONMANAGER
#  \brief The Addon Manager allows to install workbenches and macros made by users
#  @{

class CommandAddonManager:
    """The main Addon Manager class and FreeCAD command"""

    def GetResources(self):

        return {'Pixmap': 'AddonManager',
                'MenuText': QT_TRANSLATE_NOOP("Std_AddonMgr", '&Addon manager'),
                'ToolTip': QT_TRANSLATE_NOOP("Std_AddonMgr", 'Manage external workbenches and macros'),
                'Group': 'Tools'}

    def Activated(self):

        # display first use dialog if needed

        from PySide import QtGui
        readWarning = FreeCAD.ParamGet('User parameter:Plugins/addonsRepository').GetBool('readWarning',False)
        if not readWarning:
            if QtGui.QMessageBox.warning(None,"FreeCAD",translate("AddonsInstaller", "The addons that can be installed here are not officially part of FreeCAD, and are not reviewed by the FreeCAD team. Make sure you know what you are installing!"), QtGui.QMessageBox.Cancel | QtGui.QMessageBox.Ok) != QtGui.QMessageBox.StandardButton.Cancel:
                FreeCAD.ParamGet('User parameter:Plugins/addonsRepository').SetBool('readWarning',True)
                readWarning = True

        if readWarning:
            self.launch()

    def launch(self):

        """Shows the Addon Manager UI"""

        import FreeCADGui
        from PySide import QtGui

        # create the dialog
        self.dialog = FreeCADGui.PySideUic.loadUi(os.path.join(os.path.dirname(__file__),"AddonManager.ui"))

        # cleanup the leftovers from previous runs
        self.repos = []
        self.macros = []
        self.macro_repo_dir = tempfile.mkdtemp()
        self.doUpdate = []
        self.addon_removed = False
        for worker in ["update_worker","check_worker","show_worker","showmacro_worker","macro_worker","install_worker"]:
            if hasattr(self,worker):
                thread = getattr(self,worker)
                if thread:
                    if thread.isFinished():
                        setattr(self,worker,None)
        self.dialog.tabWidget.setCurrentIndex(0)
        # these 2 settings to prevent loading an addon description on start (let the user click something first)
        self.firsttime = True
        self.firstmacro = True

        # restore window geometry and splitter state from stored state
        pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons")
        w = pref.GetInt("WindowWidth",600)
        h = pref.GetInt("WindowHeight",480)
        self.dialog.resize(w,h)
        sl = pref.GetInt("SplitterLeft",298)
        sr = pref.GetInt("SplitterRight",274)
        self.dialog.splitter.setSizes([sl,sr])

        # set nice icons to everything, by theme with fallback to FreeCAD icons
        self.dialog.setWindowIcon(QtGui.QIcon(":/icons/AddonManager.svg"))
        self.dialog.buttonExecute.setIcon(QtGui.QIcon.fromTheme("execute",QtGui.QIcon(":/icons/button_valid.svg")))
        self.dialog.buttonUninstall.setIcon(QtGui.QIcon.fromTheme("cancel",QtGui.QIcon(":/icons/edit_Cancel.svg")))
        self.dialog.buttonInstall.setIcon(QtGui.QIcon.fromTheme("download",QtGui.QIcon(":/icons/edit_OK.svg")))
        self.dialog.buttonUpdateAll.setIcon(QtGui.QIcon(":/icons/button_valid.svg"))
        self.dialog.buttonConfigure.setIcon(QtGui.QIcon(":/icons/preferences-system.svg"))
        self.dialog.buttonClose.setIcon(QtGui.QIcon.fromTheme("close",QtGui.QIcon(":/icons/process-stop.svg")))
        self.dialog.tabWidget.setTabIcon(0,QtGui.QIcon.fromTheme("folder",QtGui.QIcon(":/icons/folder.svg")))
        self.dialog.tabWidget.setTabIcon(1,QtGui.QIcon(":/icons/applications-python.svg"))


        # enable/disable stuff
        self.dialog.buttonExecute.setEnabled(False)
        self.dialog.buttonUninstall.setEnabled(False)
        self.dialog.buttonInstall.setEnabled(False)
        self.dialog.buttonUpdateAll.setEnabled(False)

        # connect slots
        self.dialog.buttonExecute.clicked.connect(self.executemacro)
        self.dialog.rejected.connect(self.reject)
        self.dialog.buttonInstall.clicked.connect(self.install)
        self.dialog.buttonUninstall.clicked.connect(self.remove)
        self.dialog.buttonUpdateAll.clicked.connect(self.apply_updates)
        self.dialog.listWorkbenches.currentRowChanged.connect(self.show)
        self.dialog.tabWidget.currentChanged.connect(self.switchtab)
        self.dialog.listMacros.currentRowChanged.connect(self.show_macro)
        self.dialog.buttonConfigure.clicked.connect(self.show_config)
        self.dialog.buttonClose.clicked.connect(self.dialog.reject)

        # allow to open links in browser
        self.dialog.description.setOpenLinks(True)
        self.dialog.description.setOpenExternalLinks(True)

        # center the dialog over the FreeCAD window
        mw = FreeCADGui.getMainWindow()
        self.dialog.move(mw.frameGeometry().topLeft() + mw.rect().center() - self.dialog.rect().center())

        # populate the list
        self.update()

        # rock 'n roll!!!
        self.dialog.exec_()

    def reject(self):

        """called when the window has been closed"""

        # save window geometry and splitter state for next use
        pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons")
        pref.SetInt("WindowWidth",self.dialog.width())
        pref.SetInt("WindowHeight",self.dialog.height())
        pref.SetInt("SplitterLeft",self.dialog.splitter.sizes()[0])
        pref.SetInt("SplitterRight",self.dialog.splitter.sizes()[1])

        # ensure all threads are finished before closing
        oktoclose = True
        for worker in ["update_worker","check_worker","show_worker","showmacro_worker",
                       "macro_worker","install_worker"]:
            if hasattr(self,worker):
                thread = getattr(self,worker)
                if thread:
                    if not thread.isFinished():
                        oktoclose = False

        # all threads have finished
        if oktoclose:
            if (hasattr(self,"install_worker") and self.install_worker) or (hasattr(self,"addon_removed") and self.addon_removed):
                # display restart dialog
                from PySide import QtGui,QtCore
                m = QtGui.QMessageBox()
                m.setWindowTitle(translate("AddonsInstaller","Addon manager"))
                m.setWindowIcon(QtGui.QIcon(":/icons/AddonManager.svg"))
                m.setText(translate("AddonsInstaller","You must restart FreeCAD for changes to take effect. Press Ok to restart FreeCAD now, or Cancel to restart later."))
                m.setIcon(m.Warning)
                m.setStandardButtons(m.Ok | m.Cancel)
                m.setDefaultButton(m.Cancel)
                ret = m.exec_()
                if ret == m.Ok:
                    shutil.rmtree(self.macro_repo_dir,onerror=self.remove_readonly)
                    # restart FreeCAD after a delay to give time to this dialog to close
                    QtCore.QTimer.singleShot(1000,utils.restartFreeCAD)
            try:
                shutil.rmtree(self.macro_repo_dir,onerror=self.remove_readonly)
            except:
                pass

        return True

    def update(self):

        """updates the list of workbenches"""

        self.dialog.listWorkbenches.clear()
        self.dialog.buttonExecute.setEnabled(False)
        self.repos = []
        self.update_worker = UpdateWorker()
        self.update_worker.info_label.connect(self.show_information)
        self.update_worker.addon_repo.connect(self.add_addon_repo)
        self.update_worker.progressbar_show.connect(self.show_progress_bar)
        self.update_worker.done.connect(self.check_updates)
        self.update_worker.start()

    def check_updates(self):

        "checks every installed addon for available updates"

        pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons")
        if pref.GetBool("AutoCheck",False) and not self.doUpdate:
            if hasattr(self,"check_worker"):
                thread = self.check_worker
                if thread:
                    if not thread.isFinished():
                        return
            self.dialog.buttonUpdateAll.setText(translate("AddonsInstaller","Checking for updates..."))
            self.check_worker = CheckWBWorker(self.repos)
            self.check_worker.mark.connect(self.mark)
            self.check_worker.enable.connect(self.enable_updates)
            self.check_worker.addon_repos.connect(self.update_repos)
            self.check_worker.start()

    def apply_updates(self):

        """apply all available updates"""

        if self.doUpdate:
            self.install(self.doUpdate)
            self.dialog.buttonUpdateAll.setEnabled(False)

    def enable_updates(self,num):

        """enables the update button"""

        if num:
            self.dialog.buttonUpdateAll.setText(translate("AddonsInstaller","Apply")+" "+str(num)+" "+translate("AddonsInstaller","update(s)"))
            self.dialog.buttonUpdateAll.setEnabled(True)
        else:
            self.dialog.buttonUpdateAll.setText(translate("AddonsInstaller","No update available"))
            self.dialog.buttonUpdateAll.setEnabled(False)

    def add_addon_repo(self, addon_repo):

        """adds a workbench to the list"""

        from PySide import QtGui
        self.repos.append(addon_repo)
        addonicon = self.get_icon(addon_repo[0])
        if addon_repo[2] > 0:
            item = QtGui.QListWidgetItem(addonicon,str(addon_repo[0]) + str(" ("+translate("AddonsInstaller","Installed")+")"))
            item.setForeground(QtGui.QBrush(QtGui.QColor(0,182,41)))
            self.dialog.listWorkbenches.addItem(item)
        else:
            self.dialog.listWorkbenches.addItem(QtGui.QListWidgetItem(addonicon,str(addon_repo[0])))

    def get_icon(self,repo):

        """returns an icon for a repo"""

        import AddonManager_rc
        from PySide import QtGui
        path = ":/icons/" + repo + "_workbench_icon.svg"
        if QtCore.QFile.exists(path):
            addonicon = QtGui.QIcon(path)
        else:
            addonicon = QtGui.QIcon(":/icons/document-package.svg")
        if addonicon.isNull():
            addonicon = QtGui.QIcon(":/icons/document-package.svg")
        return addonicon

    def show_information(self, label):

        """shows text in the information pane"""

        self.dialog.description.setText(label)
        if self.dialog.listWorkbenches.isVisible():
            self.dialog.listWorkbenches.setFocus()
        else:
            self.dialog.listMacros.setFocus()

    def show(self,idx):

        """loads information of a given workbench"""

        # this function is triggered also when the list is populated, prevent that here
        if idx == 0 and self.firsttime:
            self.dialog.listWorkbenches.setCurrentRow(-1)
            self.firsttime = False
            return

        if self.repos and idx >= 0:
            if hasattr(self,"show_worker"):
                # kill existing show worker (might still be busy loading images...)
                if self.show_worker:
                    self.show_worker.exit()
                    self.show_worker.stopImageLoading()
                    # wait until the thread stops
                    while True:
                        if self.show_worker.isFinished():
                            break
            self.show_worker = ShowWorker(self.repos, idx)
            self.show_worker.info_label.connect(self.show_information)
            self.show_worker.addon_repos.connect(self.update_repos)
            self.show_worker.progressbar_show.connect(self.show_progress_bar)
            self.show_worker.start()
            self.dialog.buttonInstall.setEnabled(True)
            self.dialog.buttonUninstall.setEnabled(True)

    def show_macro(self,idx):

        """loads information of a given macro"""

        # this function is triggered when the list is populated, prevent that here
        if idx == 0 and self.firstmacro:
            self.dialog.listMacros.setCurrentRow(-1)
            self.firstmacro = False
            return

        if self.macros and idx >= 0:
            if hasattr(self,"showmacro_worker"):
                if self.showmacro_worker:
                    if not self.showmacro_worker.isFinished():
                        self.showmacro_worker.exit()
                    if not self.showmacro_worker.isFinished():
                        return
            self.showmacro_worker = GetMacroDetailsWorker(self.macros[idx])
            self.showmacro_worker.info_label.connect(self.show_information)
            self.showmacro_worker.progressbar_show.connect(self.show_progress_bar)
            self.showmacro_worker.start()
            self.dialog.buttonInstall.setEnabled(True)
            self.dialog.buttonUninstall.setEnabled(True)
            if self.macros[idx].is_installed():
                self.dialog.buttonExecute.setEnabled(True)
            else:
                self.dialog.buttonExecute.setEnabled(False)

    def switchtab(self,idx):

        """does what needs to be done when switching tabs"""

        if idx == 1:
            if not self.macros:
                self.dialog.listMacros.clear()
                self.macros = []
                self.macro_worker = FillMacroListWorker(self.macro_repo_dir)
                self.macro_worker.add_macro_signal.connect(self.add_macro)
                self.macro_worker.info_label_signal.connect(self.show_information)
                self.macro_worker.progressbar_show.connect(self.show_progress_bar)
                self.macro_worker.start()
                self.dialog.listMacros.setCurrentRow(0)

    def update_repos(self, repos):

        """this function allows threads to update the main list of workbenches"""

        self.repos = repos

    def add_macro(self, macro):

        """adds a macro to the list"""

        if macro.name:
            if macro in self.macros:
                # The macro is already in the list of macros.
                old_macro = self.macros[self.macros.index(macro)]
                utils.update_macro_details(old_macro, macro)
            else:
                from PySide import QtGui
                self.macros.append(macro)
                import AddonManager_rc
                path = ":/icons/" + macro.name.replace(" ","_") + "_macro_icon.svg"
                if QtCore.QFile.exists(path):
                    addonicon = QtGui.QIcon(path)
                else:
                    addonicon = QtGui.QIcon(":/icons/document-python.svg")
                if addonicon.isNull():
                    addonicon = QtGui.QIcon(":/icons/document-python.svg")
                if macro.is_installed():
                    item = QtGui.QListWidgetItem(addonicon, macro.name + str(' (Installed)'))
                    item.setForeground(QtGui.QBrush(QtGui.QColor(0,182,41)))
                    self.dialog.listMacros.addItem(item)
                else:
                    self.dialog.listMacros.addItem(QtGui.QListWidgetItem(addonicon,macro.name))

    def install(self,repos=None):

        """installs a workbench or macro"""

        if self.dialog.tabWidget.currentIndex() == 0:
            # Tab "Workbenches".
            idx = None
            if repos:
                idx = []
                for repo in repos:
                    for i,r in enumerate(self.repos):
                        if r[0] == repo:
                            idx.append(i)
            else:
                idx = self.dialog.listWorkbenches.currentRow()
            if idx != None:
                if hasattr(self,"install_worker") and self.install_worker:
                    if self.install_worker.isRunning():
                        return
                self.install_worker = InstallWorker(self.repos, idx)
                self.install_worker.info_label.connect(self.show_information)
                self.install_worker.progressbar_show.connect(self.show_progress_bar)
                self.install_worker.mark_recompute.connect(self.mark_recompute)
                self.install_worker.start()

        elif self.dialog.tabWidget.currentIndex() == 1:
            # Tab "Macros".
            macro = self.macros[self.dialog.listMacros.currentRow()]
            if utils.install_macro(macro, self.macro_repo_dir):
                self.dialog.description.setText(translate("AddonsInstaller", "Macro successfully installed. The macro is now available from the Macros dialog."))
            else:
                self.dialog.description.setText(translate("AddonsInstaller", "Unable to install"))

    def show_progress_bar(self, state):

        """shows or hides the progress bar"""

        if state == True:
            self.dialog.tabWidget.setEnabled(False)
            self.dialog.buttonInstall.setEnabled(False)
            self.dialog.buttonUninstall.setEnabled(False)
            self.dialog.progressBar.show()
        else:
            self.dialog.progressBar.hide()
            self.dialog.tabWidget.setEnabled(True)
            if not (self.firsttime and self.firstmacro):
                self.dialog.buttonInstall.setEnabled(True)
                self.dialog.buttonUninstall.setEnabled(True)
            if self.dialog.listWorkbenches.isVisible():
                self.dialog.listWorkbenches.setFocus()
            else:
                self.dialog.listMacros.setFocus()

    def executemacro(self):

        """executes a selected macro"""

        import FreeCADGui
        if self.dialog.tabWidget.currentIndex() == 1:
            # Tab "Macros".
            macro = self.macros[self.dialog.listMacros.currentRow()]
            if not macro.is_installed():
                # Macro not installed, nothing to do.
                return
            macro_path = os.path.join(FreeCAD.getUserMacroDir(True), macro.filename)
            if os.path.exists(macro_path):
                macro_path = macro_path.replace("\\","/")

                FreeCADGui.open(str(macro_path))
                self.dialog.hide()
                FreeCADGui.SendMsgToActiveView("Run")
        else:
            self.dialog.buttonExecute.setEnabled(False)

    def remove_readonly(self, func, path, _):

        """Remove a read-only file."""

        os.chmod(path, stat.S_IWRITE)
        func(path)

    def remove(self):

        """uninstalls a macro or workbench"""

        if self.dialog.tabWidget.currentIndex() == 0:
            # Tab "Workbenches".
            idx = self.dialog.listWorkbenches.currentRow()
            basedir = FreeCAD.getUserAppDataDir()
            moddir = basedir + os.sep + "Mod"
            clonedir = moddir + os.sep + self.repos[idx][0]
            if os.path.exists(clonedir):
                shutil.rmtree(clonedir, onerror=self.remove_readonly)
                self.dialog.description.setText(translate("AddonsInstaller", "Addon successfully removed. Please restart FreeCAD"))
            else:
                self.dialog.description.setText(translate("AddonsInstaller", "Unable to remove this addon"))

        elif self.dialog.tabWidget.currentIndex() == 1:
            # Tab "Macros".
            macro = self.macros[self.dialog.listMacros.currentRow()]
            if utils.remove_macro(macro):
                self.dialog.description.setText(translate('AddonsInstaller', 'Macro successfully removed.'))
            else:
                self.dialog.description.setText(translate('AddonsInstaller', 'Macro could not be removed.'))
        self.update_status(soft=True)
        self.addon_removed = True # A value to trigger the restart message on dialog close

    def mark_recompute(self,addon):

        """marks an addon in the list as installed but needs recompute"""

        for i in range(self.dialog.listWorkbenches.count()):
            txt = self.dialog.listWorkbenches.item(i).text().strip()
            if txt.endswith(" ("+translate("AddonsInstaller","Installed")+")"):
                txt = txt[:-12]
            elif txt.endswith(" ("+translate("AddonsInstaller","Update available")+")"):
                txt = txt[:-19]
            if txt == addon:
                from PySide import QtGui
                self.dialog.listWorkbenches.item(i).setText(txt+" ("+translate("AddonsInstaller","Restart required")+")")
                self.dialog.listWorkbenches.item(i).setIcon(QtGui.QIcon(":/icons/edit-undo.svg"))

    def update_status(self,soft=False):

        """Updates the list of workbenches/macros. If soft is true, items
        are not recreated (and therefore display text isn't triggered)"
        """

        moddir = FreeCAD.getUserAppDataDir() + os.sep + "Mod"
        from PySide import QtGui
        if soft:
            for i in range(self.dialog.listWorkbenches.count()):
                txt = self.dialog.listWorkbenches.item(i).text().strip()
                ext = ""
                if txt.endswith(" ("+translate("AddonsInstaller","Installed")+")"):
                    txt = txt[:-12]
                    ext = " ("+translate("AddonsInstaller","Installed")+")"
                elif txt.endswith(" ("+translate("AddonsInstaller","Update available")+")"):
                    txt = txt[:-19]
                    ext = " ("+translate("AddonsInstaller","Update available")+")"
                elif txt.endswith(" ("+translate("AddonsInstaller","Restart required")+")"):
                    txt = txt[:-19]
                    ext = " ("+translate("AddonsInstaller","Restart required")+")"
                if os.path.exists(os.path.join(moddir,txt)):
                    self.dialog.listWorkbenches.item(i).setText(txt+ext)
                else:
                    self.dialog.listWorkbenches.item(i).setText(txt)
                    self.dialog.listWorkbenches.item(i).setIcon(self.get_icon(txt))
            for i in range(self.dialog.listMacros.count()):
                txt = self.dialog.listMacros.item(i).text().strip()
                if txt.endswith(" ("+translate("AddonsInstaller","Installed")+")"):
                    txt = txt[:-12]
                elif txt.endswith(" ("+translate("AddonsInstaller","Update available")+")"):
                    txt = txt[:-19]
                if os.path.exists(os.path.join(moddir,txt)):
                    self.dialog.listMacros.item(i).setText(txt+ext)
                else:
                    self.dialog.listMacros.item(i).setText(txt)
                    self.dialog.listMacros.item(i).setIcon(QtGui.QIcon(":/icons/document-python.svg"))
        else:
            self.dialog.listWorkbenches.clear()
            self.dialog.listMacros.clear()
            for wb in self.repos:
                if os.path.exists(os.path.join(moddir,wb[0])):
                    self.dialog.listWorkbenches.addItem(QtGui.QListWidgetItem(QtGui.QIcon(":/icons/button_valid.svg"),str(wb[0]) + " ("+translate("AddonsInstaller","Installed")+")"))
                    wb[2] = 1
                else:
                    self.dialog.listWorkbenches.addItem(QtGui.QListWidgetItem(QtGui.QIcon(":/icons/document-python.svg"),str(wb[0])))
                    wb[2] = 0
            for macro in self.macros:
                if macro.is_installed():
                    self.dialog.listMacros.addItem(item)
                else:
                    self.dialog.listMacros.addItem(QtGui.QListWidgetItem(QtGui.QIcon(":/icons/document-python.svg"),+macro.name))

    def mark(self,repo):

        """mark a workbench as updatable"""

        from PySide import QtGui
        for i in range(self.dialog.listWorkbenches.count()):
            w = self.dialog.listWorkbenches.item(i)
            if (w.text() == str(repo)) or w.text().startswith(str(repo)+" "):
                w.setText(str(repo) + str(" ("+translate("AddonsInstaller","Update available")+")"))
                w.setForeground(QtGui.QBrush(QtGui.QColor(182,90,0)))
                if not repo in self.doUpdate:
                    self.doUpdate.append(repo)

    def show_config(self):

        """shows the configuration dialog"""

        import FreeCADGui
        from PySide import QtGui
        self.config = FreeCADGui.PySideUic.loadUi(os.path.join(os.path.dirname(__file__),"AddonManagerOptions.ui"))

        # restore stored values
        pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons")
        self.config.checkUpdates.setChecked(pref.GetBool("AutoCheck",False))
        self.config.customRepositories.setPlainText(pref.GetString("CustomRepositories",""))
        self.config.radioButtonNoProxy.setChecked(pref.GetBool("NoProxyCheck",True))
        self.config.radioButtonSystemProxy.setChecked(pref.GetBool("SystemProxyCheck",False))
        self.config.radioButtonUserProxy.setChecked(pref.GetBool("UserProxyCheck",False))
        self.config.userProxy.setPlainText(pref.GetString("ProxyUrl",""))

        # center the dialog over the Addon Manager
        self.config.move(self.dialog.frameGeometry().topLeft() + self.dialog.rect().center() - self.config.rect().center())

        ret = self.config.exec_()

        if ret:
            # OK button has been pressed
            pref.SetBool("AutoCheck",self.config.checkUpdates.isChecked())
            pref.SetString("CustomRepositories",self.config.customRepositories.toPlainText())
            pref.SetBool("NoProxyCheck",self.config.radioButtonNoProxy.isChecked())
            pref.SetBool("SystemProxyCheck",self.config.radioButtonSystemProxy.isChecked())
            pref.SetBool("UserProxyCheck",self.config.radioButtonUserProxy.isChecked())
            pref.SetString("ProxyUrl",self.config.userProxy.toPlainText())

def check_updates(addon_name,callback):

    """Checks for updates for a given addon"""

    oname = "update_checker_"+addon_name
    setattr(FreeCAD,oname,CheckSingleWorker(addon_name))
    getattr(FreeCAD,oname).updateAvailable.connect(callback)
    getattr(FreeCAD,oname).start()

## @}
