1# topic.py - Topic dialog for TortoiseHg
2#
3# Copyright 2010 Michal De Wildt <michael.dewildt@gmail.com>
4#
5# This software may be used and distributed according to the terms of the
6# GNU General Public License version 2, incorporated herein by reference.
7
8from __future__ import absolute_import
9
10from mercurial import error
11
12from .qtcore import (
13    Qt,
14    pyqtSlot,
15)
16from .qtgui import (
17    QComboBox,
18    QDialog,
19    QDialogButtonBox,
20    QFontMetrics,
21    QFormLayout,
22    QFrame,
23    QLabel,
24    QLayout,
25    QSizePolicy,
26    QVBoxLayout,
27    QWidget,
28)
29
30from mercurial import (
31    pycompat,
32)
33
34from ..util import hglib
35from ..util.obsoleteutil import first_known_successors
36from ..util.i18n import _
37from . import (
38    cmdcore,
39    qtlib,
40)
41
42class TopicDialog(QDialog):
43
44    def __init__(self, repoagent, rev, parent=None):
45        super(TopicDialog, self).__init__(parent)
46        self.setWindowFlags(self.windowFlags() &
47                            ~Qt.WindowContextHelpButtonHint)
48        self._repoagent = repoagent
49        repo = repoagent.rawRepo()
50        self._cmdsession = cmdcore.nullCmdSession()
51        self.rev = rev
52
53        # base layout box
54        base = QVBoxLayout()
55        base.setSpacing(0)
56        base.setContentsMargins(*(0,)*4)
57        base.setSizeConstraint(QLayout.SetMinAndMaxSize)
58        self.setLayout(base)
59
60        # main layout grid
61        formwidget = QWidget(self)
62        formwidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
63        form = QFormLayout(fieldGrowthPolicy=QFormLayout.AllNonFixedFieldsGrow)
64        formwidget.setLayout(form)
65        base.addWidget(formwidget)
66
67        self.revLabel = QLabel()  # text is set in self.revUpdated()
68        form.addRow(_('Revision:'), self.revLabel)
69
70        # topic combo
71        self.topicsCombo = QComboBox()
72        self.topicsCombo.setEditable(True)
73        self.topicsCombo.setMinimumContentsLength(30)  # cut long name
74        self.topicsCombo.currentIndexChanged.connect(self.topicTextChanged)
75        self.topicsCombo.editTextChanged.connect(self.topicTextChanged)
76        qtlib.allowCaseChangingInput(self.topicsCombo)
77        form.addRow(_('Topic:'), self.topicsCombo)
78
79        # bottom buttons
80        BB = QDialogButtonBox
81        bbox = QDialogButtonBox()
82        self.setBtn = bbox.addButton(_('&Set'), BB.ActionRole)
83        self.clearBtn = bbox.addButton(_('&Clear'), BB.ActionRole)
84        bbox.addButton(BB.Close)
85        bbox.rejected.connect(self.reject)
86        form.addRow(bbox)
87
88        self.setBtn.clicked.connect(self.set_topic)
89        self.clearBtn.clicked.connect(self.clear_topic)
90
91        # horizontal separator
92        self.sep = QFrame()
93        self.sep.setFrameShadow(QFrame.Sunken)
94        self.sep.setFrameShape(QFrame.HLine)
95        self.layout().addWidget(self.sep)
96
97        # status line
98        self.status = qtlib.StatusLabel()
99        self.status.setContentsMargins(4, 2, 4, 4)
100        self.layout().addWidget(self.status)
101        self._finishmsg = None
102
103        # dialog setting
104        self.setWindowTitle(_('Topic - %s') % repoagent.displayName())
105        self.setWindowIcon(qtlib.geticon('hg-topics'))
106
107        # prepare to show
108        self.clear_status()
109        self.revUpdated()
110        self.refresh()
111        self._repoagent.repositoryChanged.connect(self.refresh)
112        self.topicsCombo.setFocus()
113        self.topicTextChanged()
114
115    def revUpdated(self):
116        if self.rev is None:
117            hasunicodestar = QFontMetrics(self.font()).inFont(u'\u2605')
118            if hasunicodestar:
119                # The Unicode symbol is a black star:
120                revText = u'\u2605 ' + _('Working Directory') + u' \u2605'
121            else:
122                revText = '*** ' + _('Working Directory') + ' ***'
123        else:
124            revText = '%d (%s)' % (self.rev, self.repo[self.rev])
125        self.revLabel.setText(revText)
126        self.topicsCombo.setEditText(self._current_topic)
127
128    @property
129    def repo(self):
130        return self._repoagent.rawRepo()
131
132    @pyqtSlot()
133    def refresh(self):
134        """Update drop-down list if repo changed."""
135        cur = self.topicsCombo.currentText()
136        self.topicsCombo.clear()
137        self.topicsCombo.addItems(sorted(map(hglib.tounicode,
138                                             self.repo.topics)))
139        self.topicsCombo.setEditText(cur)
140
141    @pyqtSlot()
142    def topicTextChanged(self):
143        topic = self.topicsCombo.currentText()
144        self.setBtn.setEnabled(bool(topic))
145
146    @property
147    def _current_topic(self):
148        return hglib.tounicode(self.repo[self.rev].topic())
149
150    def setTopicName(self, name):
151        self.topicsCombo.setEditText(name)
152
153    def set_status(self, text, icon=None):
154        self.status.setVisible(True)
155        self.sep.setVisible(True)
156        self.status.set_status(text, icon)
157
158    def clear_status(self):
159        self.status.setHidden(True)
160        self.sep.setHidden(True)
161
162    def _runTopic(self, *args, **opts):
163        self._finishmsg = opts.pop('finishmsg')
164        cmdline = hglib.buildcmdargs('topic', *args, **opts)
165        self._cmdsession = sess = self._repoagent.runCommand(cmdline, self)
166        sess.commandFinished.connect(self._onTopicFinished)
167
168    @pyqtSlot(int)
169    def _onTopicFinished(self, ret):
170        if ret == 0:
171            self.set_status(self._finishmsg, True)
172        else:
173            self.set_status(self._cmdsession.errorString(), False)
174        repo = self.repo.unfiltered()
175        ctx = repo[self.rev]
176        if ctx.extinct():
177            changes = [x for x in first_known_successors(ctx)]
178            if changes:
179                self.rev = changes[0].rev()
180        self.revUpdated()
181
182    @pyqtSlot()
183    def set_topic(self):
184        topic = pycompat.unicode(self.topicsCombo.currentText())
185        finishmsg = _("Set topic to '%s'") % topic
186        self._runTopic(topic, rev=self.rev, finishmsg=finishmsg)
187
188    @pyqtSlot()
189    def clear_topic(self):
190        finishmsg = _("Cleared current topic '%s'") % self._current_topic
191        self._runTopic(rev=self.rev, clear=True, finishmsg=finishmsg)
192