def __init__(self, parent, repo, refa, refb):
        super(DiffViewerDialog, self).__init__(parent,
                               Qt.WindowSystemMenuHint | Qt.WindowTitleHint)
        self.repo = repo

        self.setupUi(self)

        self.setWindowFlags(self.windowFlags() |
                            Qt.WindowSystemMenuHint)

        self.commit1 = refa
        self.commit1Panel = RefPanel(self.repo, refa)
        layout = QHBoxLayout()
        layout.setSpacing(0)
        layout.setMargin(0)
        layout.addWidget(self.commit1Panel)
        self.commit1Widget.setLayout(layout)
        self.commit2 = refb
        self.commit2Panel = RefPanel(self.repo, refb)
        layout = QHBoxLayout()
        layout.setSpacing(0)
        layout.setMargin(0)
        layout.addWidget(self.commit2Panel)
        self.commit2Widget.setLayout(layout)
        self.commit1Panel.refChanged.connect(self.refsHaveChanged)
        self.commit2Panel.refChanged.connect(self.refsHaveChanged)

        self.featuresTree.currentItemChanged.connect(self.treeItemChanged)

        self.featuresTree.header().hide()

        self.computeDiffs()
        self.groupBox.adjustSize()
    def __init__(self, parent, repo, refa, refb):
        super(DiffViewerDialog, self).__init__(parent,
                               Qt.WindowSystemMenuHint | Qt.WindowTitleHint)
        self.repo = repo

        self.setupUi(self)

        self.setWindowFlags(self.windowFlags() |
                            Qt.WindowSystemMenuHint)

        self.commit1 = refa
        self.commit1Panel = RefPanel(self.repo, refa)
        layout = QHBoxLayout()
        layout.setSpacing(0)
        layout.setMargin(0)
        layout.addWidget(self.commit1Panel)
        self.commit1Widget.setLayout(layout)
        self.commit2 = refb
        self.commit2Panel = RefPanel(self.repo, refb)
        layout = QHBoxLayout()
        layout.setSpacing(0)
        layout.setMargin(0)
        layout.addWidget(self.commit2Panel)
        self.commit2Widget.setLayout(layout)
        self.commit1Panel.refChanged.connect(self.refsHaveChanged)
        self.commit2Panel.refChanged.connect(self.refsHaveChanged)

        self.featuresTree.currentItemChanged.connect(self.treeItemChanged)

        self.featuresTree.header().hide()

        self.computeDiffs()
        self.groupBox.adjustSize()
class DiffViewerDialog(WIDGET, BASE):

    def __init__(self, parent, repo, refa, refb):
        super(DiffViewerDialog, self).__init__(parent,
                               Qt.WindowSystemMenuHint | Qt.WindowTitleHint)
        self.repo = repo

        self.setupUi(self)

        self.setWindowFlags(self.windowFlags() |
                            Qt.WindowSystemMenuHint)

        self.commit1 = refa
        self.commit1Panel = RefPanel(self.repo, refa)
        layout = QHBoxLayout()
        layout.setSpacing(0)
        layout.setMargin(0)
        layout.addWidget(self.commit1Panel)
        self.commit1Widget.setLayout(layout)
        self.commit2 = refb
        self.commit2Panel = RefPanel(self.repo, refb)
        layout = QHBoxLayout()
        layout.setSpacing(0)
        layout.setMargin(0)
        layout.addWidget(self.commit2Panel)
        self.commit2Widget.setLayout(layout)
        self.commit1Panel.refChanged.connect(self.refsHaveChanged)
        self.commit2Panel.refChanged.connect(self.refsHaveChanged)

        self.featuresTree.currentItemChanged.connect(self.treeItemChanged)

        self.featuresTree.header().hide()

        self.computeDiffs()
        self.groupBox.adjustSize()


    def refsHaveChanged(self):
        self.computeDiffs()

    def treeItemChanged(self, current, previous):
        qgsgeom1 = None
        qgsgeom2 = None
        crs = "EPSG:4326"
        if not isinstance(current, FeatureItem):
            self.attributesTable.clear()
            self.attributesTable.setRowCount(0)
            return
        color = {"MODIFIED": QColor(255, 170, 0), "ADDED":Qt.green,
                 "REMOVED":Qt.red , "NO_CHANGE":Qt.white}
        path = current.layername + "/" + current.featureid
        featurediff = self.changes[path].featurediff()
        self.attributesTable.clear()
        self.attributesTable.verticalHeader().show()
        self.attributesTable.horizontalHeader().show()
        self.attributesTable.setRowCount(len(featurediff))
        self.attributesTable.setVerticalHeaderLabels([a["attributename"] for a in featurediff])
        self.attributesTable.setHorizontalHeaderLabels(["Old value", "New value", "Change type"])
        for i, attrib in enumerate(featurediff):
            try:
                if attrib["changetype"] == "MODIFIED":
                    oldvalue = attrib["oldvalue"]
                    newvalue = attrib["newvalue"]
                elif attrib["changetype"] == "ADDED":
                    newvalue = attrib["newvalue"]
                    oldvalue = ""
                elif attrib["changetype"] == "REMOVED":
                    oldvalue = attrib["oldvalue"]
                    newvalue = ""
                else:
                    oldvalue = newvalue = attrib["oldvalue"]
            except:
                oldvalue = newvalue = ""
            self.attributesTable.setItem(i, 0, DiffItem(oldvalue))
            self.attributesTable.setItem(i, 1, DiffItem(newvalue))
            try:
                self.attributesTable.setItem(i, 2, QTableWidgetItem(""))
                if qgsgeom1 is None or qgsgeom2 is None:
                    if "crs" in attrib:
                        crs = attrib["crs"]
                    qgsgeom1 = QgsGeometry.fromWkt(oldvalue)
                    qgsgeom2 = QgsGeometry.fromWkt(newvalue)
                    if qgsgeom1 is not None and qgsgeom2 is not None:
                        widget = QWidget()
                        btn = QPushButton()
                        btn.setText("View detail")
                        btn.clicked.connect(lambda: self.viewGeometryChanges(qgsgeom1, qgsgeom2, crs))
                        label = QLabel()
                        label.setText(attrib["changetype"])
                        layout = QHBoxLayout(widget)
                        layout.addWidget(label);
                        layout.addWidget(btn);
                        layout.setContentsMargins(0, 0, 0, 0)
                        widget.setLayout(layout)
                        self.attributesTable.setCellWidget(i, 2, widget)
                    else:
                        self.attributesTable.setItem(i, 2, QTableWidgetItem(attrib["changetype"]))
                else:
                    self.attributesTable.setItem(i, 2, QTableWidgetItem(attrib["changetype"]))
            except:
                self.attributesTable.setItem(i, 2, QTableWidgetItem(attrib["changetype"]))
            for col in range(3):
                self.attributesTable.item(i, col).setBackgroundColor(color[attrib["changetype"]]);
        self.attributesTable.resizeColumnsToContents()
        self.attributesTable.horizontalHeader().setResizeMode(QHeaderView.Stretch)

    def viewGeometryChanges(self, g1, g2, crs):
        dlg = GeometryDiffViewerDialog([g1, g2], QgsCoordinateReferenceSystem(crs))
        dlg.exec_()


    def computeDiffs(self):
        self.commit1 = self.commit1Panel.getRef()
        self.commit2 = self.commit2Panel.getRef()

        self.featuresTree.clear()
        changes = execute(lambda: self.repo.diff(self.commit1.commitid, self.commit2.commitid))
        layerItems = {}
        layerSubItems = {}
        self.changes = {}
        for c in changes:
            self.changes[c.path] = c
            layername = c.path.split("/")[0]
            featureid = c.path.split("/")[-1]
            if layername not in layerItems:
                item = QTreeWidgetItem()
                item.setText(0, layername)
                item.setIcon(0, layerIcon)
                layerItems[layername] = item
                addedItem = QTreeWidgetItem()
                addedItem.setText(0, "Added")
                addedItem.setIcon(0, addedIcon)
                removedItem = QTreeWidgetItem()
                removedItem.setText(0, "Removed")
                removedItem.setIcon(0, removedIcon)
                modifiedItem = QTreeWidgetItem()
                modifiedItem.setText(0, "Modified")
                modifiedItem.setIcon(0, modifiedIcon)
                layerSubItems[layername] = {FEATURE_ADDED: addedItem,
                                            FEATURE_REMOVED: removedItem,
                                            FEATURE_MODIFIED:modifiedItem}
            item = FeatureItem(layername, featureid)
            layerSubItems[layername][c.changetype].addChild(item)
        for layername, item in layerItems.iteritems():
            for i in [FEATURE_ADDED, FEATURE_REMOVED, FEATURE_MODIFIED]:
                subItem = layerSubItems[layername][i]
                item.addChild(subItem)
                subItem.setText(0, "%s [%i features]" %
                                                    (subItem.text(0),
                                                     subItem.childCount()))

            self.featuresTree.addTopLevelItem(item)
        self.attributesTable.clear()
        self.attributesTable.verticalHeader().hide()
        self.attributesTable.horizontalHeader().hide()
        
        self.featuresTree.expandAll()

    def reject(self):
        QDialog.reject(self)
class DiffViewerDialog(WIDGET, BASE):

    def __init__(self, parent, repo, refa, refb):
        super(DiffViewerDialog, self).__init__(parent,
                               Qt.WindowSystemMenuHint | Qt.WindowTitleHint)
        self.repo = repo

        self.setupUi(self)

        self.setWindowFlags(self.windowFlags() |
                            Qt.WindowSystemMenuHint)

        self.commit1 = refa
        self.commit1Panel = RefPanel(self.repo, refa)
        layout = QHBoxLayout()
        layout.setSpacing(0)
        layout.setMargin(0)
        layout.addWidget(self.commit1Panel)
        self.commit1Widget.setLayout(layout)
        self.commit2 = refb
        self.commit2Panel = RefPanel(self.repo, refb)
        layout = QHBoxLayout()
        layout.setSpacing(0)
        layout.setMargin(0)
        layout.addWidget(self.commit2Panel)
        self.commit2Widget.setLayout(layout)
        self.commit1Panel.refChanged.connect(self.refsHaveChanged)
        self.commit2Panel.refChanged.connect(self.refsHaveChanged)

        self.featuresTree.currentItemChanged.connect(self.treeItemChanged)

        self.featuresTree.header().hide()

        self.computeDiffs()
        self.groupBox.adjustSize()


    def refsHaveChanged(self):
        self.computeDiffs()

    def treeItemChanged(self, current, previous):
        qgsgeom1 = None
        qgsgeom2 = None
        crs = "EPSG:4326"
        if not isinstance(current, FeatureItem):
            self.attributesTable.clear()
            self.attributesTable.setRowCount(0)
            return
        color = {"MODIFIED": QColor(255, 170, 0), "ADDED":Qt.green,
                 "REMOVED":Qt.red , "NO_CHANGE":Qt.white}
        path = current.layername + "/" + current.featureid
        featurediff = self.changes[path].featurediff()
        self.attributesTable.clear()
        self.attributesTable.verticalHeader().show()
        self.attributesTable.horizontalHeader().show()
        self.attributesTable.setRowCount(len(featurediff))
        self.attributesTable.setVerticalHeaderLabels([a["attributename"] for a in featurediff])
        self.attributesTable.setHorizontalHeaderLabels(["Old value", "New value", "Change type"])
        for i, attrib in enumerate(featurediff):
            try:
                if attrib["changetype"] == "MODIFIED":
                    oldvalue = attrib["oldvalue"]
                    newvalue = attrib["newvalue"]
                elif attrib["changetype"] == "ADDED":
                    newvalue = attrib["newvalue"]
                    oldvalue = ""
                elif attrib["changetype"] == "REMOVED":
                    oldvalue = attrib["oldvalue"]
                    newvalue = ""
                else:
                    oldvalue = newvalue = attrib["oldvalue"]
            except:
                oldvalue = newvalue = ""
            self.attributesTable.setItem(i, 0, DiffItem(oldvalue))
            self.attributesTable.setItem(i, 1, DiffItem(newvalue))
            try:
                self.attributesTable.setItem(i, 2, QTableWidgetItem(""))
                if qgsgeom1 is None or qgsgeom2 is None:
                    if "crs" in attrib:
                        crs = attrib["crs"]
                    qgsgeom1 = QgsGeometry.fromWkt(oldvalue)
                    qgsgeom2 = QgsGeometry.fromWkt(newvalue)
                    if qgsgeom1 is not None and qgsgeom2 is not None:
                        widget = QWidget()
                        btn = QPushButton()
                        btn.setText("View detail")
                        btn.clicked.connect(lambda: self.viewGeometryChanges(qgsgeom1, qgsgeom2, crs))
                        label = QLabel()
                        label.setText(attrib["changetype"])
                        layout = QHBoxLayout(widget)
                        layout.addWidget(label);
                        layout.addWidget(btn);
                        layout.setContentsMargins(0, 0, 0, 0)
                        widget.setLayout(layout)
                        self.attributesTable.setCellWidget(i, 2, widget)
                    else:
                        self.attributesTable.setItem(i, 2, QTableWidgetItem(attrib["changetype"]))
                else:
                    self.attributesTable.setItem(i, 2, QTableWidgetItem(attrib["changetype"]))
            except:
                self.attributesTable.setItem(i, 2, QTableWidgetItem(attrib["changetype"]))
            for col in range(3):
                self.attributesTable.item(i, col).setBackgroundColor(color[attrib["changetype"]]);
        self.attributesTable.resizeColumnsToContents()
        self.attributesTable.horizontalHeader().setResizeMode(QHeaderView.Stretch)

    def viewGeometryChanges(self, g1, g2, crs):
        dlg = GeometryDiffViewerDialog([g1, g2], QgsCoordinateReferenceSystem(crs))
        dlg.exec_()


    def computeDiffs(self):
        self.commit1 = self.commit1Panel.getRef()
        self.commit2 = self.commit2Panel.getRef()

        self.featuresTree.clear()
        changes = execute(lambda: self.repo.diff(self.commit1.commitid, self.commit2.commitid))
        layerItems = {}
        layerSubItems = {}
        self.changes = {}
        for c in changes:
            self.changes[c.path] = c
            layername = c.path.split("/")[0]
            featureid = c.path.split("/")[-1]
            if layername not in layerItems:
                item = QTreeWidgetItem()
                item.setText(0, layername)
                item.setIcon(0, layerIcon)
                layerItems[layername] = item
                addedItem = QTreeWidgetItem()
                addedItem.setText(0, "Added")
                addedItem.setIcon(0, addedIcon)
                removedItem = QTreeWidgetItem()
                removedItem.setText(0, "Removed")
                removedItem.setIcon(0, removedIcon)
                modifiedItem = QTreeWidgetItem()
                modifiedItem.setText(0, "Modified")
                modifiedItem.setIcon(0, modifiedIcon)
                layerSubItems[layername] = {FEATURE_ADDED: addedItem,
                                            FEATURE_REMOVED: removedItem,
                                            FEATURE_MODIFIED:modifiedItem}
            item = FeatureItem(layername, featureid)
            layerSubItems[layername][c.changetype].addChild(item)
        for layername, item in layerItems.iteritems():
            for i in [FEATURE_ADDED, FEATURE_REMOVED, FEATURE_MODIFIED]:
                subItem = layerSubItems[layername][i]
                item.addChild(subItem)
                subItem.setText(0, "%s [%i features]" %
                                                    (subItem.text(0),
                                                     subItem.childCount()))

            self.featuresTree.addTopLevelItem(item)
        self.attributesTable.clear()
        self.attributesTable.verticalHeader().hide()
        self.attributesTable.horizontalHeader().hide()
        for item in layerItems.values():
            item.setExpanded(True)

    def reject(self):
        QDialog.reject(self)
    def __init__(self, parent, repo, refa, refb):
        QtGui.QDialog.__init__(self, parent,
                               QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint)
        self.repo = repo
        self.layers = []
        self.allchanges = {}
        self.currentPath = None

        if (isinstance(refa, Commit) and isinstance(refb, Commit)
                and refa.committerdate > refb.committerdate):
            refa, refb = refb, refa

        self.ui = Ui_DiffViewerDialog()
        self.ui.setupUi(self)

        self.setWindowFlags(self.windowFlags() |
                              QtCore.Qt.WindowSystemMenuHint |
                              QtCore.Qt.WindowMinMaxButtonsHint)

        self.commit1 = refa
        self.commit1Panel = RefPanel(self.repo, refa, onlyCommits = False)
        layout = QtGui.QHBoxLayout()
        layout.setSpacing(0)
        layout.setMargin(0)
        layout.addWidget(self.commit1Panel)
        self.ui.commit1Widget.setLayout(layout)
        self.commit2 = refb
        self.commit2Panel = RefPanel(self.repo, refb, onlyCommits = False)
        layout = QtGui.QHBoxLayout()
        layout.setSpacing(0)
        layout.setMargin(0)
        layout.addWidget(self.commit2Panel)
        self.ui.commit2Widget.setLayout(layout)
        self.commit1Panel.refChanged.connect(self.refsHaveChanged)
        self.commit2Panel.refChanged.connect(self.refsHaveChanged)

        horizontalLayout = QtGui.QHBoxLayout()
        horizontalLayout.setSpacing(0)
        horizontalLayout.setMargin(0)
        self.mapCanvas = QgsMapCanvas()
        self.mapCanvas.setCanvasColor(QtCore.Qt.white)
        settings = QtCore.QSettings()
        self.mapCanvas.enableAntiAliasing(settings.value("/qgis/enable_anti_aliasing", False, type = bool))
        self.mapCanvas.useImageToRender(settings.value("/qgis/use_qimage_to_render", False, type = bool))
        action = settings.value("/qgis/wheel_action", 0, type = float)
        zoomFactor = settings.value("/qgis/zoom_factor", 2, type = float)
        self.mapCanvas.setWheelAction(QgsMapCanvas.WheelAction(action), zoomFactor)
        horizontalLayout.addWidget(self.mapCanvas)
        self.ui.mapContainer.setLayout(horizontalLayout)

        self.panAndSelectTool = MapToolPanAndSelect(self.mapCanvas, self)
        self.mapCanvas.setMapTool(self.panAndSelectTool)

        self.ui.attributesTable.horizontalHeader().sectionClicked.connect(self.sortByColumn)
        self.ui.attributesTable.customContextMenuRequested.connect(self.showContextMenu)
        self.ui.layerCombo.currentIndexChanged.connect(self.layerChanged)

        def _zoomToFullExtent():
            extent = self.getFullExtent()
            self.mapCanvas.setExtent(extent)
            self.mapCanvas.refresh()
        self.ui.zoomToExtentButton.clicked.connect(_zoomToFullExtent)

        self.ui.baseLayerCheck.setChecked(True)
        self.ui.compareLayerCheck.setChecked(True)

        self.computeDiffs()

        self.ui.baseLayerCheck.stateChanged.connect(self.showLayers)
        self.ui.compareLayerCheck.stateChanged.connect(self.showLayers)

        self.showMaximized()
class DiffViewerDialog(QtGui.QDialog):

    def __init__(self, parent, repo, refa, refb):
        QtGui.QDialog.__init__(self, parent,
                               QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint)
        self.repo = repo
        self.layers = []
        self.allchanges = {}
        self.currentPath = None

        if (isinstance(refa, Commit) and isinstance(refb, Commit)
                and refa.committerdate > refb.committerdate):
            refa, refb = refb, refa

        self.ui = Ui_DiffViewerDialog()
        self.ui.setupUi(self)

        self.setWindowFlags(self.windowFlags() |
                              QtCore.Qt.WindowSystemMenuHint |
                              QtCore.Qt.WindowMinMaxButtonsHint)

        self.commit1 = refa
        self.commit1Panel = RefPanel(self.repo, refa, onlyCommits = False)
        layout = QtGui.QHBoxLayout()
        layout.setSpacing(0)
        layout.setMargin(0)
        layout.addWidget(self.commit1Panel)
        self.ui.commit1Widget.setLayout(layout)
        self.commit2 = refb
        self.commit2Panel = RefPanel(self.repo, refb, onlyCommits = False)
        layout = QtGui.QHBoxLayout()
        layout.setSpacing(0)
        layout.setMargin(0)
        layout.addWidget(self.commit2Panel)
        self.ui.commit2Widget.setLayout(layout)
        self.commit1Panel.refChanged.connect(self.refsHaveChanged)
        self.commit2Panel.refChanged.connect(self.refsHaveChanged)

        horizontalLayout = QtGui.QHBoxLayout()
        horizontalLayout.setSpacing(0)
        horizontalLayout.setMargin(0)
        self.mapCanvas = QgsMapCanvas()
        self.mapCanvas.setCanvasColor(QtCore.Qt.white)
        settings = QtCore.QSettings()
        self.mapCanvas.enableAntiAliasing(settings.value("/qgis/enable_anti_aliasing", False, type = bool))
        self.mapCanvas.useImageToRender(settings.value("/qgis/use_qimage_to_render", False, type = bool))
        action = settings.value("/qgis/wheel_action", 0, type = float)
        zoomFactor = settings.value("/qgis/zoom_factor", 2, type = float)
        self.mapCanvas.setWheelAction(QgsMapCanvas.WheelAction(action), zoomFactor)
        horizontalLayout.addWidget(self.mapCanvas)
        self.ui.mapContainer.setLayout(horizontalLayout)

        self.panAndSelectTool = MapToolPanAndSelect(self.mapCanvas, self)
        self.mapCanvas.setMapTool(self.panAndSelectTool)

        self.ui.attributesTable.horizontalHeader().sectionClicked.connect(self.sortByColumn)
        self.ui.attributesTable.customContextMenuRequested.connect(self.showContextMenu)
        self.ui.layerCombo.currentIndexChanged.connect(self.layerChanged)

        def _zoomToFullExtent():
            extent = self.getFullExtent()
            self.mapCanvas.setExtent(extent)
            self.mapCanvas.refresh()
        self.ui.zoomToExtentButton.clicked.connect(_zoomToFullExtent)

        self.ui.baseLayerCheck.setChecked(True)
        self.ui.compareLayerCheck.setChecked(True)

        self.computeDiffs()

        self.ui.baseLayerCheck.stateChanged.connect(self.showLayers)
        self.ui.compareLayerCheck.stateChanged.connect(self.showLayers)

        self.showMaximized()

    def refsHaveChanged(self):
        self.computeDiffs()


    def showContextMenu(self, point):
        row = self.ui.attributesTable.selectionModel().currentIndex().row()
        model = self.ui.attributesTable.model()
        index = model.index(row, self.geogigIdx)
        geogigid = model.data(index)
        menu = QtGui.QMenu()
        zoomAction = QtGui.QAction("Zoom to this feature", None)
        zoomAction.triggered.connect(lambda: self.zoomToFeature(geogigid))
        menu.addAction(zoomAction)
        viewAction = QtGui.QAction("View geometry changes...", None)
        viewAction.triggered.connect(lambda: self.viewGeometryChanges(geogigid))
        menu.addAction(viewAction)
        globalPoint = self.ui.attributesTable.mapToGlobal(point)
        menu.exec_(globalPoint)

    def zoomToFeature(self, geogigid):
        geometries = [g for g in self.features[geogigid][GEOMETRY_FIELD] if g is not None]
        extent = geometries[0].boundingBox()
        for geom in geometries:
            extent.combineExtentWith(geom.boundingBox())
        self.mapCanvas.setExtent(extent)
        self.mapCanvas.refresh()

    def viewGeometryChanges(self, geogigid):
        geometries = self.features[geogigid][GEOMETRY_FIELD]
        dlg = GeometryDiffViewerDialog(geometries, self.layers[0].crs())
        dlg.exec_()

    def sortByColumn(self, col):
        self.ui.attributesTable.sortByColumn(col, QtCore.Qt.DescendingOrder)

    def getFullExtent(self):
        layers = [lay for lay in self.layers if lay is not None]
        extent = layers[0].extent()
        for layer in layers:
            extent.combineExtentWith(layer.extent())
        return extent

    def selectionChanged(self):
        row = self.ui.attributesTable.selectionModel().currentIndex().row()
        model = self.ui.attributesTable.model()
        index = model.index(row, self.geogigIdx)
        geogigid = model.data(index)
        def _filter(feature):
            return feature["geogigid"] == geogigid
        for layer in self.layers:
            if layer is not None:
                features = filter(_filter, layer.getFeatures())
                layer.setSelectedFeatures([feature.id() for feature in features])
        self.mapCanvas.refresh()


    def createTableDataFromLayers(self):
        self.attribs = []
        self.features = {}
        for layer in self.layers:
            if layer is not None:
                fields = layer.pendingFields().toList()
                for f in fields:
                    if f.name() not in self.attribs:
                        self.attribs.append(f.name())
        self.attribs.remove("changetype")
        self.attribs.insert(0, "changetype")
        if self.layers[0] is not None:
            layer = self.layers[0]
            fields = [f.name() for f in layer.pendingFields().toList()]
            features = layer.getFeatures()
            geogigidIdx = fields.index("geogigid")
            for feature in features:
                attrs = feature.attributes()
                geogigid = attrs[geogigidIdx]
                if not isinstance(geogigid, QtCore.QPyNullVariant):
                    featuredict = {}
                    for field in self.attribs:
                        try:
                            idx = fields.index(field)
                            value = attrs[idx]
                        except ValueError:
                            value = None
                        featuredict[field] = [value, None]
                    featuredict[GEOMETRY_FIELD] = [QgsGeometry(feature.geometry()), None]
                    self.features[geogigid] = featuredict
        if self.layers[1] is not None:
            layer = self.layers[1]
            fields = [f.name() for f in layer.pendingFields().toList()]
            features = layer.getFeatures()
            geogigidIdx = fields.index("geogigid")
            for feature in features:
                attrs = feature.attributes()
                geogigid = attrs[geogigidIdx]
                if not isinstance(geogigid, QtCore.QPyNullVariant):
                    if geogigid not in self.features:
                        self.features[geogigid] = {attr: [None, None] for attr in self.attribs}
                        self.features[geogigid][GEOMETRY_FIELD] = [None, None]
                    featuredict = self.features[geogigid]
                    for field in self.attribs:
                        try:
                            idx = fields.index(field)
                            value = attrs[idx]
                            featuredict[field][1] = value
                        except ValueError:
                            pass
                    featuredict[GEOMETRY_FIELD][1] = QgsGeometry(feature.geometry())

        self.attribs.insert(1, GEOMETRY_FIELD)
        self.geogigIdx = self.attribs.index("geogigid")


    def layerChanged(self):
        layername = self.ui.layerCombo.currentText()
        self.computeLayerDiffs(layername)

    def computeDiffs(self):
        self.commit1 = self.commit1Panel.getRef()
        self.commit2 = self.commit2Panel.getRef()

        self.unloadLayers()

        allchanges = exportVersionDiffs(self.commit1, self.commit2)
        self.allchanges = {}
        for path, layers in allchanges.iteritems():
            self.allchanges[path] = layers
            for layer in layers:
                if layer is not None:
                    QgsMapLayerRegistry.instance().addMapLayer(layer, False)
        if self.allchanges:
            self.layers = self.allchanges.values()[0]

            self.ui.layerCombo.blockSignals(True)
            self.ui.layerCombo.clear()
            self.ui.layerCombo.addItems(self.allchanges.keys())
            self.ui.layerCombo.blockSignals(False)

            self.computeLayerDiffs(self.allchanges.keys()[0])
        else:
            self.mapCanvas.clear()
            self.proxyModel.deleteLater()


    def computeLayerDiffs(self, layername):
        def _computeLayerDiff():
            self.layers = self.allchanges[layername]
            self.createTableDataFromLayers()
            self.geogigidIdx = self.attribs.index("geogigid")
            self.changeTypeIdx = self.attribs.index("changetype")

            model = DiffTableModel(self.attribs, self.features)
            self.proxyModel = QtGui.QSortFilterProxyModel();
            self.proxyModel.setSourceModel(model)
            self.ui.attributesTable.setModel(self.proxyModel)
            self.ui.attributesTable.selectionModel().selectionChanged.connect(self.selectionChanged)
            self.ui.attributesTable.setColumnHidden(self.geogigidIdx, True)
            self.ui.attributesTable.resizeColumnsToContents()
            self.ui.attributesTable.setVisible(False)
            vporig = self.ui.attributesTable.viewport().geometry()
            vpnew = vporig;
            vpnew.setWidth(10000);
            self.ui.attributesTable.viewport().setGeometry(vpnew);
            self.ui.attributesTable.resizeRowsToContents()
            self.ui.attributesTable.viewport().setGeometry(vporig);
            self.ui.attributesTable.setVisible(True)

            self.showLayers()

        execute(_computeLayerDiff)

    def showLayers(self):
        layers = [lay for lay in self.layers if lay is not None]
        self.mapCanvas.setDestinationCrs(layers[0].crs())
        extent = self.getFullExtent()
        self.mapCanvas.setRenderFlag(False)
        self.mapCanvas.clear()
        visibility = [self.ui.compareLayerCheck.isChecked(), self.ui.baseLayerCheck.isChecked()]
        mapLayers = [QgsMapCanvasLayer(lay) for i, lay in enumerate(reversed(self.layers)) if visibility[i]]
        self.mapCanvas.setLayerSet(mapLayers)
        self.mapCanvas.setRenderFlag(True)
        self.mapCanvas.refresh()
        self.mapCanvas.setExtent(extent)

    def reject(self):
        QtGui.QDialog.reject(self)
        self.unloadLayers()

    def unloadLayers(self):
        for layers in self.allchanges.values():
            for layer in layers:
                if layer is not None:
                    QgsMapLayerRegistry.instance().removeMapLayer(layer.id())
    def __init__(self, parent, repo, refa, refb):
        QtGui.QDialog.__init__(self, parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint)
        self.repo = repo
        self.layers = []
        self.allchanges = {}
        self.currentPath = None

        if isinstance(refa, Commit) and isinstance(refb, Commit) and refa.committerdate > refb.committerdate:
            refa, refb = refb, refa

        self.ui = Ui_DiffViewerDialog()
        self.ui.setupUi(self)

        self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowMinMaxButtonsHint)

        self.commit1 = refa
        self.commit1Panel = RefPanel(self.repo, refa, onlyCommits=False)
        layout = QtGui.QHBoxLayout()
        layout.setSpacing(0)
        layout.setMargin(0)
        layout.addWidget(self.commit1Panel)
        self.ui.commit1Widget.setLayout(layout)
        self.commit2 = refb
        self.commit2Panel = RefPanel(self.repo, refb, onlyCommits=False)
        layout = QtGui.QHBoxLayout()
        layout.setSpacing(0)
        layout.setMargin(0)
        layout.addWidget(self.commit2Panel)
        self.ui.commit2Widget.setLayout(layout)
        self.commit1Panel.refChanged.connect(self.refsHaveChanged)
        self.commit2Panel.refChanged.connect(self.refsHaveChanged)

        horizontalLayout = QtGui.QHBoxLayout()
        horizontalLayout.setSpacing(0)
        horizontalLayout.setMargin(0)
        self.mapCanvas = QgsMapCanvas()
        self.mapCanvas.setCanvasColor(QtCore.Qt.white)
        settings = QtCore.QSettings()
        self.mapCanvas.enableAntiAliasing(settings.value("/qgis/enable_anti_aliasing", False, type=bool))
        self.mapCanvas.useImageToRender(settings.value("/qgis/use_qimage_to_render", False, type=bool))
        action = settings.value("/qgis/wheel_action", 0, type=float)
        zoomFactor = settings.value("/qgis/zoom_factor", 2, type=float)
        self.mapCanvas.setWheelAction(QgsMapCanvas.WheelAction(action), zoomFactor)
        horizontalLayout.addWidget(self.mapCanvas)
        self.ui.mapContainer.setLayout(horizontalLayout)

        self.panAndSelectTool = MapToolPanAndSelect(self.mapCanvas, self)
        self.mapCanvas.setMapTool(self.panAndSelectTool)

        self.ui.attributesTable.horizontalHeader().sectionClicked.connect(self.sortByColumn)
        self.ui.attributesTable.customContextMenuRequested.connect(self.showContextMenu)
        self.ui.layerCombo.currentIndexChanged.connect(self.layerChanged)

        def _zoomToFullExtent():
            extent = self.getFullExtent()
            self.mapCanvas.setExtent(extent)
            self.mapCanvas.refresh()

        self.ui.zoomToExtentButton.clicked.connect(_zoomToFullExtent)

        self.ui.baseLayerCheck.setChecked(True)
        self.ui.compareLayerCheck.setChecked(True)

        self.computeDiffs()

        self.ui.baseLayerCheck.stateChanged.connect(self.showLayers)
        self.ui.compareLayerCheck.stateChanged.connect(self.showLayers)

        self.showMaximized()
class DiffViewerDialog(QtGui.QDialog):
    def __init__(self, parent, repo, refa, refb):
        QtGui.QDialog.__init__(self, parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint)
        self.repo = repo
        self.layers = []
        self.allchanges = {}
        self.currentPath = None

        if isinstance(refa, Commit) and isinstance(refb, Commit) and refa.committerdate > refb.committerdate:
            refa, refb = refb, refa

        self.ui = Ui_DiffViewerDialog()
        self.ui.setupUi(self)

        self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowMinMaxButtonsHint)

        self.commit1 = refa
        self.commit1Panel = RefPanel(self.repo, refa, onlyCommits=False)
        layout = QtGui.QHBoxLayout()
        layout.setSpacing(0)
        layout.setMargin(0)
        layout.addWidget(self.commit1Panel)
        self.ui.commit1Widget.setLayout(layout)
        self.commit2 = refb
        self.commit2Panel = RefPanel(self.repo, refb, onlyCommits=False)
        layout = QtGui.QHBoxLayout()
        layout.setSpacing(0)
        layout.setMargin(0)
        layout.addWidget(self.commit2Panel)
        self.ui.commit2Widget.setLayout(layout)
        self.commit1Panel.refChanged.connect(self.refsHaveChanged)
        self.commit2Panel.refChanged.connect(self.refsHaveChanged)

        horizontalLayout = QtGui.QHBoxLayout()
        horizontalLayout.setSpacing(0)
        horizontalLayout.setMargin(0)
        self.mapCanvas = QgsMapCanvas()
        self.mapCanvas.setCanvasColor(QtCore.Qt.white)
        settings = QtCore.QSettings()
        self.mapCanvas.enableAntiAliasing(settings.value("/qgis/enable_anti_aliasing", False, type=bool))
        self.mapCanvas.useImageToRender(settings.value("/qgis/use_qimage_to_render", False, type=bool))
        action = settings.value("/qgis/wheel_action", 0, type=float)
        zoomFactor = settings.value("/qgis/zoom_factor", 2, type=float)
        self.mapCanvas.setWheelAction(QgsMapCanvas.WheelAction(action), zoomFactor)
        horizontalLayout.addWidget(self.mapCanvas)
        self.ui.mapContainer.setLayout(horizontalLayout)

        self.panAndSelectTool = MapToolPanAndSelect(self.mapCanvas, self)
        self.mapCanvas.setMapTool(self.panAndSelectTool)

        self.ui.attributesTable.horizontalHeader().sectionClicked.connect(self.sortByColumn)
        self.ui.attributesTable.customContextMenuRequested.connect(self.showContextMenu)
        self.ui.layerCombo.currentIndexChanged.connect(self.layerChanged)

        def _zoomToFullExtent():
            extent = self.getFullExtent()
            self.mapCanvas.setExtent(extent)
            self.mapCanvas.refresh()

        self.ui.zoomToExtentButton.clicked.connect(_zoomToFullExtent)

        self.ui.baseLayerCheck.setChecked(True)
        self.ui.compareLayerCheck.setChecked(True)

        self.computeDiffs()

        self.ui.baseLayerCheck.stateChanged.connect(self.showLayers)
        self.ui.compareLayerCheck.stateChanged.connect(self.showLayers)

        self.showMaximized()

    def refsHaveChanged(self):
        self.computeDiffs()

    def showContextMenu(self, point):
        row = self.ui.attributesTable.selectionModel().currentIndex().row()
        model = self.ui.attributesTable.model()
        index = model.index(row, self.geogigIdx)
        geogigid = model.data(index)
        menu = QtGui.QMenu()
        zoomAction = QtGui.QAction("Zoom to this feature", None)
        zoomAction.triggered.connect(lambda: self.zoomToFeature(geogigid))
        menu.addAction(zoomAction)
        viewAction = QtGui.QAction("View geometry changes...", None)
        viewAction.triggered.connect(lambda: self.viewGeometryChanges(geogigid))
        menu.addAction(viewAction)
        globalPoint = self.ui.attributesTable.mapToGlobal(point)
        menu.exec_(globalPoint)

    def zoomToFeature(self, geogigid):
        geometries = [g for g in self.features[geogigid][GEOMETRY_FIELD] if g is not None]
        extent = geometries[0].boundingBox()
        for geom in geometries:
            extent.combineExtentWith(geom.boundingBox())
        self.mapCanvas.setExtent(extent)
        self.mapCanvas.refresh()

    def viewGeometryChanges(self, geogigid):
        geometries = self.features[geogigid][GEOMETRY_FIELD]
        dlg = GeometryDiffViewerDialog(geometries, self.layers[0].crs())
        dlg.exec_()

    def sortByColumn(self, col):
        self.ui.attributesTable.sortByColumn(col, QtCore.Qt.DescendingOrder)

    def getFullExtent(self):
        layers = [lay for lay in self.layers if lay is not None]
        extent = layers[0].extent()
        for layer in layers:
            extent.combineExtentWith(layer.extent())
        return extent

    def selectionChanged(self):
        row = self.ui.attributesTable.selectionModel().currentIndex().row()
        model = self.ui.attributesTable.model()
        index = model.index(row, self.geogigIdx)
        geogigid = model.data(index)

        def _filter(feature):
            return feature["geogigid"] == geogigid

        for layer in self.layers:
            if layer is not None:
                features = filter(_filter, layer.getFeatures())
                layer.setSelectedFeatures([feature.id() for feature in features])
        self.mapCanvas.refresh()

    def createTableDataFromLayers(self):
        self.attribs = []
        self.features = {}
        for layer in self.layers:
            if layer is not None:
                fields = layer.pendingFields().toList()
                for f in fields:
                    if f.name() not in self.attribs:
                        self.attribs.append(f.name())
        self.attribs.remove("changetype")
        self.attribs.insert(0, "changetype")
        if self.layers[0] is not None:
            layer = self.layers[0]
            fields = [f.name() for f in layer.pendingFields().toList()]
            features = layer.getFeatures()
            geogigidIdx = fields.index("geogigid")
            for feature in features:
                attrs = feature.attributes()
                geogigid = attrs[geogigidIdx]
                if not isinstance(geogigid, QtCore.QPyNullVariant):
                    featuredict = {}
                    for field in self.attribs:
                        try:
                            idx = fields.index(field)
                            value = attrs[idx]
                        except ValueError:
                            value = None
                        featuredict[field] = [value, None]
                    featuredict[GEOMETRY_FIELD] = [QgsGeometry(feature.geometry()), None]
                    self.features[geogigid] = featuredict
        if self.layers[1] is not None:
            layer = self.layers[1]
            fields = [f.name() for f in layer.pendingFields().toList()]
            features = layer.getFeatures()
            geogigidIdx = fields.index("geogigid")
            for feature in features:
                attrs = feature.attributes()
                geogigid = attrs[geogigidIdx]
                if not isinstance(geogigid, QtCore.QPyNullVariant):
                    if geogigid not in self.features:
                        self.features[geogigid] = {attr: [None, None] for attr in self.attribs}
                        self.features[geogigid][GEOMETRY_FIELD] = [None, None]
                    featuredict = self.features[geogigid]
                    for field in self.attribs:
                        try:
                            idx = fields.index(field)
                            value = attrs[idx]
                            featuredict[field][1] = value
                        except ValueError:
                            pass
                    featuredict[GEOMETRY_FIELD][1] = QgsGeometry(feature.geometry())

        self.attribs.insert(1, GEOMETRY_FIELD)
        self.geogigIdx = self.attribs.index("geogigid")

    def layerChanged(self):
        layername = self.ui.layerCombo.currentText()
        self.computeLayerDiffs(layername)

    def computeDiffs(self):
        self.commit1 = self.commit1Panel.getRef()
        self.commit2 = self.commit2Panel.getRef()

        self.unloadLayers()

        allchanges = exportVersionDiffs(self.commit1, self.commit2)
        self.allchanges = {}
        for path, layers in allchanges.iteritems():
            self.allchanges[path] = layers
            for layer in layers:
                if layer is not None:
                    QgsMapLayerRegistry.instance().addMapLayer(layer, False)
        if self.allchanges:
            self.layers = self.allchanges.values()[0]

            self.ui.layerCombo.blockSignals(True)
            self.ui.layerCombo.clear()
            self.ui.layerCombo.addItems(self.allchanges.keys())
            self.ui.layerCombo.blockSignals(False)

            self.computeLayerDiffs(self.allchanges.keys()[0])
        else:
            self.mapCanvas.clear()
            self.proxyModel.deleteLater()

    def computeLayerDiffs(self, layername):
        def _computeLayerDiff():
            self.layers = self.allchanges[layername]
            self.createTableDataFromLayers()
            self.geogigidIdx = self.attribs.index("geogigid")
            self.changeTypeIdx = self.attribs.index("changetype")

            model = DiffTableModel(self.attribs, self.features)
            self.proxyModel = QtGui.QSortFilterProxyModel()
            self.proxyModel.setSourceModel(model)
            self.ui.attributesTable.setModel(self.proxyModel)
            self.ui.attributesTable.selectionModel().selectionChanged.connect(self.selectionChanged)
            self.ui.attributesTable.setColumnHidden(self.geogigidIdx, True)
            self.ui.attributesTable.resizeColumnsToContents()
            self.ui.attributesTable.setVisible(False)
            vporig = self.ui.attributesTable.viewport().geometry()
            vpnew = vporig
            vpnew.setWidth(10000)
            self.ui.attributesTable.viewport().setGeometry(vpnew)
            self.ui.attributesTable.resizeRowsToContents()
            self.ui.attributesTable.viewport().setGeometry(vporig)
            self.ui.attributesTable.setVisible(True)

            self.showLayers()

        execute(_computeLayerDiff)

    def showLayers(self):
        layers = [lay for lay in self.layers if lay is not None]
        self.mapCanvas.setDestinationCrs(layers[0].crs())
        extent = self.getFullExtent()
        self.mapCanvas.setRenderFlag(False)
        self.mapCanvas.clear()
        visibility = [self.ui.compareLayerCheck.isChecked(), self.ui.baseLayerCheck.isChecked()]
        mapLayers = [QgsMapCanvasLayer(lay) for i, lay in enumerate(reversed(self.layers)) if visibility[i]]
        self.mapCanvas.setLayerSet(mapLayers)
        self.mapCanvas.setRenderFlag(True)
        self.mapCanvas.refresh()
        self.mapCanvas.setExtent(extent)

    def reject(self):
        QtGui.QDialog.reject(self)
        self.unloadLayers()

    def unloadLayers(self):
        for layers in self.allchanges.values():
            for layer in layers:
                if layer is not None:
                    QgsMapLayerRegistry.instance().removeMapLayer(layer.id())