1# qfold.py - QFold dialog for TortoiseHg
2#
3# Copyright 2010 Steve Borho <steve@borho.org>
4# Copyright 2010 Johan Samyn <johan.samyn@gmail.com>
5#
6# This software may be used and distributed according to the terms of the
7# GNU General Public License version 2, incorporated herein by reference.
8
9from __future__ import absolute_import
10
11from .qtcore import (
12    QSettings,
13    Qt,
14    pyqtSlot,
15)
16from .qtgui import (
17    QCheckBox,
18    QDialog,
19    QDialogButtonBox,
20    QGroupBox,
21    QLabel,
22    QListView,
23    QListWidget,
24    QListWidgetItem,
25    QShortcut,
26    QTextEdit,
27    QVBoxLayout,
28)
29
30from hgext import mq
31from mercurial import (
32    context,
33    pycompat,
34)
35
36from ..util import hglib
37from ..util.i18n import _
38from . import (
39    messageentry,
40    qscilib,
41    qtlib,
42)
43
44class QFoldDialog(QDialog):
45
46    def __init__(self, repoagent, patches, parent):
47        super(QFoldDialog, self).__init__(parent)
48        self._repoagent = repoagent
49        self.setWindowTitle(_('Patch fold - %s') % repoagent.displayName())
50        self.setWindowIcon(qtlib.geticon('hg-qfold'))
51
52        f = self.windowFlags()
53        self.setWindowFlags(f & ~Qt.WindowContextHelpButtonHint
54                            | Qt.WindowMaximizeButtonHint)
55
56        self.setLayout(QVBoxLayout())
57
58        mlbl = QLabel(_('New patch message:'))
59        self.layout().addWidget(mlbl)
60        self.msgte = messageentry.MessageEntry(self)
61        self.msgte.installEventFilter(qscilib.KeyPressInterceptor(self))
62        self.layout().addWidget(self.msgte)
63
64        self.keepchk = QCheckBox(_('Keep patch files'))
65        self.keepchk.setChecked(True)
66        self.layout().addWidget(self.keepchk)
67
68        q = self.repo.mq
69        q.parseseries()
70        patches = [p for p in q.series if p in patches]
71
72        class PatchListWidget(QListWidget):
73            def __init__(self, parent):
74                QListWidget.__init__(self, parent)
75                self.setCurrentRow(0)
76            def focusInEvent(self, event):
77                i = self.item(self.currentRow())
78                if i:
79                    self.parent().parent().showSummary(i)
80                QListWidget.focusInEvent(self, event)
81            def dropEvent(self, event):
82                QListWidget.dropEvent(self, event)
83                spp = self.parent().parent()
84                spp.msgte.setText(spp.composeMsg(self.getPatchList()))
85            def getPatchList(self):
86                return [hglib.fromunicode(self.item(i).text()) \
87                        for i in pycompat.xrange(0, self.count())]
88
89        ugb = QGroupBox(_('Patches to fold'))
90        ugb.setLayout(QVBoxLayout())
91        ugb.layout().setContentsMargins(*(0,)*4)
92        self.ulw = PatchListWidget(self)
93        self.ulw.setDragDropMode(QListView.InternalMove)
94        ugb.layout().addWidget(self.ulw)
95        self.ulw.currentItemChanged.connect(lambda:
96                self.showSummary(self.ulw.item(self.ulw.currentRow())))
97        self.layout().addWidget(ugb)
98
99        for p in patches:
100            item = QListWidgetItem(hglib.tounicode(p))
101            item.setFlags(Qt.ItemIsSelectable |
102                          Qt.ItemIsEnabled |
103                          Qt.ItemIsDragEnabled)
104            self.ulw.addItem(item)
105
106        slbl = QLabel(_('Summary:'))
107        self.layout().addWidget(slbl)
108        self.summ = QTextEdit()
109        self.summ.setFont(qtlib.getfont('fontcomment').font())
110        self.summ.setMaximumHeight(80)
111        self.summ.setReadOnly(True)
112        self.summ.setFocusPolicy(Qt.NoFocus)
113        self.layout().addWidget(self.summ)
114
115        BB = QDialogButtonBox
116        bbox = QDialogButtonBox(BB.Ok|BB.Cancel)
117        bbox.accepted.connect(self.accept)
118        bbox.rejected.connect(self.reject)
119        self.layout().addWidget(bbox)
120        self.bbox = bbox
121
122        QShortcut('Ctrl+Return', self, self.accept)
123        QShortcut('Ctrl+Enter', self, self.accept)
124
125        self._repoagent.configChanged.connect(self.configChanged)
126
127        self._readsettings()
128
129        self.msgte.setText(self.composeMsg(patches))
130        self.msgte.refresh(self.repo)
131
132    @property
133    def repo(self):
134        return self._repoagent.rawRepo()
135
136    def showSummary(self, item):
137        patchname = hglib.fromunicode(item.text())
138        txt = '\n'.join(mq.patchheader(self.repo.mq.join(patchname)).message)
139        self.summ.setText(hglib.tounicode(txt))
140
141    def composeMsg(self, patches):
142        descs = [hglib.revsymbol(self.repo, b'qtip').description()]
143        # lookup of unapplied patches is handled by thgrepo hack
144        descs.extend(self.repo[p].description() for p in patches)
145        return u'\n* * *\n'.join(map(hglib.tounicode, descs))
146
147    @pyqtSlot()
148    def configChanged(self):
149        '''Repository is reporting its config files have changed'''
150        self.msgte.refresh(self.repo)
151
152    def options(self):
153        return {'keep': self.keepchk.isChecked(),
154                'message': pycompat.unicode(self.msgte.text())}
155
156    def patches(self):
157        return pycompat.maplist(hglib.tounicode, self.ulw.getPatchList())
158
159    def accept(self):
160        self._writesettings()
161        QDialog.accept(self)
162
163    def closeEvent(self, event):
164        self._writesettings()
165        super(QFoldDialog, self).closeEvent(event)
166
167    def _readsettings(self):
168        s = QSettings()
169        self.restoreGeometry(qtlib.readByteArray(s, 'qfold/geom'))
170
171    def _writesettings(self):
172        s = QSettings()
173        s.setValue('qfold/geom', self.saveGeometry())
174