import numpy as np
import yaml

from calphy.integrators import *
import pyscal.traj_process as ptp
import calphy.lattice as pl
import calphy.helpers as ph
import calphy.phase as cph
from calphy.errors import *

class Liquid(cph.Phase):
    """
    Class for free energy calculation with liquid as the reference state

    Parameters
    ----------
    options : dict
        dict of input options

    kernel : int
        the index of the calculation that should be run from
        the list of calculations in the input file

    simfolder : string
        base folder for running calculations

    """
    def __init__(self, options=None, kernel=None, simfolder=None):
        """
        Set up class
        """
        #call base class
        super().__init__(options=options,
        kernel=kernel, simfolder=simfolder)

    def run_averaging(self):
        """
        Run averaging routine

        Parameters
        ----------
        None

        Returns
        -------
        None

        Notes
        -----
        Run averaging routine using LAMMPS. Starting from the initial lattice two different routines can
        be followed:
        If pressure is specified, MD simulations are run until the pressure converges within the given
        threshold value.
        If `fix_lattice` option is True, then the input structure is used as it is and the corresponding pressure
        is calculated.
        At the end of the run, the averaged box dimensions are calculated.
        """
        #create lammps object
        lmp = ph.create_object(self.cores, self.simfolder, self.options["md"]["timestep"], self.options["units"], self.options["atom_style"])

        #set up structure
        lmp = ph.create_structure(lmp, self.calc, self.options["units"], self.options["atom_style"])

        #set up potential
        lmp = ph.set_potential(lmp, self.options)

        #Melt regime for the liquid
        # myRand = 6683
        myRand = np.random.randint(0, 10000)
        print(myRand)
        lmp.velocity("all create", int(self.thigh*.75), myRand)
        lmp.command("fix 0 all shake 1e-6 200 0 b 1 a 1")

        #add some computes
        lmp.command("variable         mvol equal vol")
        lmp.command("variable         mlx equal lx")
        lmp.command("variable         mly equal ly")
        lmp.command("variable         mlz equal lz")
        lmp.command("variable         mpress equal press")

        #melting cycle
        #try with different multiples of thmult until the structure melts.
        #TODO : Add option to skip melting cycle

        melted = False

        #this is the multiplier for thigh to try melting routines
        for thmult in np.arange(0.5, 2.0, 0.1):

            trajfile = os.path.join(self.simfolder, "traj.melt")
            if os.path.exists(trajfile):
                os.remove(trajfile)

            self.logger.info("Starting melting cycle with thigh temp %f, factor %f"%(self.thigh, thmult))
            lmp.fix("1 all npt temp", self.thigh*thmult, self.thigh*thmult, self.options["md"]["tdamp"],
                                          "iso", self.p, self.p, self.options["md"]["pdamp"])
            lmp.run(int(self.options["md"]["nsmall"]))
            lmp.unfix(1)
            lmp.dump("2 all custom", 1, trajfile,"id type mass x y z vx vy vz")
            lmp.run(0)
            lmp.undump(2)

            #we have to check if the structure melted
            solids = ph.find_solid_fraction(os.path.join(self.simfolder, "traj.melt"))
            self.logger.info("fraction of solids found: %f", solids/self.natoms)
            if (solids/self.natoms < self.options["conv"]["liquid_frac"]):
                melted = True
                break

        #if melting cycle is over and still not melted, raise error
        if not melted:
            lmp.close()
            raise SolidifiedError("Liquid system did not melt, maybe try a higher thigh temperature.")

        #now assign correct temperature and equilibrate
        lmp.velocity("all create", self.t, np.random.randint(0, 10000))
        lmp.fix("1 all npt temp", self.t, self.t, self.options["md"]["tdamp"],
                                      "iso", self.p, self.p, self.options["md"]["pdamp"])
        lmp.run(int(self.options["md"]["nsmall"]))

        #start recording average values
        lmp.command("fix              2 all ave/time %d %d %d v_mlx v_mly v_mlz v_mpress file avg.dat"%(int(self.options["md"]["nevery"]),
            int(self.options["md"]["nrepeat"]), int(self.options["md"]["nevery"]*self.options["md"]["nrepeat"])))

        #monitor the average values until calculation is converged
        laststd = 0.00
        converged = False

        for i in range(self.options["md"]["ncycles"]):
            lmp.command("run              %d"%int(self.options["md"]["nsmall"]))
            ncount = int(self.options["md"]["nsmall"])//int(self.options["md"]["nevery"]*self.options["md"]["nrepeat"])
            #now we can check if it converted
            file = os.path.join(self.simfolder, "avg.dat")
            lx, ly, lz, ipress = np.loadtxt(file, usecols=(1,2,3,4), unpack=True)

            lxpc = ipress
            mean = np.mean(lxpc)
            std = np.std(lxpc)
            volatom = np.mean((lx*ly*lz)/self.natoms)
            self.logger.info("At count %d mean pressure is %f with vol/atom %f"%(i+1, mean, volatom))

            lmp.command("dump              2 all custom 1 traj.dat id type mass x y z")
            lmp.command("run               0")
            lmp.command("undump            2")
            solids = ph.find_solid_fraction(os.path.join(self.simfolder, "traj.dat"))
            self.logger.info("fraction of solids found: %f", solids/self.natoms)
            if (solids/self.natoms > self.options["conv"]["liquid_frac"]):
                lmp.close()
                raise SolidifiedError('System solidified, increase temperature')

            #check melting;
            if (np.abs(mean - self.p)) < self.options["conv"]["p_tol"]:
                #process other means
                self.lx = np.round(np.mean(lx[-ncount+1:]), decimals=3)
                self.ly = np.round(np.mean(ly[-ncount+1:]), decimals=3)
                self.lz = np.round(np.mean(lz[-ncount+1:]), decimals=3)
                self.volatom = volatom
                self.vol = self.lx*self.ly*self.lz
                self.rho = self.natoms/(self.lx*self.ly*self.lz)

                self.logger.info("finalized vol/atom %f at pressure %f"%(self.volatom, mean))
                self.logger.info("Avg box dimensions x: %f, y: %f, z:%f"%(self.lx, self.ly, self.lz))
                converged = True
                break

            laststd = std

        if not converged:
            lmp.close()
            raise ValueError("Pressure did not converge after MD runs, maybe change lattice_constant and try?")

        lmp.command("dump              2 all custom 1 traj.dat id type mass x y z vx vy vz")
        lmp.command("run               0")
        lmp.command("undump            2")
        lmp.command("write_data dump.lmpdata")
        lmp.command("write_restart restart.rst")

        #finish run and close object
        lmp.close()

        #process the trajectory
        self.process_traj()

    def run_integration(self, iteration=1):
        """
        Run integration routine

        Parameters
        ----------
        iteration : int, optional
            iteration number for running independent iterations

        Returns
        -------
        None

        Notes
        -----
        Run the integration routine where the initial and final systems are connected using
        the lambda parameter. See algorithm 4 in publication.
        """
        lmp = ph.create_object(self.cores, self.simfolder, self.options["md"]["timestep"], self.options["units"], self.options["atom_style"])

        lmp.command("read_restart restart.rst")
        lmp.command("fix 0 all shake 1e-6 200 0 b 1 a 1")

        # Adiabatic switching parameters.
        lmp.command("variable        li       equal   1.0")
        lmp.command("variable        lf       equal   0.0")

        #read in the conf file
        conf = os.path.join(self.simfolder, "conf.dump")
        # lmp = ph.read_dump(lmp, conf, species=self.options["nelements"])

        #set hybrid ufm and normal potential
        #lmp = ph.set_hybrid_potential(lmp, self.options, self.eps)
        lmp = ph.set_potential(lmp, self.options)

        #remap the box to get the correct pressure
        lmp = ph.remap_box(lmp, self.lx, self.ly, self.lz)

        lmp.command("fix              f1 all nve")
        lmp.command("fix              f2 all langevin %f %f %f %d zero yes"%(self.t, self.t, self.options["md"]["tdamp"],
                                        np.random.randint(0, 10000)))

        lmp.command("run               %d"%self.options["md"]["te"])

        lmp.command("unfix            f1")
        lmp.command("unfix            f2")

        #---------------------------------------------------------------
        # FWD cycle
        #---------------------------------------------------------------

        lmp.command("variable         flambda equal ramp(${li},${lf})")
        lmp.command("variable         blambda equal 1.0-v_flambda")

        lmp.command("dump dumpxyz all custom 1 tip4p_ufm_traj.dump type x y z")

        lmp.command("pair_style       hybrid/scaled v_flambda %s v_blambda ufm 7.5"%self.options["md"]["pair_style"])

        for pc in self.options["md"]["pair_coeff"]:
            pcraw = pc.split()
            pcnew = " ".join([*pcraw[:2], self.options["md"]["pair_style"].split()[0], *pcraw[2:]])
            lmp.pair_coeff(pcnew)

        # pc =  self.options["md"]["pair_coeff"]
        # pcraw = pc.split()
        # pcnew = " ".join([*pcraw[:2], *[self.options["md"]["pair_style"],], *pcraw[2:]])

        # lmp.command("pair_coeff       %s"%pcnew)
        lmp.command("pair_coeff       * * ufm %f 1.5"%self.eps)

        lmp.command("compute          c1 all pair %s"%self.options["md"]["pair_style"].split()[0])
        lmp.command("compute          c2 all pair ufm")

        lmp.command("variable         step equal step")
        lmp.command("variable         dU1 equal c_c1/atoms")
        lmp.command("variable         dU2 equal c_c2/atoms")

        lmp.command("thermo_style     custom step v_dU1 v_dU2")
        lmp.command("thermo           100")

        # lmp.command("velocity         all create %f %d mom yes rot yes dist gaussian"%(self.t, np.random.randint(0, 10000)))

        lmp.command("fix              f1 all nve")
        lmp.command("fix              f2 all langevin %f %f %f %d zero yes"%(self.t, self.t, self.options["md"]["tdamp"],
                                        np.random.randint(0, 10000)))
        lmp.command("compute          Tcm all temp/com")
        lmp.command("fix_modify       f2 temp Tcm")

        lmp.command("fix              f3 all print 1 \"${dU1} ${dU2} ${flambda}\" screen no file forward_%d.dat"%iteration)
        lmp.command("run               %d"%self.options["md"]["ts"])

        lmp.command("unfix            f1")
        lmp.command("unfix            f2")
        lmp.command("unfix            f3")
        lmp.command("uncompute        c1")
        lmp.command("uncompute        c2")

        #---------------------------------------------------------------
        # EQBRM cycle
        #---------------------------------------------------------------

        lmp.command("pair_style       ufm 7.5")
        lmp.command("pair_coeff       * * %f 1.5"%self.eps)

        if self.options["md"]["kspace_style"]: lmp.kspace_style("none")

        lmp.command("thermo_style     custom step pe")
        lmp.command("thermo           100")

        lmp.command("fix              f1 all nve")
        lmp.command("fix              f2 all langevin %f %f %f %d zero yes"%(self.t, self.t, self.options["md"]["tdamp"],
                                        np.random.randint(0, 10000)))
        lmp.command("fix_modify       f2 temp Tcm")

        lmp.command("run               %d"%self.options["md"]["te"])

        lmp.command("unfix            f1")
        lmp.command("unfix            f2")

        #---------------------------------------------------------------
        # BKD cycle
        #---------------------------------------------------------------

        lmp.command("variable         flambda equal ramp(${lf},${li})")
        lmp.command("variable         blambda equal 1.0-v_flambda")

        lmp.command("pair_style       hybrid/scaled v_flambda %s v_blambda ufm 7.5"%self.options["md"]["pair_style"])

        for pc in self.options["md"]["pair_coeff"]:
            pcraw = pc.split()
            pcnew = " ".join([*pcraw[:2], self.options["md"]["pair_style"].split()[0], *pcraw[2:]])
            lmp.pair_coeff(pcnew)
        # lmp.command("pair_coeff       %s"%pcnew)

        if self.options["md"]["kspace_style"]: lmp.kspace_style(self.options["md"]["kspace_style"])

        lmp.command("pair_coeff       * * ufm %f 1.5"%self.eps)

        lmp.command("compute          c1 all pair %s"%self.options["md"]["pair_style"].split()[0])
        lmp.command("compute          c2 all pair ufm")

        lmp.command("variable         step equal step")
        lmp.command("variable         dU1 equal c_c1/atoms")
        lmp.command("variable         dU2 equal c_c2/atoms")

        lmp.command("thermo_style     custom step v_dU1 v_dU2")
        lmp.command("thermo           100")

        lmp.command("fix              f1 all nve")
        lmp.command("fix              f2 all langevin %f %f %f %d zero yes"%(self.t, self.t, self.options["md"]["tdamp"],
                                        np.random.randint(0, 10000)))
        lmp.command("fix_modify       f2 temp Tcm")

        lmp.command("fix              f3 all print 1 \"${dU1} ${dU2} ${flambda}\" screen no file backward_%d.dat"%iteration)
        lmp.command("run               %d"%self.options["md"]["ts"])

        lmp.command("unfix            f1")
        lmp.command("unfix            f2")
        lmp.command("unfix            f3")
        lmp.command("uncompute        c1")
        lmp.command("uncompute        c2")

        #close object
        lmp.close()

    def thermodynamic_integration(self):
        """
        Calculate free energy after integration step

        Parameters
        ----------
        None

        Returns
        -------
        None

        Notes
        -----
        Calculates the final work, energy dissipation and free energy by
        matching with UFM model
        """
        w, q, qerr = find_w(self.simfolder, nsims=self.nsims,
            full=True, solid=False)

        #TODO: Hardcoded UFM parameters - enable option to change
        f1 = get_uhlenbeck_ford_fe(self.t,
            self.rho, 50, 1.5)

        #Get ideal gas fe
        f2 = get_ideal_gas_fe(self.t, self.rho,
            self.natoms, self.options["mass"], self.concentration)

        self.ferr = qerr
        self.fref = f1
        self.fideal = f2
        self.w = w

        #add pressure contribution if required
        if self.p != 0:
            p = self.p/(10000*160.21766208)
            v = self.vol/self.natoms
            self.pv = p*v
        else:
            self.pv = 0

        #calculate final free energy
        self.fe = self.fideal + self.fref - self.w + self.pv


# TODO Error Handling & Reporting
# TODO Platform Compatibility
# TODO GUI Polishing
# TODO Code comments to each functionality
# TODO Start, Stop and Resume functionality
# TODO Proper indication of started scanning operations
# TODO Faster port scanning if possible
# TODO App name and icon changes and complete .exe

import subprocess
import sys
import csv
from subprocess import check_output
import requests
from PyQt5.QtCore import QThread, pyqtSignal, Qt, QObject
from PyQt5.uic import loadUi
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QApplication, QMainWindow
import threading
import nmap

class IP:
    def __init__(self, ip, hostname, mac, manufacturer, osys, status):
        self.ip = ip
        self.hostname = hostname
        self.mac = mac
        self.manufacturer = manufacturer
        self.os = osys
        self.status = status

class Pass:
    def __int__(self, ip, command, range, port, method):
        self.ip = ip
        self.command = command
        self.range = range
        self.port = port
        self.method = method

class Port:
    def __init__(self, port, name, product, version):
        self.port = port
        self.name = name
        self.product = product
        self.version = version

def run():
    app = QApplication(sys.argv)
    w = MainWindow()
    widget = QtWidgets.QStackedWidget()
    widget.addWidget(w)
    widget.setGeometry(300, 100, 1400, 900)
    widget.show()
    try:
        sys.exit(app.exec_())
    except:
        print("Exiting")

parent_threads = []
threads = []

# noinspection PyUnresolvedReferences
class MainThread(QThread):

    update = pyqtSignal(int)

    def __init__(self, *args):
        super(QThread, self).__init__()
        if len(args) == 3:
            self.__w = args[0]
            self.__subnet = args[1]
            self.__p = args[2]
            self.__arg = len(args)
        elif len(args) == 6:
            self.__w = args[0]
            self.__ip = args[1]
            self.__command = args[2]
            self.__range = args[3]
            self.__port = args[4]
            self.__method = args[5]
            self.__arg = len(args)

    def run(self):
        if self.__arg == 3:
            val = MainWindow.ping(self.__w, self.__p, self.__subnet)
            self.update.emit(val)
        elif self.__arg == 6:
            if self.__method == 0:
                val = MainWindow.port_scan_all(self.__w, self.__ip, self.__command)
                self.update.emit(val)
            elif self.__method == 1:
                val = MainWindow.port_scan_singal(self.__w, self.__ip, self.__port, self.__command)
                self.update.emit(val)
            elif self.__method == 2:
                val = MainWindow.port_scan_range(self.__w, self.__ip, self.__range, self.__command)
                self.update.emit(val)
            elif self.__method == 3:
                val = MainWindow.port_scan_common(self.__w, self.__ip, self.__command)
                self.update.emit(val)

# noinspection PyUnresolvedReferences
class MainWindow(QMainWindow):
    p_val = []
    ips = []

    def __init__(self):
        super(MainWindow, self).__init__()
        loadUi("App.ui", self)
        self.pushButton.clicked.connect(self.scan)
        self.pushButton_2.clicked.connect(self.port_scan)
        self.actionStatus.changed.connect(self.hide_status)
        self.actionIP.changed.connect(self.hide_ip)
        self.actionMac.changed.connect(self.hide_mac)
        self.actionHost_Name.changed.connect(self.hide_host_name)
        self.actionOS.changed.connect(self.hide_os)
        self.actionManufacturer.changed.connect(self.hide_manu)
        self.actionTime.changed.connect(self.hide_time)
        self.comboBox.currentIndexChanged.connect(self.selected_ports_disabler)
        self.actionDownload_as_csv_file.triggered.connect(self.download)
        self.actionAbout.triggered.connect(self.about)

    def about(self):
        msg = QtWidgets.QMessageBox()
        msg.setWindowTitle('About')
        msg.setGeometry(800, 500, 900, 900)
        msg.setText('This is a Ping Sweep program with some port scanning functionalities and a GUI')
        msg.setIcon(QtWidgets.QMessageBox.Information)
        x = msg.exec_()

    def download(self):
        header = ['', 'Status', 'IP', 'Mac', 'Host Name', 'OS', 'Manufacturer']
        f = open('ping_data.csv', 'w', newline='')
        writer = csv.writer(f)
        writer.writerow(header)
        for i in range(len(MainWindow.ips)):
            x = self.treeWidget.topLevelItem(i)
            row = []
            for p in range(7):
                row.append(x.text(p))
            writer.writerow(row)
            cc = x.childCount()
            if cc > 0:
                for q in range(cc):
                    ch = x.child(q)
                    row = []
                    for p in range(4):
                        row.append(ch.text(p))
                    writer.writerow(row)
        f.close()

    def selected_ports_disabler(self):
        if self.comboBox.currentIndex() == 1:
            self.spinBox_3.setEnabled(True)
            self.spinBox.setEnabled(False)
            self.spinBox_2.setEnabled(False)
        elif self.comboBox.currentIndex() == 2:
            self.spinBox_3.setEnabled(False)
            self.spinBox.setEnabled(True)
            self.spinBox_2.setEnabled(True)
        else:
            self.spinBox_3.setEnabled(False)
            self.spinBox.setEnabled(False)
            self.spinBox_2.setEnabled(False)

    def hide_status(self):
        if not self.actionStatus.isChecked():
            self.treeWidget.setColumnHidden(1, True)
        else:
            self.treeWidget.setColumnHidden(1, False)

    def hide_ip(self):
        if not self.actionIP.isChecked():
            self.treeWidget.setColumnHidden(2, True)
        else:
            self.treeWidget.setColumnHidden(2, False)

    def hide_mac(self):
        if not self.actionMac.isChecked():
            self.treeWidget.setColumnHidden(3, True)
        else:
            self.treeWidget.setColumnHidden(3, False)

    def hide_host_name(self):
        if not self.actionHost_Name.isChecked():
            self.treeWidget.setColumnHidden(4, True)
        else:
            self.treeWidget.setColumnHidden(4, False)

    def hide_os(self):
        if not self.actionOS.isChecked():
            self.treeWidget.setColumnHidden(5, True)
        else:
            self.treeWidget.setColumnHidden(5, False)

    def hide_manu(self):
        if not self.actionManufacturer.isChecked():
            self.treeWidget.setColumnHidden(6, True)
        else:
            self.treeWidget.setColumnHidden(6, False)

    def hide_time(self):
        if not self.actionTime.isChecked():
            self.treeWidget.setColumnHidden(7, True)
        else:
            self.treeWidget.setColumnHidden(7, False)

    def scan(self):
        MainWindow.p_val = []
        MainWindow.ips = []
        self.pushButton.setEnabled(False)
        self.pushButton_2.setEnabled(False)
        self.textEdit.clear()
        self.pBar.reset()
        self.pBar.setValue(0)
        self.treeWidget.clear()
        config = check_output('ipconfig /all', universal_newlines=True)
        networks = config.split('IPv4 Address. . . . . . . . . . . :')
        maxp = (len(networks) - 1) * 255
        self.pBar.setMaximum(maxp)
        for i in range(len(networks) - 1):
            tt = threading.Thread(target=MainWindow.multi_network, args=[self, networks, i])
            tt.start()

    def port_scan(self):
        MainWindow.p_val = []
        selected_ips = []
        self.pushButton.setEnabled(False)
        self.pushButton_2.setEnabled(False)
        self.pBar.reset()
        self.pBar.setValue(0)
        self.pBar.setMaximum(len(MainWindow.ips))
        ai = self.comboBox_3.currentText()
        method = self.comboBox_2.currentIndex()
        command = ''
        if method == 0:
            command = '-sT'
        elif method == 1:
            command = '-sS'
        elif method == 2:
            command = '-sX'
        elif method == 3:
            command = '-sA'
        elif method == 4:
            command = '-sU'
        selected = self.comboBox.currentText()
        if ai == 'All Hosts':
            if selected == 'all':
                for ip in MainWindow.ips:
                    tht = MainThread(self, ip, command, '', '', 0)
                    tht.update.connect(self.evt_update)
                    tht.start()
                    tht.wait(30)
            elif selected == 'singal port':
                for ip in MainWindow.ips:
                    p = self.spinBox_3.value()
                    tht = MainThread(self, ip, command, '', p, 1)
                    tht.update.connect(self.evt_update)
                    tht.start()
                    tht.wait(30)
            elif selected == 'range':
                for ip in MainWindow.ips:
                    fp = self.spinBox.value()
                    lp = self.spinBox_2.value()
                    range = ''
                    if fp < lp:
                        range = str(fp) + '-' + str(lp)
                    elif lp > fp:
                        range = str(lp) + '-' + str(fp)
                    elif fp == lp:
                        range = str(lp)
                    tht = MainThread(self, ip, command, range, '', 2)
                    tht.update.connect(self.evt_update)
                    tht.start()
                    tht.wait(30)
            elif selected == 'common':
                for ip in MainWindow.ips:
                    tht = MainThread(self, ip, command, '', '', 3)
                    tht.update.connect(self.evt_update)
                    tht.start()
                    tht.wait(30)
        elif ai == 'Individual Hosts':
            for ip in MainWindow.ips:
                t = self.treeWidget.findItems(str(ip), Qt.MatchFlag.MatchExactly, 2)[0]
                if t.checkState(0) == 2:
                    selected_ips.append(ip)
            self.pBar.setMaximum(len(selected_ips))
            if selected == 'all':
                for ip in selected_ips:
                    tht = MainThread(self, ip, command, '', '', 0)
                    tht.update.connect(self.evt_update)
                    tht.start()
                    tht.wait(30)
            elif selected == 'singal port':
                for ip in selected_ips:
                    p = self.spinBox_3.value()
                    tht = MainThread(self, ip, command, '', p, 1)
                    tht.update.connect(self.evt_update)
                    tht.start()
                    tht.wait(30)
            elif selected == 'range':
                for ip in selected_ips:
                    fp = self.spinBox.value()
                    lp = self.spinBox_2.value()
                    range = ''
                    if fp < lp:
                        range = str(fp) + '-' + str(lp)
                    elif lp > fp:
                        range = str(lp) + '-' + str(fp)
                    elif fp == lp:
                        range = str(lp)
                    tht = MainThread(self, ip, command, range, '', 2)
                    tht.update.connect(self.evt_update)
                    tht.start()
                    tht.wait(30)
            elif selected == 'common':
                for ip in selected_ips:
                    tht = MainThread(self, ip, command, '', '', 3)
                    tht.update.connect(self.evt_update)
                    tht.start()
                    tht.wait(30)

    def port_scan_all(self, ip, method):
        out = False
        args = 'nmap ' + method + ' -sV -p 1-65535 -O -Pn'
        nm = nmap.PortScanner()
        try:
            nm.scan(hosts=ip, arguments=args, timeout=900)
        except nmap.PortScannerTimeout as exc:
            print(ip + '  Scan Complete is timed out')
            return 0
        xx = nm.scanstats()
        timee = xx['elapsed']
        if len(nm.all_hosts()) == 0:
            out = True
        if out:
            print(ip + '  Scan Complete is out')
            return 0
        if not out:
            for proto in nm[ip].all_protocols():
                for port in nm[ip].all_tcp():
                    if nm[ip][proto][port]['state'] == 'open' or nm[ip][proto][port]['state'] == 'filtered':
                        pp = Port(port, nm[ip]['tcp'][int(port)]['name'], nm[ip]['tcp'][int(port)]['product'],
                                  nm[ip]['tcp'][int(port)]['version'])
                        os = nm[ip]['osmatch'][0]['name']
                        MainWindow.add_child(self, ip, pp, timee, os)
                        print('done  ' + ip + ' ' + str(port))
            print(ip + '  Scan Complete')
            return 0

    def port_scan_range(self, ip, range, method):
        out = False
        args = 'nmap ' + method + ' -sV -p ' + str(range) + ' -O -Pn'
        nm = nmap.PortScanner()
        try:
            nm.scan(hosts=ip, arguments=args, timeout=700)
        except nmap.PortScannerTimeout as exc:
            print(ip + '  Scan Complete is timed out')
            return 0
        xx = nm.scanstats()
        timee = xx['elapsed']
        if len(nm.all_hosts()) == 0:
            out = True
        if out:
            print(ip + '  Scan Complete is out')
            return 0
        if not out:
            for proto in nm[ip].all_protocols():
                for port in nm[ip].all_tcp():
                    if nm[ip][proto][port]['state'] == 'open' or nm[ip][proto][port]['state'] == 'filtered':
                        pp = Port(port, nm[ip]['tcp'][int(port)]['name'], nm[ip]['tcp'][int(port)]['product'],
                                  nm[ip]['tcp'][int(port)]['version'])
                        os = nm[ip]['osmatch'][0]['name']
                        MainWindow.add_child(self, ip, pp, timee, os)
                        print('done  ' + ip + ' ' + str(port))
            print(ip + '  Scan Complete')
            return 0

    def port_scan_common(self, ip, method):
        out = False
        args = 'nmap ' + method + ' -sV --top-ports 1000 -O -Pn'
        nm = nmap.PortScanner()
        try:
            nm.scan(hosts=ip, arguments=args, timeout=600)
        except nmap.PortScannerTimeout as exc:
            print(ip + '  Scan Complete is timed out')
            return 0
        xx = nm.scanstats()
        timee = xx['elapsed']
        if len(nm.all_hosts()) == 0:
            out = True
        if out:
            print(ip + '  Scan Complete is out')
            return 0
        if not out:
            for proto in nm[ip].all_protocols():
                for port in nm[ip].all_tcp():
                    if nm[ip][proto][port]['state'] == 'open' or nm[ip][proto][port]['state'] == 'filtered':
                        pp = Port(port, nm[ip]['tcp'][int(port)]['name'], nm[ip]['tcp'][int(port)]['product'],
                                  nm[ip]['tcp'][int(port)]['version'])
                        os = nm[ip]['osmatch'][0]['name']
                        MainWindow.add_child(self, ip, pp, timee, os)
                        print('done  ' + ip + ' ' + str(port))
            print(ip + '  Scan Complete')
            return 0

    def port_scan_singal(self, ip, p, method):
        out = False
        args = 'nmap ' + method + ' -sV -p ' + str(p) + ' -O -Pn'
        nm = nmap.PortScanner()
        try:
            nm.scan(hosts=ip, arguments=args, timeout=150)
        except nmap.PortScannerTimeout as exc:
            print(ip + '  Scan Complete is timed out')
            return 0
        xx = nm.scanstats()
        timee = xx['elapsed']
        if len(nm.all_hosts()) == 0:
            out = True
        if out:
            print(ip + '  Scan Complete is out')
            return 0
        if not out:
            for proto in nm[ip].all_protocols():
                for port in nm[ip].all_tcp():
                    if nm[ip][proto][port]['state'] == 'open':
                        pp = Port(port, nm[ip]['tcp'][int(port)]['name'], nm[ip]['tcp'][int(port)]['product'],
                                  nm[ip]['tcp'][int(port)]['version'])
                        os = nm[ip]['osmatch'][0]['name']
                        MainWindow.add_child(self, ip, pp, timee, os)
                        print('done  ' + ip + ' ' + str(port))
        print(ip + '  Scan Complete')
        return 0

    def evt_update(self, val):
        MainWindow.p_val.append(val)
        self.pBar.setValue(len(MainWindow.p_val))
        if self.pBar.value() == self.pBar.maximum():
            self.pBar.reset()
            self.pBar.setValue(0)
            self.pushButton.setEnabled(True)
            self.pushButton_2.setEnabled(True)

    def add_row(self, entry):
        item = QtWidgets.QTreeWidgetItem(self.treeWidget)
        item.setCheckState(0, Qt.CheckState.Unchecked)
        item.setText(1, entry.status)
        item.setText(2, entry.ip)
        item.setText(3, entry.mac)
        item.setText(4, entry.hostname)
        item.setText(5, entry.os)
        item.setText(6, entry.manufacturer)
        MainWindow.ips.append(entry.ip)

    def add_child(self, ip, entry, time, os):
        t = self.treeWidget.findItems(str(ip), Qt.MatchFlag.MatchExactly, 2)[0]
        t.setText(5, os)
        t.setText(7, time)
        item = QtWidgets.QTreeWidgetItem(t)
        item.setText(1, str(entry.port))
        item.setText(2, entry.name)
        item.setText(3, entry.product)
        item.setText(4, entry.version)

    def ping(self, p, subnet):
        status = 'alive'
        try:
            o = check_output('ping -a ' + str(subnet) + str(p) + ' -n 1 ', universal_newlines=True)
        except subprocess.CalledProcessError as exc:
            o = exc.output
            a = check_output('arp -a', universal_newlines=True)
            if a.find(str(subnet) + str(p) + ' ') > -1:
                status = status + '/filtered'
            else:
                return 0
        if o.find('unreachable') > -1:
            return 0
        try:
            r = o.split('Pinging ')
            t = r[1].rsplit(' with')
            ih = t[0]
            h = ih.split(' [')
            if len(h) == 2:
                host = h[0]
                q = h[1].rsplit(']')
                ip = q[0]
            else:
                host = 'Unknown'
                ip = h[0]

            if status == 'alive':
                r = t[1].split('TTL=')
                t = r[1].rsplit('Ping ')
                ttl = t[0]
                if int(ttl) == 32:
                    os = 'Windows 95/98/ME'
                elif int(ttl) == 64:
                    os = 'Linux/Unix'
                elif int(ttl) == 128:
                    os = 'Windows XP/7/8/2003/2008'
                elif int(ttl) == 255:
                    os = 'Solaris'
            else:
                os = 'Unknown'
            j = check_output('ipconfig /all', universal_newlines=True)
            tmp1 = j.split('IPv4 Address. . . . . . . . . . . :')
            for i in range(len(tmp1) - 1):
                tmp2 = tmp1[i + 1].splitlines()
                tmp3 = tmp2[0].replace(" ", "").split('.')
                tmp4 = tmp3[3].split('(')
                tmp3[3] = tmp4[0]
                tmp5 = '.'.join(tmp3)
                if ip == tmp5:
                    mainIp = tmp5
                    break
                else:
                    mainIp = ''
            a = check_output('arp -a', universal_newlines=True)
            if ip == mainIp:
                j = check_output('ipconfig /all', universal_newlines=True)
                tmp1 = j.split(str(ip))
                tmp2 = tmp1[0].rsplit('Physical Address. . . . . . . . . :', 1)
                tmp3 = tmp2[1].splitlines()
                mac = ''.join(tmp3[0].split())
                response = requests.request("GET", 'https://api.maclookup.app/v2/macs/' + mac)
                if response.status_code == 200:
                    tmp1 = response.text.split('company')
                    tmp2 = tmp1[1].split(',', 1)
                    tmp3 = tmp2[0].split('":"')
                    tmp4 = tmp3[1].split('"')
                    company = tmp4[0]
                else:
                    company = 'Unknown'
                entry = IP(str(ip), host, mac, company, os, status)
                MainWindow.add_row(self, entry)
                return 0
            v = a.split(ip + ' ')
            m = v[1].split('dynamic')
            ma = m[0]
            mac = ma.replace(" ", "")
            response = requests.request("GET", 'https://api.maclookup.app/v2/macs/' + mac)
            if response.status_code == 200:
                tmp1 = response.text.split('company')
                tmp2 = tmp1[1].split(',', 1)
                tmp3 = tmp2[0].split('":"')
                tmp4 = tmp3[1].split('"')
                company = tmp4[0]
                if company == '':
                    company = 'Unknown'
            else:
                company = 'Unknown'
            entry = IP(str(ip), host, mac, company, os, status)
            MainWindow.add_row(self, entry)
            return 0
        except IndexError:
            print('An out of bounds index error has accord')

    def multi_network(self, networks, i):
        ipv4 = networks[i + 1].splitlines()
        mask = ipv4[0].replace(" ", "").split('.')
        mask[3] = ''
        subnet = '.'.join(mask)
        self.textEdit.insertPlainText(subnet + '0/24  ')
        for x in range(255):
            mt = MainThread(self, subnet, x)
            mt.update.connect(self.evt_update)
            mt.start()
            threads.append(mt)
            mt.wait(15)

t = threading.Thread(target=run)
t.start()


import json
import logging

from django.http import HttpResponse, HttpResponseBadRequest
from django.utils.decorators import method_decorator
from django.utils.encoding import force_text
from django.views.decorators.csrf import csrf_exempt
from django.views.generic import View

from closeio import utils

from . import signals

logger = logging.getLogger(__name__)

class CloseIOWebHook(View):
    @method_decorator(csrf_exempt)
    def dispatch(self, *args, **kwargs):
        return super(CloseIOWebHook, self).dispatch(*args, **kwargs)

    def post(self, request, *args, **kwargs):
        try:
            query = json.loads(force_text(request.body))
        except (ValueError, TypeError):
            logger.exception("CloseIO webhook request could not be parsed.")
            return HttpResponseBadRequest()

        try:
            event = query['event']
            model = query['model']
            data = utils.parse(query['data'])
        except KeyError:
            logger.exception("CloseIO webhook request could not be dispatched.")
            return HttpResponseBadRequest()

        data_to_send = dict(
            instance=data
        )

        if event == 'create':
            signals.closeio_create.send(
                sender=self.__class__,
                model=model,
                **data_to_send
            )

        elif event == 'update':
            signals.closeio_update.send(
                sender=self.__class__,
                model=model,
                **data_to_send
            )

        elif event == 'delete':
            data_to_send = dict(
                instance_id=data.get('id', '')
            )
            signals.closeio_delete.send(
                sender=self.__class__,
                model=model,
                **data_to_send
            )

        elif event == 'merge':
            data_to_send = dict(
                source_id=data.get('source_id', ''),
                destination_id=data.get('destination_id', ''),
            )
            signals.closeio_merge.send(
                sender=self.__class__,
                model=model,
                **data_to_send
            )

        expl_signal_name = '%s_%s' % (model, event)

        if hasattr(signals, expl_signal_name):
            signal = getattr(signals, expl_signal_name)
            signal.send(
                sender=self.__class__,
                **data_to_send
            )

        signals.closeio_event.send(
            sender=self.__class__,
            model=model,
            event=event,
            instance=data,
        )

        return HttpResponse()


from json import load, dump

from data.config import ADMIN_ID

class UserNotFoundError(Exception):
    pass

async def get_users() -> dict:
    with open('data/users.json', 'r', encoding='utf-8') as f: return load(f)

async def get_admins(skip_cadmin: bool = False) -> dict:
    admins = {}
    with open('data/users.json', 'r', encoding='utf-8') as f:
        data = load(f)
        for user_id in data.keys():
            if data[user_id]["admin"]:
                admins[user_id] = data[user_id]
        if skip_cadmin: admins.pop(str(ADMIN_ID))
        return admins

async def add_user(user_id: str, username: str, admin: bool = False):
    with open('data/users.json',  'r+', encoding='utf-8') as f:
        data = load(f)
        data[user_id] = {"username": username, "admin": admin}
        f.seek(0)
        dump(data, f, indent=4, ensure_ascii=False)
        f.truncate()

async def delete_admin(user_id: str):
    with open('data/users.json', 'r+', encoding='utf-8') as f:
        data = load(f)
        data[user_id]["admin"] = False
        f.seek(0)
        dump(data, f, indent=4, ensure_ascii=False)
        f.truncate()

async def give_admin(user_id: str):
    try:
        with open('data/users.json', 'r+', encoding='utf-8') as f:
            data = load(f)
            data[user_id]["admin"] = True
            f.seek(0)
            dump(data, f, indent=4, ensure_ascii=False)
            f.truncate()
            return True

    except KeyError:
        return False


import time
from flask import Flask, Response, request, redirect, url_for, session
from flask_cors import CORS
import json
import logging
import os
from flask_dance.contrib.google import make_google_blueprint, google
from oauthlib.oauth2.rfc6749.errors import TokenExpiredError
import re

from application_services.CommentsResource.comment_service import CommentResource
from application_services.UsersResource.user_addr_service import UserAddrResource
from application_services.UsersResource.user_service import UserResource
from application_services.AppHTTPStatus import AppHTTPStatus
# from application_services.smarty_address_service import SmartyAddressService
from database_services.RDBService import RDBService as RDBService

# from flask_dance.contrib.google import make_google_blueprint, google
# import middleware.simple_security as simple_security
from middleware.notification import NotificationMiddlewareHandler as NotificationMiddlewareHandler
from middleware.steamsignin import SteamSignIn

import middleware.security as security

logging.basicConfig(level=logging.DEBUG)

logger = logging.getLogger()
logger.setLevel(logging.INFO)

# pagination data
OFFSET = 0
MAXLIMIT = 10 #20

app = Flask(__name__)
CORS(app)
app.secret_key = "supersekrit"
os.environ['steawmpowered_key'] = 'BBC837ECFD8415A58B7575D83BCDA639'
@app.errorhandler(404)
def not_found(e):
    rsp = Response(response=json.dumps({"ERROR": "404 NOT FOUND"}, default=str, indent=4), status=404,
                   content_type="application/json")
    return rsp

@app.errorhandler(500)
def messy_error(e):
    print(e)
    rsp = Response(json.dumps({"ERROR": "500 WEIRD SERVER ERROR"}, default=str, indent=4), status=500,
                   content_type="application/json")
    return rsp

# help function for pagination
def handle_links(url, offset, limit):
    if "?" not in url:
        url += "?offset=" + str(offset) + "&limit=" + str(limit)
    else:
        if "offset" not in url:
            url = url + "&offset=" + str(offset)
        if "limit" not in url:
            url = url + "&limit=" + str(limit)
    links = []
    nexturl = re.sub("offset=\d+", "offset=" + str(offset + limit), url)
    prevurl = re.sub("offset=\d+", "offset=" + str(max(0, offset - limit)), url)
    links.append({"rel": "self", "href": url})
    links.append({"rel": "next", "href": nexturl})
    links.append({"rel": "prev", "href": prevurl})
    return links

@app.route('/')
def hello_world():
    return '<u>Hello World!</u>'

@app.route('/comments', methods=['GET', 'POST'])
def get_comments():
    if request.method == 'GET':
        offset = int(request.args.get("offset", OFFSET))
        limit = int(request.args.get("limit", MAXLIMIT))
        if limit > MAXLIMIT:
            limit = MAXLIMIT
        query_parms = dict()
        arg_list = [i for i in request.args.keys()]
        for i in arg_list:
            if i.lower() != "offset" and i.lower() != "limit":
                query_parms[i] = request.args.get(i)
        data, exception_res = CommentResource.find_by_template(query_parms, limit, offset)
        links = handle_links(request.url, offset, limit)
        if data is not None:
            res = data
        else:
            res = data
        rsp = AppHTTPStatus().format_rsp(res, exception_res, method=request.method, path=request.path)
        return rsp
    elif request.method == 'POST':
        create_data = request.form
        if create_data:
            pass
        else:
            create_data = request.json
        res, exception_res = CommentResource.create(create_data)
        rsp = AppHTTPStatus().format_rsp(res, exception_res, method=request.method, path=request.path)
        return rsp
    else:
        res = Response()
        res.headers['Access-Control-Allow-Origin'] = '*'
        return res
    pass

@app.route('/comments/<comment_id>', methods=['GET', 'PUT', 'DELETE'])
def get_comment_by_comment_id(comment_id):
    if request.method == 'GET':
        template = {"comment_id": comment_id}
        res, exception_res = CommentResource.find_by_template(template, 1, 0)
        rsp = AppHTTPStatus().format_rsp(res, exception_res, method=request.method, path=request.path)
        return rsp
    elif request.method == 'PUT':
        content = request.form['content']
        select_data = {"comment_id": comment_id}
        update_data = {"content":content}
        res, exception_res = CommentResource.update(select_data, update_data)
        rsp = AppHTTPStatus().format_rsp(res, exception_res, method=request.method, path=request.path)
        return rsp
    elif request.method == 'DELETE':
        template = {"comment_id": comment_id}
        res, exception_res = CommentResource.delete(template)
        rsp = AppHTTPStatus().format_rsp(res, exception_res, method=request.method, path=request.path)
        return rsp

@app.route('/forums/<forum_id>/comments', methods=['GET'])
def get_comments_from_forum_id(forum_id):
    if request.method == 'GET':
        template = {"forum_id": forum_id}
        offset = int(request.args.get("offset", OFFSET))
        limit = int(request.args.get("limit", MAXLIMIT))
        if limit > MAXLIMIT:
            limit = MAXLIMIT
        res, exception_res = CommentResource.find_by_template(template, limit, offset)
        rsp = AppHTTPStatus().format_rsp(res, exception_res, method=request.method, path=request.path)
        return rsp

@app.route('/comments/<comment_id>/user', methods=['GET'])
def get_linked_user(comment_id):
    if request.method == 'GET':
        template = {"comment_id": comment_id}
        res = CommentResource.find_linked_user(template)
        rsp = Response(json.dumps(res, default=str), status=200, content_type="application/json")
        return rsp

if __name__ == '__main__':
    app.run(port=8080, debug=False, host="0.0.0.0")


from __future__ import absolute_import, print_function

__metaclass__ = type

import time

from oops_amqp import anybson as bson
from oops_amqp.utils import (
    amqplib_error_types,
    close_ignoring_connection_errors,
    is_amqplib_connection_error,
    )

__all__ = [
    'Receiver',
    ]

class Receiver:
    """Republish OOPS reports from AMQP to a local oops.Config.

    :ivar stopping: When True will cause Receiver to break out of run_forever.
        Calls to run_forever reset this to False.
    :ivar sentinel: If a message identical to the sentinel is received,
        handle_report will set stopping to True.
    """

    def __init__(self, config, connection_factory, queue_name):
        """Create a Receiver.

        :param config: An oops.Config to republish the OOPS reports.
        :param connection_factory: An amqplib connection factory, used to make
            the initial connection and to reconnect if that connection is
            interrupted.
        :param queue_name: The queue to listen for reports on.
        """
        self.config = config
        self.connection = None
        self.channel = None
        self.connection_factory = connection_factory
        self.queue_name = queue_name
        self.sentinel = None

    def handle_report(self, message):
        # bson requires bytes.
        body = message.body
        if not isinstance(body, bytes):
            body = body.encode(message.content_encoding or 'UTF-8')
        if body == self.sentinel:
            self.stopping = True
            self.channel.basic_ack(message.delivery_tag)
            return
        try:
            report = bson.loads(body)
        except KeyError:
            # Garbage in the queue. Possibly this should raise an OOPS itself
            # (through a different config) or log an info level message.
            pass
        self.config.publish(report)
        # ACK last so errors here don't eat the message.
        self.channel.basic_ack(message.delivery_tag)

    def run_forever(self):
        """Run in a loop handling messages.

        If the amqp server is down or uncontactable for > 120 seconds, error
        out.
        """
        self.stopping = False
        self.went_bad = None
        while (not self.stopping and
               (not self.went_bad or time.time() < self.went_bad + 120)):
            try:
                self._run_forever()
            except amqplib_error_types as e:
                if not is_amqplib_connection_error(e):
                    # Something unknown went wrong.
                    raise
                if not self.went_bad:
                    self.went_bad = time.time()
                # Don't probe immediately, give the network/process time to
                # come back.
                time.sleep(0.1)

    def _run_forever(self):
        self.connection = self.connection_factory()
        self.connection.connect()
        # A successful connection: record this so run_forever won't bail early.
        self.went_bad = None
        try:
            self.channel = self.connection.channel()
            try:
                self.consume_tag = self.channel.basic_consume(
                    self.queue_name, callback=self.handle_report)
                try:
                    while True:
                        self.connection.drain_events(timeout=1)
                        if self.stopping:
                            break
                finally:
                    if self.channel.is_open:
                        self.channel.basic_cancel(self.consume_tag)
            finally:
                close_ignoring_connection_errors(self.channel)
        finally:
            close_ignoring_connection_errors(self.connection)


import matplotlib.pyplot as plt
import numpy as np

# 样本数据
data = np.random.randn(1000)
print(data)

# 绘制直方图
plt.hist(data, edgecolor='black')  # 使用5个柱形，黑色边界

# 添加标签和标题
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.title('Histogram')

# 显示图形
plt.show()


import collections
import os
import tempfile
import re
import hashlib

import gtk
import gobject

from . import dialogue
from . import ws_event
from . import gutils
from . import icons
from . import ifce
from . import utils
from . import file_tree
from . import cmd_result
from . import text_edit
from . import const
from . import diff
from . import tlview
from . import path
from . import change_set
from . import actions
from . import ws_actions
from . import table
from . import patch_view

def _markup_applied_patch(patch_name, guards, selected):
    markup = patch_name
    appliable = True
    if guards:
        unselected_pluses = False
        selected_pluses = False
        selected_minuses = False
        markup += " :"
        for guard in guards:
            if guard[1:] in selected:
                markup += " %s" % guard
                if guard[0] == "-":
                    selected_minuses = True
                else:
                    selected_pluses = True
            else:
                markup += " %s" % guard
                unselected_pluses = unselected_pluses or guard[0] == "+"
        if selected_minuses:
            appliable = False
        elif selected_pluses:
            appliable = True
        else:
            appliable = not unselected_pluses
    return (markup, appliable)

def _markup_unapplied_patch(patch_name, guards, selected):
    amarkup, appliable = _markup_applied_patch(patch_name, guards, selected)
    markup = '' + amarkup + ''
    return (markup, appliable)

def _patch_status_icon(status):
    if status != const.NOT_APPLIED:
        return icons.STOCK_APPLIED
    else:
        return None

AC_POP_POSSIBLE = ws_actions.AC_PMIC
AC_PUSH_POSSIBLE, AC_PUSH_POSSIBLE_MASK = actions.ActionCondns.new_flags_and_mask(1)

AC_APPLIED, AC_UNAPPLIED, AC_APPLIED_CONDNS = actions.ActionCondns.new_flags_and_mask(2)
AC_INTERDIFF, AC_INTERDIFF_MASK = actions.ActionCondns.new_flags_and_mask(1)

AC_WS_UPDATE_QSAVE_READY, \
AC_WS_UPDATE_PULL_READY, \
AC_WS_UPDATE_READY, \
AC_WS_UPDATE_TO_READY, \
AC_WS_UPDATE_MERGE_READY, \
AC_WS_UPDATE_CLEAN_UP_READY, \
AC_WS_CONDNS = actions.ActionCondns.new_flags_and_mask(6)

def get_applied_condns(seln):
    model, model_iter = seln.get_selected()
    if model_iter is None:
        return actions.MaskedCondns(actions.AC_DONT_CARE, AC_APPLIED_CONDNS)
    cond = AC_APPLIED if model.get_patch_is_applied(model_iter) else AC_UNAPPLIED
    return actions.MaskedCondns(cond, AC_APPLIED_CONDNS)

def get_interdiff_condns():
    return actions.MaskedCondns(AC_INTERDIFF if utils.which("interdiff") is not None else 0, AC_INTERDIFF_MASK)

def get_pushable_condns(unapplied_count):
    return actions.MaskedCondns(AC_PUSH_POSSIBLE if unapplied_count > 0 else 0, AC_PUSH_POSSIBLE_MASK)

def get_ws_update_condns(applied_count, unapplied_count):
    condn = actions.AC_DONT_CARE
    if ifce.PM.get_ws_update_qsave_ready(unapplied_count=unapplied_count, applied_count=applied_count):
        condn += AC_WS_UPDATE_QSAVE_READY
    if ifce.PM.get_ws_update_pull_ready(applied_count=applied_count):
        condn += AC_WS_UPDATE_PULL_READY
    if ifce.PM.get_ws_update_to_ready(applied_count=applied_count):
        condn += AC_WS_UPDATE_TO_READY
    if ifce.PM.get_ws_update_ready(applied_count=applied_count):
        condn += AC_WS_UPDATE_READY
    if ifce.PM.get_ws_update_merge_ready(unapplied_count=unapplied_count):
        condn += AC_WS_UPDATE_MERGE_READY
    if ifce.PM.get_ws_update_clean_up_ready():
        condn += AC_WS_UPDATE_CLEAN_UP_READY
    return actions.MaskedCondns(condn, AC_WS_CONDNS)

_finish_empty_msg_prompt = '\n'.join(
    [_('Do you wish to:'),
     _('\tcancel,'),
     _('\tedit the description and retry, or'),
     _('\tforce the finish operation?')
    ])

class ListView(table.TableView):
    PopUp = "/patches_popup"
    class Model(tlview.NamedListStore):
        Row = collections.namedtuple('Row',    ['name', 'icon', 'markup'])
        types = Row(
            name=gobject.TYPE_STRING,
            icon=gobject.TYPE_STRING,
            markup=gobject.TYPE_STRING,
        )
        def get_patch_name(self, plist_iter):
            return self.get_value_named(plist_iter, 'name')
        def get_patch_is_applied(self, plist_iter):
            return self.get_value_named(plist_iter, 'icon') is not None
    specification = tlview.ViewSpec(
        properties={
            'enable-grid-lines' : False,
            'reorderable' : False,
            'rules_hint' : False,
            'headers-visible' : False,
        },
        selection_mode=gtk.SELECTION_SINGLE,
        columns=[
            tlview.ColumnSpec(
                title=_('Patch List'),
                properties={'expand': False, 'resizable' : True},
                cells=[
                    tlview.CellSpec(
                        cell_renderer_spec=tlview.CellRendererSpec(
                            cell_renderer=gtk.CellRendererPixbuf,
                            expand=False,
                            start=True
                        ),
                        properties={},
                        cell_data_function_spec=None,
                        attributes = {'stock_id' : Model.col_index('icon')}
                    ),
                    tlview.CellSpec(
                        cell_renderer_spec=tlview.CellRendererSpec(
                            cell_renderer=gtk.CellRendererText,
                            expand=False,
                            start=True
                        ),
                        properties={'editable' : False},
                        cell_data_function_spec=None,
                        attributes = {'markup' : Model.col_index('markup')}
                    ),
                ],
            ),
        ]
    )
    UI_DESCR = \
    '''
    <ui>
      <menubar name="patches_menubar">
        <menu name="patches_menu" action="menu_patches">
          <menuitem action="pm_pop_all"/>
          <menuitem action="pm_push_all"/>
          <menuitem action="pm_import_patch_series"/>
        </menu>
        <menu name="patches_ws_menu" action="menu_patches_ws">
          <menuitem action="save_queue_state_for_update"/>
          <menuitem action="pm_pull_to_repository"/>
          <menuitem action="pm_update_workspace"/>
          <menuitem action="pm_update_workspace_to"/>
          <menuitem action="pm_push_all_with_merge"/>
          <menuitem action="pm_clean_up_after_update"/>
        </menu>
      </menubar>
      <toolbar name="patches_toolbar">
        <toolitem action="pm_refresh_top_patch"/>
        <toolitem action="pm_push"/>
        <toolitem action="pm_pop"/>
        <separator/>
        <toolitem name="PushMerge" action="pm_push_merge"/>
        <separator/>
        <toolitem action="pm_new"/>
        <toolitem action="pm_select_guards"/>
        <toolitem action="pm_import_external_patch"/>
      </toolbar>
      <menubar name="patch_list_menubar">
        <menu name="patch_list_menu" action="menu_patch_list">
          <menuitem action="refresh_patch_list"/>
        </menu>
      </menubar>
      <popup name="patches_popup">
        <placeholder name="applied">
          <menuitem action="pm_pop_to_patch"/>
          <menuitem action="pm_finish_to"/>
        </placeholder>
        <separator/>
        <placeholder name="applied_indifferent">
          <menuitem action="pm_edit_patch_descr"/>
          <menuitem action="pm_patch_view"/>
          <menuitem action="pm_view_patch_files"/>
          <menuitem action="pm_view_patch_diff"/>
          <menuitem action="pm_rename_patch"/>
          <menuitem action="pm_set_patch_guards"/>
        </placeholder>
        <separator/>
        <placeholder name="unapplied">
          <menuitem action="pm_delete_patch"/>
          <menuitem action="pm_push_to"/>
          <menuitem action="pm_fold"/>
          <menuitem action="pm_fold_to"/>
          <menuitem action="pm_duplicate"/>
        </placeholder>
        <placeholder name="unapplied_and_interdiff">
          <menuitem action="pm_interdiff"/>
        </placeholder>
      </popup>
    </ui>
    '''
    def __init__(self, busy_indicator=None, size_req=None):
        self.last_import_dir = None
        self._hash_data = None
        table.TableView.__init__(self, busy_indicator=busy_indicator, size_req=size_req)
        # This callback is needed to process applied/unapplied status
        self.get_selection().connect('changed', self._selection_changed_cb)
        self.add_notification_cb(ws_event.CHANGE_WD, self._repopulate_list_cb)
        self.add_notification_cb(ws_event.PATCH_CHANGES, self._update_list_cb)
        self.add_notification_cb(ws_event.AUTO_UPDATE, self._auto_update_list_cb)
        self.repopulate_list()
    def populate_action_groups(self):
        table.TableView.populate_action_groups(self)
        self.action_groups[AC_APPLIED].add_actions(
            [
                ("pm_pop_to_patch", icons.STOCK_POP_PATCH, _('QPop To'), None,
                 _('Pop to the selected patch'), self.do_pop_to),
                ("pm_finish_to", icons.STOCK_FINISH_PATCH, _('QFinish To'), None,
                 _('Move patches up to the selected patch into main repository'), self.do_finish_to),
            ])
        self.action_groups[actions.AC_SELN_MADE].add_actions(
            [
                ("pm_edit_patch_descr", gtk.STOCK_EDIT, _('Description'), None,
                 _('Edit the selected patch\'s description'), self.do_edit_description),
                ("pm_patch_view", icons.STOCK_DIFF, _('Details'), None,
                 _('View the selected patch\'s details'), self.do_view_selected_patch),
                ("pm_view_patch_files", gtk.STOCK_FILE, _('Files'), None,
                 _('Show files affected by the selected patch'), self.show_files),
                ("pm_view_patch_diff", icons.STOCK_DIFF, _('Diff'), None,
                 _('Show diff for the selected patch'), self.show_diff_acb),
                ("pm_rename_patch", None, _('QRename'), None,
                 _('Rename the selected patch'), self.do_rename),
                ("pm_set_patch_guards", icons.STOCK_QGUARD, None, None,
                 _('Set guards on the selected patch'), self.do_set_guards),
            ])
        self.action_groups[AC_UNAPPLIED].add_actions(
            [
                ("pm_delete_patch", gtk.STOCK_DELETE, "QDelete", None,
                 _('Delete the selected patch'), self.do_delete),
                ("pm_push_to", icons.STOCK_PUSH_PATCH, "QPush To", None,
                 _('Push to the selected patch'), self.do_push_to),
                ("pm_fold", icons.STOCK_FOLD_PATCH, "QFold", None,
                 _('Fold the selected patch into the top patch'), self.do_fold),
                ("pm_fold_to", icons.STOCK_FOLD_PATCH, "QFold To", None,
                 _('Fold patches up to the selected patch into the top patch'), self.do_fold_to),
                ("pm_duplicate", gtk.STOCK_COPY, _('Duplicate'), None,
                 _('Duplicate the selected patch behind the top patch'), self.do_duplicate),
            ])
        self.action_groups[AC_UNAPPLIED + AC_INTERDIFF].add_actions(
            [
                ("pm_interdiff", gtk.STOCK_PASTE, "Interdiff", None,
                 _('Place the "interdiff" of the selected patch and the top patch behind the top patch'), self.do_interdiff),
            ])
        self.action_groups[AC_PUSH_POSSIBLE].add_actions(
            [
                ("pm_push", icons.STOCK_PUSH_PATCH, "QPush", None,
                 _('Apply the next unapplied patch'), self.do_push),
                ("pm_push_all", icons.STOCK_PUSH_PATCH, "QPush All", None,
                 _('Apply all remaining unapplied patches'), self.do_push_all),
            ])
        self.action_groups[AC_POP_POSSIBLE].add_actions(
            [
                ("pm_pop", icons.STOCK_POP_PATCH, "QPop", None,
                 _('Pop the top applied patch'), self.do_pop),
                ("pm_pop_all", icons.STOCK_POP_PATCH, "QPop All", None,
                 _('Pop all remaining applied patches'), self.do_pop_all),
                ("pm_refresh_top_patch", icons.STOCK_QREFRESH, None, None,
                 _('Refresh the top patch'), self.do_refresh),
            ])
        self.action_groups[ws_actions.AC_IN_PGND].add_actions(
            [
                ("menu_patches", None, _('_Patches')),
                ("menu_patches_ws", None, _('_Workspace Update')),
                ("refresh_patch_list", gtk.STOCK_REFRESH, _('Update Patch List'), None,
                 _('Refresh/update the patch list display'), self._update_list_cb),
                ("pm_import_patch_series", icons.STOCK_IMPORT_PATCH, _('QImport Patch Series'), None,
                 _('Import an external patch (mq/quilt style) series'), self.do_import_external_patch_series),
                ("pm_select_guards", icons.STOCK_QSELECT, None, None,
                 _('Select which guards are in force'), self.do_select_guards),
            ])
        self.action_groups[ws_actions.AC_IN_REPO].add_actions(
            [
                ("menu_patch_list", None, _('Patch _List')),
                ("pm_new", icons.STOCK_QNEW, None, None,
                 _('Create a new patch'), self.do_new_patch),
                ("pm_import_external_patch", icons.STOCK_IMPORT_PATCH, "QImport", None,
                 _('Import an external patch'), self.do_import_external_patch),
            ])
        self.action_groups[AC_WS_UPDATE_MERGE_READY].add_actions(
            [
                ("pm_push_merge", icons.STOCK_QPUSH_MERGE, None, None,
                 _('Apply the next unapplied patch merging with equivalent saved patch'), self.do_push_merge),
                ("pm_push_all_with_merge", icons.STOCK_QPUSH_MERGE_ALL, None, None,
                 _('Apply all remaining unapplied patches with "merge" option enabled'), self.do_push_all_with_merge),
            ])
        self.action_groups[AC_WS_UPDATE_QSAVE_READY].add_actions(
            [
                ("save_queue_state_for_update", None, _('QSave For Update'), None,
                 _('Save the queue state prepatory to update'), self.do_save_queue_state_for_update),
            ])
        self.action_groups[AC_WS_UPDATE_READY].add_actions(
            [
                ("pm_update_workspace", None, _('Update Workspace'), None,
                 _('Update the workspace to the repository tip'), self.do_update_workspace),
            ])
        self.action_groups[AC_WS_UPDATE_TO_READY].add_actions(
            [
                ("pm_update_workspace_to", None, _('Update Workspace To'), None,
                 _('Update the workspace to a specified revision'), self.do_update_workspace_to),
            ])
        self.action_groups[AC_WS_UPDATE_PULL_READY].add_actions(
            [
                ("pm_pull_to_repository", None, _('Pull To Workspace'), None,
                 _('Pull to the repository from the default remote path'), self.do_pull_to_repository),
            ])
        self.action_groups[AC_WS_UPDATE_CLEAN_UP_READY].add_actions(
            [
                ("pm_clean_up_after_update", gtk.STOCK_CLEAR, _('Clean Up'), None,
                 _('Clean up left over heads after repostory and patch series update'), self.do_clean_up_after_update),
            ])
    def _selection_changed_cb(self, selection):
        # This callback is needed to process applied/unapplied status
        # self.action_groups' callback handles the other selection conditions
        self.action_groups.update_condns(get_applied_condns(selection))
    def get_selected_patch(self):
        store, store_iter = self.seln.get_selected()
        return None if store_iter is None else store.get_patch_name(store_iter)
    def _update_list_cb(self, _arg=None):
        self.refresh_contents()
    def _auto_update_list_cb(self, _arg=None):
        if not self._hash_data or (self._hash_data.digest() != ifce.PM.get_all_patches_hash_digest()):
            self.refresh_contents()
    def _fetch_contents(self):
        self._hash_data = hashlib.sha1()
        patch_data_list = ifce.PM.get_all_patches_data(self._hash_data)
        selected = ifce.PM.get_selected_guards(self._hash_data)
        unapplied_count = 0
        applied_count = 0
        contents = []
        for patch_data in patch_data_list:
            icon = _patch_status_icon(patch_data.state)
            if patch_data.state is not const.NOT_APPLIED:
                markup, dummy = _markup_applied_patch(patch_data.name, patch_data.guards, selected)
                contents.append([patch_data.name, icon, markup])
                applied_count += 1
            else:
                markup, appliable = _markup_unapplied_patch(patch_data.name, patch_data.guards, selected)
                contents.append([patch_data.name, icon, markup])
                if appliable:
                    unapplied_count += 1
        condns = get_pushable_condns(unapplied_count)
        condns |= get_ws_update_condns(applied_count, unapplied_count)
        self.action_groups.update_condns(condns)
        return contents
    def repopulate_list(self):
        self.set_contents()
        condns = get_applied_condns(self.get_selection())
        condns |= ws_actions.get_in_pgnd_condns()
        condns |= get_interdiff_condns()
        self.action_groups.update_condns(condns)
    def _repopulate_list_cb(self, _arg=None):
        self.show_busy()
        ifce.PM.update_is_enabled()
        self.repopulate_list()
        self.unshow_busy()
    def do_refresh(self, _action=None, notify=True):
        self.show_busy()
        result = ifce.PM.do_refresh(notify=notify)
        self.unshow_busy()
        if result.ecode != cmd_result.OK:
            if result.ecode & cmd_result.SUGGEST_RECOVER:
                if dialogue.ask_recover_or_cancel(result) == dialogue.Response.RECOVER:
                    self.show_busy()
                    result = ifce.PM.do_recover_interrupted_refresh()
                    if result.ecode == cmd_result.OK:
                        result = ifce.PM.do_refresh(notify=notify)
                    self.unshow_busy()
            if result.ecode != cmd_result.OK: # There're may still be problems
                dialogue.report_any_problems(result)
                return
    def _do_pop_to(self, patch=None):
        while True:
            self.show_busy()
            result = ifce.PM.do_pop_to(patch=patch)
            self.unshow_busy()
            if result.ecode != cmd_result.OK:
                if result.ecode & cmd_result.SUGGEST_FORCE_OR_REFRESH:
                    ans = dialogue.ask_force_refresh_or_cancel(result)
                    if ans == gtk.RESPONSE_CANCEL:
                        return False
                    elif ans == dialogue.Response.REFRESH:
                        self.do_refresh(notify=False)
                        continue
                    elif ans == dialogue.Response.FORCE:
                        self.show_busy()
                        result = ifce.PM.do_pop_to(force=True)
                        self.unshow_busy()
                if result.ecode != cmd_result.OK: # there're are still problems
                    dialogue.report_any_problems(result)
                    return False
            return True
    def do_pop_to(self, _action=None):
        patch = self.get_selected_patch()
        while ifce.PM.get_top_patch() != patch:
            if not self._do_pop_to(patch):
                break
    def do_pop(self, _action=None):
        self._do_pop_to()
    def do_pop_all(self, _action=None):
        self._do_pop_to("")
    def _do_push_to(self, patch=None, merge=False):
        while True:
            self.show_busy()
            result = ifce.PM.do_push_to(patch=patch, merge=merge)
            self.unshow_busy()
            if result.ecode != cmd_result.OK:
                if result.ecode & cmd_result.SUGGEST_FORCE_OR_REFRESH:
                    ans = dialogue.ask_force_refresh_or_cancel(result, parent=None)
                    if ans == gtk.RESPONSE_CANCEL:
                        return False
                    if ans == dialogue.Response.REFRESH:
                        self.do_refresh(notify=False)
                        continue
                    elif ans == dialogue.Response.FORCE:
                        self.show_busy()
                        result = ifce.PM.do_push_to(force=True, merge=merge)
                        self.unshow_busy()
                if result.ecode != cmd_result.OK: # there're are still problems
                    dialogue.report_any_problems(result)
                    return False
            return True
    def do_push_to(self, _action=None):
        patch = self.get_selected_patch()
        while ifce.PM.get_top_patch() != patch:
            if not self._do_push_to(patch=patch):
                break
    def do_push(self, _action=None):
        self._do_push_to()
    def do_push_merge(self, _action=None):
        self._do_push_to(merge=True)
    def do_push_all(self, _action=None):
        self._do_push_to(patch="", merge=False)
    def do_push_all_with_merge(self, _action=None):
        self._do_push_to(patch="", merge=True)
    def do_finish_to(self, _action=None):
        patch = self.get_selected_patch()
        while True:
            next_patch = ifce.PM.get_base_patch()
            if not next_patch:
                break
            while True:
                self.show_busy()
                is_ok = ifce.PM.get_description_is_finish_ready(next_patch)
                self.unshow_busy()
                if is_ok:
                    break
                dummyres = cmd_result.Result(ecode=cmd_result.SUGGEST_ALL,
                    stdout = _('"%s" has an empty description.') % next_patch,
                    stderr = '')
                ans = dialogue.ask_edit_force_or_cancel(dummyres, clarification=_finish_empty_msg_prompt, parent=None)
                if ans == gtk.RESPONSE_CANCEL:
                    return
                elif ans == dialogue.Response.FORCE:
                    break
                self.do_edit_description_wait(next_patch)
            self.show_busy()
            result = ifce.PM.do_finish_patch(next_patch)
            self.unshow_busy()
            if result.ecode != cmd_result.OK:
                dialogue.report_any_problems(result)
                break
            if patch == next_patch:
                break
    def do_edit_description_wait(self, patch=None):
        if not patch:
            patch = self.get_selected_patch()
        PatchDescrEditDialog(patch, parent=None).run()
    def do_edit_description(self, _action=None):
        patch = self.get_selected_patch()
        PatchDescrEditDialog(patch, parent=None).show()
    def do_view_selected_patch(self, action=None):
        patchname = self.get_selected_patch()
        patch_view.Dialogue(patchname).show()
    def show_files(self, _action=None):
        patch = self.get_selected_patch()
        dialog = file_tree.PatchFilesDialog(patch=patch)
        dialog.show()
    def show_diff_acb(self, _action=None):
        patch = self.get_selected_patch()
        dialog = diff.PmDiffTextDialog(parent=dialogue.main_window, patch=patch)
        dialog.show()
    def do_rename(self, _action=None):
        patch = self.get_selected_patch()
        dialog = dialogue.ReadTextDialog(_('Rename Patch: %s') % patch, _('New Name:'), patch)
        response = dialog.run()
        if response == gtk.RESPONSE_OK:
            new_name = dialog.entry.get_text()
            dialog.destroy()
            if patch == new_name:
                return
            self.show_busy()
            result = ifce.PM.do_rename_patch(patch, new_name)
            self.unshow_busy()
            dialogue.report_any_problems(result)
        else:
            dialog.destroy()
    def do_set_guards(self, _action=None):
        patch = self.get_selected_patch()
        cguards = ' '.join(ifce.PM.get_patch_guards(patch))
        dialog = dialogue.ReadTextDialog(_('Set Guards: %s') % patch, _('Guards:'), cguards)
        response = dialog.run()
        if response == gtk.RESPONSE_OK:
            guards = dialog.entry.get_text()
            dialog.destroy()
            self.show_busy()
            result = ifce.PM.do_set_patch_guards(patch, guards)
            self.unshow_busy()
            dialogue.report_any_problems(result)
        else:
            dialog.destroy()
    def do_select_guards(self, _action=None):
        cselected_guards = ' '.join(ifce.PM.get_selected_guards())
        dialog = dialogue.ReadTextDialog(_('Select Guards: %s') % os.getcwd(), _('Guards:'), cselected_guards)
        response = dialog.run()
        if response == gtk.RESPONSE_OK:
            selected_guards = dialog.entry.get_text()
            dialog.destroy()
            self.show_busy()
            result = ifce.PM.do_select_guards(selected_guards)
            self.unshow_busy()
            dialogue.report_any_problems(result)
        else:
            dialog.destroy()
    def do_delete(self, _action=None):
        patch = self.get_selected_patch()
        if dialogue.ask_ok_cancel(_('Confirm delete "%s" patch?') % patch):
            self.show_busy()
            result = ifce.PM.do_delete_patch(patch)
            self.unshow_busy()
            dialogue.report_any_problems(result)
    def do_fold(self, _action=None):
        patch = self.get_selected_patch()
        self.show_busy()
        result = ifce.PM.do_fold_patch(patch)
        self.unshow_busy()
        dialogue.report_any_problems(result)
    def do_fold_to(self, _action=None):
        patch = self.get_selected_patch()
        while True:
            next_patch = ifce.PM.get_next_patch()
            if not next_patch:
                return
            self.show_busy()
            result = ifce.PM.do_fold_patch(next_patch)
            self.unshow_busy()
            if result.ecode != cmd_result.OK:
                dialogue.report_any_problems(result)
                return
            if patch == next_patch:
                return
    def do_duplicate(self, _action=None):
        patch = self.get_selected_patch()
        dialog = DuplicatePatchDialog(patch, parent=None)
        if dialog.run() == gtk.RESPONSE_CANCEL:
            dialog.destroy()
            return
        duplicate_patch_name = dialog.get_duplicate_patch_name()
        duplicate_patch_descr = dialog.get_duplicate_patch_descr()
        dialog.destroy()
        if not duplicate_patch_name:
            return
        self.show_busy()
        old_pfname = ifce.PM.get_patch_file_name(patch)
        result = ifce.PM.do_import_patch(old_pfname, duplicate_patch_name)
        self.unshow_busy()
        if result.ecode == cmd_result.ERROR_SUGGEST_FORCE:
            if dialogue.ask_force_refresh_or_cancel(result) == dialogue.Response.FORCE:
                self.show_busy()
                result = ifce.PM.do_import_patch(old_pfname, duplicate_patch_name, force=True)
                self.unshow_busy()
            else:
                return
        if result.ecode != cmd_result.OK:
            dialogue.report_any_problems(result)
            if result.ecode & cmd_result.ERROR:
                return
        self.show_busy()
        result = ifce.PM.do_set_patch_description(duplicate_patch_name, duplicate_patch_descr)
        self.unshow_busy()
        dialogue.report_any_problems(result)
    def do_interdiff(self, _action=None):
        patch = self.get_selected_patch()
        dialog = DuplicatePatchDialog(patch, verb="Interdiff", parent=None)
        if dialog.run() == gtk.RESPONSE_CANCEL:
            dialog.destroy()
            return
        interdiff_patch_name = dialog.get_duplicate_patch_name()
        interdiff_patch_descr = dialog.get_duplicate_patch_descr()
        dialog.destroy()
        if not interdiff_patch_name:
            return
        self.show_busy()
        top_patch = ifce.PM.get_top_patch()
        if top_patch:
            top_pfname = ifce.PM.get_patch_file_name(top_patch)
            old_pfname = ifce.PM.get_patch_file_name(patch)
            result = utils.run_cmd("interdiff %s %s" % (top_pfname, old_pfname))
            if result.ecode != cmd_result.OK:
                dialogue.report_any_problems(result)
                return
            temp_pfname = tempfile.mktemp()
            tfobj = open(temp_pfname, 'w')
            tfobj.write('\n'.join([interdiff_patch_descr, result.stdout]))
            tfobj.close()
        else:
            temp_pfname = ifce.PM.get_patch_file_name(patch)
        result = ifce.PM.do_import_patch(temp_pfname, interdiff_patch_name)
        self.unshow_busy()
        if result.ecode == cmd_result.ERROR_SUGGEST_FORCE:
            if dialogue.ask_force_refresh_or_cancel(result) == dialogue.Response.FORCE:
                self.show_busy()
                result = ifce.PM.do_import_patch(temp_pfname, interdiff_patch_name, force=True)
                self.unshow_busy()
        if top_patch:
            os.remove(temp_pfname)
        if result.ecode != cmd_result.OK:
            dialogue.report_any_problems(result)
    def do_save_queue_state_for_update(self, _action=None):
        if not ifce.PM.get_enabled():
            dialogue.report_any_problems(ifce.PM.not_enabled_response)
            return
        self.show_busy()
        result = ifce.PM.do_save_queue_state_for_update()
        self.unshow_busy()
        dialogue.report_any_problems(result)
    def do_pull_to_repository(self, _action=None):
        if not ifce.PM.get_enabled():
            dialogue.report_any_problems(ifce.PM.not_enabled_response)
            return
        self.show_busy()
        dialog = path.PullDialog()
        self.unshow_busy()
        response = dialog.run()
        if response == gtk.RESPONSE_OK:
            source = dialog.get_path()
            rev = dialog.get_revision()
            dialog.destroy()
            self.show_busy()
            result = ifce.PM.do_pull_from(source=source, rev=rev)
            self._condn_change_update_cb()
            self.unshow_busy()
            dialogue.report_any_problems(result)
        else:
            dialog.destroy()
    def do_update_workspace(self, _action=None):
        if not ifce.PM.get_enabled():
            dialogue.report_any_problems(ifce.PM.not_enabled_response)
            return
        self.show_busy()
        result = ifce.PM.do_update_workspace()
        self._condn_change_update_cb()
        self.unshow_busy()
        dialogue.report_any_problems(result)
    def do_update_workspace_to(self, _action=None):
        if not ifce.PM.get_enabled():
            dialogue.report_any_problems(ifce.PM.not_enabled_response)
            return
        dialog = change_set.ChangeSetSelectDialog()
        response = dialog.run()
        if response == gtk.RESPONSE_OK:
            rev = dialog.get_change_set()
            dialog.destroy()
            if rev:
                self.show_busy()
                result = ifce.PM.do_update_workspace(rev=rev)
                self._condn_change_update_cb()
                self.unshow_busy()
                dialogue.report_any_problems(result)
        else:
            dialog.destroy()
    def do_clean_up_after_update(self, _action=None):
        if not ifce.PM.get_enabled():
            dialogue.report_any_problems(ifce.PM.not_enabled_response)
            return
        self.show_busy()
        result = ifce.PM.do_clean_up_after_update()
        self.unshow_busy()
        dialogue.report_any_problems(result)
    def do_new_patch(self, _action=None):
        if not ifce.PM.get_enabled():
            dialogue.report_any_problems(ifce.PM.not_enabled_response)
            return
        dialog = NewPatchDialog(parent=None)
        if dialog.run() == gtk.RESPONSE_CANCEL:
            dialog.destroy()
            return
        new_patch_name = dialog.get_new_patch_name()
        new_patch_descr = dialog.get_new_patch_descr()
        dialog.hide()
        if not new_patch_name:
            dialog.finish_up(False)
            return
        force = False
        while True:
            self.show_busy()
            result = ifce.PM.do_new_patch(new_patch_name, force=force)
            self.unshow_busy()
            if result.ecode & cmd_result.SUGGEST_FORCE_OR_REFRESH:
                ans = dialogue.ask_force_refresh_or_cancel(result, parent=None)
                if ans == gtk.RESPONSE_CANCEL:
                    dialog.finish_up(False)
                    return
                if ans == dialogue.Response.REFRESH:
                    self.do_refresh(notify=False)
                elif ans == dialogue.Response.FORCE:
                    force = True
            else:
                dialog.finish_up(False)
                dialogue.report_any_problems(result)
                break
        if new_patch_descr and result.ecode != cmd_result.ERROR:
            self.show_busy()
            result = ifce.PM.do_set_patch_description(new_patch_name, new_patch_descr)
            dialog.finish_up(True)
            self.unshow_busy()
            dialogue.report_any_problems(result)
    def do_import_external_patch(self, _action=None):
        if not ifce.PM.get_enabled():
            dialogue.report_any_problems(ifce.PM.not_enabled_response)
            return
        patch_file_name = dialogue.ask_file_name(_('Select patch file to be imported'))
        if not patch_file_name:
            return
        force = False
        patch_name = None
        while True:
            self.show_busy()
            result = ifce.PM.do_import_patch(patch_file_name, patch_name, force)
            self.unshow_busy()
            if result.ecode & cmd_result.SUGGEST_FORCE_OR_RENAME:
                ans = dialogue.ask_rename_force_or_cancel(result, clarification=_('Force import of patch, rename patch or cancel import?'))
                if ans == gtk.RESPONSE_CANCEL:
                    return
                elif ans == dialogue.Response.FORCE:
                    force = True
                    continue
                elif ans == dialogue.Response.RENAME:
                    if not patch_name:
                        patch_name = os.path.basename(patch_file_name)
                    patch_name = dialogue.get_modified_string(_('Rename Patch'), _('New Name :'), patch_name)
                    continue
            dialogue.report_any_problems(result)
            break
    def do_import_external_patch_series(self, _action=None):
        if not ifce.PM.get_enabled():
            dialogue.report_any_problems(ifce.PM.not_enabled_response)
            return
        patch_series_dir = dialogue.ask_dir_name(_('Select patch series to be imported'))
        if not patch_series_dir:
            return
        series_fn = os.sep.join([patch_series_dir, "series"])
        if (not os.path.exists(series_fn) and os.path.isfile(series_fn)):
            dialogue.report_any_problems(cmd_result.Result(cmd_result.ERROR, "", _('Series file not found.')))
            return
        sfobj = open(series_fn, 'r')
        series = sfobj.readlines()
        sfobj.close()
        series.reverse()
        index = 0
        patch_name_re = re.compile("\s*([^\s#]+)[\s#]*.*$")
        while index < len(series):
            match = patch_name_re.match(series[index])
            if not match:
                index += 1
                continue
            base_name = match.group(1)
            patch_file_name = os.sep.join([patch_series_dir, base_name])
            force = False
            patch_name = None
            while True:
                self.show_busy()
                result = ifce.PM.do_import_patch(patch_file_name, patch_name, force)
                self.unshow_busy()
                if result.ecode & cmd_result.SUGGEST_FORCE_OR_RENAME:
                    ans = dialogue.ask_rename_force_or_skip(result, clarification=_('Force import of patch, rename patch or skip patch?'))
                    if ans == dialogue.Response.SKIP_ALL:
                        index = len(series)
                        break
                    elif ans == dialogue.Response.SKIP:
                        break
                    elif ans == dialogue.Response.FORCE:
                        force = True
                        continue
                    elif ans == dialogue.Response.RENAME:
                        if not patch_name:
                            patch_name = base_name
                        patch_name = dialogue.get_modified_string(_('Rename Patch'), _('New Name :'), patch_name)
                        continue
                dialogue.report_any_problems(result)
                break
            index += 1

class List(table.TableWidget):
    View = ListView
    def __init__(self, scroll_bar=True, busy_indicator=None, size_req=None):
        table.TableWidget.__init__(self, scroll_bar=scroll_bar, busy_indicator=busy_indicator, size_req=size_req)
        self.header.lhs.pack_start(self.ui_manager.get_widget('/patch_list_menubar'), expand=True, fill=True)

def do_export_named_patch(parent, patchname, suggestion=None, busy_indicator=None):
    if not suggestion:
        suggestion = utils.convert_patchname_to_filename(patchname)
    if busy_indicator is None:
        busy_indicator = dialogue.main_window
    PROMPT = _('Export as ...')
    export_filename = dialogue.ask_file_name(PROMPT, suggestion=suggestion, existing=False)
    if export_filename is None:
        return
    force = False
    overwrite = False
    refresh_tried = False
    while True:
        busy_indicator.show_busy()
        result = ifce.PM.do_export_patch_as(patchname, export_filename, force=force, overwrite=overwrite)
        busy_indicator.unshow_busy()
        if refresh_tried:
            result = cmd_result.turn_off_flags(result, cmd_result.SUGGEST_REFRESH)
        if result.ecode & cmd_result.SUGGEST_FORCE_OR_REFRESH != 0:
            resp = dialogue.ask_force_refresh_absorb_or_cancel(result, clarification=None)
            if resp == gtk.RESPONSE_CANCEL:
                return
            elif resp == dialogue.Response.FORCE:
                force = True
            elif resp == dialogue.Response.REFRESH:
                refresh_tried = True
                dialogue.show_busy()
                result = ifce.PM.do_refresh_patch()
                dialogue.unshow_busy()
                dialogue.report_any_problems(result)
            continue
        elif result.ecode & cmd_result.SUGGEST_RENAME != 0:
            resp = dialogue.ask_rename_overwrite_or_cancel(result, clarification=None)
            if resp == gtk.RESPONSE_CANCEL:
                return
            elif resp == dialogue.Response.OVERWRITE:
                overwrite = True
            elif resp == dialogue.Response.RENAME:
                export_filename = dialogue.ask_file_name(PROMPT, suggestion=export_filename, existing=False)
                if export_filename is None:
                    return
            continue
        dialogue.report_any_problems(result)
        break

class NewPatchDescrEditWidget(text_edit.MessageWidget):
    UI_DESCR = \
        '''
        <ui>
          <menubar name="patch_summary_menubar">
            <menu name="patch_summary_menu" action="menu_summary">
              <separator/>
              <menuitem action="text_edit_insert_from"/>
            </menu>
          </menubar>
          <toolbar name="patch_summary_toolbar">
            <toolitem action="text_edit_ack"/>
            <toolitem action="text_edit_sign_off"/>
            <toolitem action="text_edit_author"/>
          </toolbar>
        </ui>
        '''
    def __init__(self):
        save_file_name = ifce.PM.get_default_new_patch_save_file()
        auto_save = save_file_name is not None and save_file_name is not ""
        text_edit.MessageWidget.__init__(self, save_file_name=save_file_name, auto_save=auto_save)
        if auto_save and os.path.exists(save_file_name):
            self.load_text_fm_file(save_file_name, already_checked=True)
        menubar = self.ui_manager.get_widget("/patch_summary_menubar")
        self.top_hbox.pack_start(menubar, fill=True, expand=False)
        toolbar = self.ui_manager.get_widget("/patch_summary_toolbar")
        toolbar.set_style(gtk.TOOLBAR_BOTH)
        toolbar.set_orientation(gtk.ORIENTATION_HORIZONTAL)
        self.top_hbox.pack_end(toolbar, fill=False, expand=False)
        self.show_all()
        self.set_focus_child(self.view)
    def populate_action_groups(self):
        text_edit.MessageWidget.populate_action_groups(self)
        self.action_groups[actions.AC_DONT_CARE].add_action(gtk.Action("menu_summary", _("Description"), _(""), None))

class PatchDescrEditWidget(text_edit.DbMessageWidget):
    UI_DESCR = \
        '''
        <ui>
          <menubar name="patch_summary_menubar">
            <menu name="patch_summary_menu" action="menu_summary">
              <separator/>
              <menuitem action="text_edit_load"/>
              <menuitem action="text_edit_save"/>
              <menuitem action="text_edit_insert_from"/>
            </menu>
          </menubar>
          <toolbar name="patch_summary_toolbar">
            <toolitem action="text_edit_ack"/>
            <toolitem action="text_edit_sign_off"/>
            <toolitem action="text_edit_author"/>
          </toolbar>
        </ui>
        '''
    def __init__(self, patch):
        self._patch = patch
        text_edit.DbMessageWidget.__init__(self, save_file_name=None, auto_save=False)
        menubar = self.ui_manager.get_widget('/patch_summary_menubar')
        self.top_hbox.pack_start(menubar, fill=True, expand=False)
        toolbar = self.ui_manager.get_widget('/patch_summary_toolbar')
        toolbar.set_style(gtk.TOOLBAR_BOTH)
        toolbar.set_orientation(gtk.ORIENTATION_HORIZONTAL)
        self.top_hbox.pack_end(toolbar, fill=False, expand=False)
        self.show_all()
        self.set_focus_child(self.view)
        self.load_text_fm_db()
    def populate_action_groups(self):
        text_edit.DbMessageWidget.populate_action_groups(self)
        self.action_groups[actions.AC_DONT_CARE].add_action(gtk.Action("menu_summary", _("Description"), _(""), None))
    def get_text_fm_db(self):
        return ifce.PM.get_patch_description(self._patch)
    def set_text_in_db(self, text):
        return ifce.PM.do_set_patch_description(self._patch, text)

class PatchDescrEditDialog(dialogue.AmodalDialog):
    def __init__(self, patch, parent=None):
        flags = gtk.DIALOG_DESTROY_WITH_PARENT
        dialogue.AmodalDialog.__init__(self, None, parent, flags, None)
        self.set_title(_('"%s" Description: %s') % (patch, utils.cwd_rel_home()))
        self.edit_descr_widget = PatchDescrEditWidget(patch)
        self.vbox.pack_start(self.edit_descr_widget)
        self.action_area.pack_start(self.edit_descr_widget.reload_button)
        self.action_area.pack_start(self.edit_descr_widget.save_button)
        self.add_button(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)
        self.connect('response', self._handle_response_cb)
        self.set_focus_child(self.edit_descr_widget.view)
    def _handle_response_cb(self, dialog, response_id):
        if response_id == gtk.RESPONSE_CLOSE:
            if self.edit_descr_widget.view.get_buffer().get_modified():
                qtn = '\n'.join([_('Unsaved changes to summary will be lost.'), _('Close anyway?')])
                if dialogue.ask_yes_no(qtn):
                    self.destroy()
            else:
                self.destroy()

class DuplicatePatchDialog(dialogue.Dialog):
    def __init__(self, patch, parent, verb=_('Duplicate')):
        flags = gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT
        title = '%s "%s": %s' % (verb, patch, utils.path_rel_home(os.getcwd()))
        dialogue.Dialog.__init__(self, title, parent, flags,
                                 (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                                  gtk.STOCK_OK, gtk.RESPONSE_OK))
        hbox = gtk.HBox()
        vbox = gtk.VBox()
        vbox.pack_start(gtk.Label(_('%s Patch:') % verb))
        vbox.pack_start(gtk.Label(_(' As Patch Named:')))
        hbox.pack_start(vbox, fill=False, expand=False)
        vbox = gtk.VBox()
        entry = gtk.Entry()
        entry.set_text(patch)
        entry.set_editable(False)
        vbox.pack_start(entry)
        self.new_name_entry = gtk.Entry()
        self.new_name_entry.set_width_chars(32)
        vbox.pack_start(self.new_name_entry)
        hbox.pack_start(vbox)
        hbox.show_all()
        self.vbox.pack_start(hbox)
        self.edit_descr_widget = NewPatchDescrEditWidget()
        try:
            self.edit_descr_widget.view.get_buffer().set_text(ifce.PM.get_patch_description(patch))
        except cmd_result.Failure:
            pass
        self.vbox.pack_start(self.edit_descr_widget)
        self.set_focus_child(self.new_name_entry)
    def get_duplicate_patch_name(self):
        return self.new_name_entry.get_text()
    def get_duplicate_patch_descr(self):
        return self.edit_descr_widget.view.get_msg()
    def finish_up(self, clear_save=False):
        self.edit_descr_widget.finish_up(clear_save)
        self.destroy()

class NewPatchDialog(dialogue.Dialog):
    def __init__(self, parent, objname=_('Patch')):
        flags = gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT
        title = _('New %s: %s -- gwsmhg') % (objname, utils.path_rel_home(os.getcwd()))
        dialogue.Dialog.__init__(self, title, parent, flags,
                                 (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                                  gtk.STOCK_OK, gtk.RESPONSE_OK))
        if not parent:
            self.set_icon_from_file(icons.APP_ICON_FILE)
        self.hbox = gtk.HBox()
        self.hbox.pack_start(gtk.Label(_('New %s Name:') % objname), fill=False, expand=False)
        self.new_name_entry = gtk.Entry()
        self.new_name_entry.set_width_chars(32)
        self.hbox.pack_start(self.new_name_entry)
        self.hbox.show_all()
        self.vbox.pack_start(self.hbox)
        self.edit_descr_widget = NewPatchDescrEditWidget()
        self.vbox.pack_start(self.edit_descr_widget)
        self.set_focus_child(self.new_name_entry)
    def get_new_patch_name(self):
        return self.new_name_entry.get_text()
    def get_new_patch_descr(self):
        return self.edit_descr_widget.get_contents()
    def finish_up(self, clear_save=False):
        self.edit_descr_widget.finish_up(clear_save)
        self.destroy()