File indexing completed on 2024-04-21 05:40:58

0001 /*
0002     SPDX-FileCopyrightText: 2010 Sebastian Doerner <sebastian@sebastian-doerner.de>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "checkoutdialog.h"
0008 #include "gitwrapper.h"
0009 
0010 #include <KConfigGroup>
0011 #include <KLocalizedString>
0012 
0013 #include <QCheckBox>
0014 #include <QComboBox>
0015 #include <QGridLayout>
0016 #include <QGroupBox>
0017 #include <QLineEdit>
0018 #include <QRadioButton>
0019 #include <QString>
0020 #include <QVBoxLayout>
0021 #include <QDialogButtonBox>
0022 #include <QPushButton>
0023 #include <QRegularExpression>
0024 
0025 CheckoutDialog::CheckoutDialog(QWidget* parent):
0026     QDialog(parent, Qt::Dialog),
0027     m_userEditedNewBranchName(false)
0028 {
0029     //branch/tag selection
0030     this->setWindowTitle(xi18nc("@title:window", "<application>Git</application> Checkout"));
0031     m_buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel);
0032     QWidget *mainWidget = new QWidget(this);
0033     QVBoxLayout *mainLayout = new QVBoxLayout;
0034     this->setLayout(mainLayout);
0035     mainLayout->addWidget(mainWidget);
0036     QPushButton *okButton = m_buttonBox->button(QDialogButtonBox::Ok);
0037     okButton->setDefault(true);
0038     okButton->setShortcut(Qt::CTRL | Qt::Key_Return);
0039     this->connect(m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
0040     this->connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
0041     okButton->setText(i18nc("@action:button", "Checkout"));
0042 
0043     QWidget *boxWidget = new QWidget(this);
0044     QVBoxLayout *boxLayout = new QVBoxLayout(boxWidget);
0045     mainLayout->addWidget(boxWidget);
0046 
0047     m_branchSelectGroupBox = new QGroupBox(boxWidget);
0048     mainLayout->addWidget(m_branchSelectGroupBox);
0049     boxLayout->addWidget(m_branchSelectGroupBox);
0050 
0051     QGridLayout * gridLayout = new QGridLayout(m_branchSelectGroupBox);
0052     m_branchSelectGroupBox->setLayout(gridLayout);
0053 
0054     m_branchRadioButton = new QRadioButton(i18nc("@option:radio Git Checkout","Branch:"), m_branchSelectGroupBox);
0055     m_branchRadioButton->setChecked(true);
0056     gridLayout->addWidget(m_branchRadioButton,0,0);
0057     m_branchRadioButton->setFocus();
0058     m_branchComboBox = new QComboBox(m_branchSelectGroupBox);
0059     gridLayout->addWidget(m_branchComboBox,0,1);
0060 
0061     QRadioButton * tagRadioButton = new QRadioButton(i18nc("@option:radio Git Checkout", "Tag:"), m_branchSelectGroupBox);
0062     gridLayout->addWidget(tagRadioButton,1,0);
0063     m_tagComboBox = new QComboBox(m_branchSelectGroupBox);
0064     m_tagComboBox->setEnabled(false);
0065     gridLayout->addWidget(m_tagComboBox,1,1);
0066 
0067     //options
0068     QGroupBox * optionsGroupBox = new QGroupBox(boxWidget);
0069     mainLayout->addWidget(optionsGroupBox);
0070     boxLayout->addWidget(optionsGroupBox);
0071     optionsGroupBox->setTitle(i18nc("@title:group", "Options"));
0072 
0073     QGridLayout * optionsGridLayout = new QGridLayout(optionsGroupBox);
0074     optionsGroupBox->setLayout(optionsGridLayout);
0075 
0076     m_newBranchCheckBox = new QCheckBox(i18nc("@option:check", "Create New Branch: "), optionsGroupBox);
0077     m_newBranchCheckBox->setToolTip(i18nc("@info:tooltip", "Create a new branch based on a selected branch or tag."));
0078     optionsGridLayout->addWidget(m_newBranchCheckBox, 0,0);
0079 
0080     mainLayout->addWidget(m_buttonBox);
0081 
0082     m_newBranchName = new QLineEdit(optionsGroupBox);
0083     m_newBranchName->setMinimumWidth(150);
0084     m_newBranchName->setClearButtonEnabled(true);
0085     optionsGridLayout->addWidget(m_newBranchName, 0, 1);
0086 
0087     //initialize alternate color scheme for errors
0088     m_errorColors = m_newBranchName->palette();
0089     m_errorColors.setColor(QPalette::Normal, QPalette::Base, Qt::red);
0090     m_errorColors.setColor(QPalette::Inactive, QPalette::Base, Qt::red);
0091 
0092     m_forceCheckBox = new QCheckBox(i18nc("@option:check", "Force"), optionsGroupBox);
0093     m_forceCheckBox->setToolTip(i18nc("@info:tooltip", "Discard local changes."));
0094     optionsGridLayout->addWidget(m_forceCheckBox, 1,0);
0095 
0096     //get branch names
0097     GitWrapper * gitWrapper = GitWrapper::instance();
0098     int currentBranchIndex;
0099     const QStringList branches = gitWrapper->branches(&currentBranchIndex);
0100 
0101     m_branchComboBox->addItems(branches);
0102     if (currentBranchIndex == -1) {
0103         m_branchComboBox->insertItem(0, QStringLiteral("(no branch)"));
0104         m_branchComboBox->setCurrentIndex(0);
0105     } else {
0106         m_branchComboBox->setCurrentIndex(currentBranchIndex);
0107     }
0108     setDefaultNewBranchName(m_branchComboBox->currentText());
0109 
0110     //keep local branches to prevent creating an equally named new branch
0111     for (const QString& b : branches) {
0112         if (!b.startsWith(QLatin1String("remotes/"))) {
0113             //you CAN create local branches called "remotes/...", but since no sane person
0114             //would do that, we save the effort of another call to "git branch"
0115             m_branchNames.insert(b);
0116         }
0117     }
0118 
0119     //get tag names
0120     const QStringList tags = gitWrapper->tags();
0121     m_tagComboBox->addItems(tags);
0122     m_tagComboBox->setCurrentIndex(m_tagComboBox->count()-1);
0123     if (m_tagComboBox->count() == 0){
0124         tagRadioButton->setEnabled(false);
0125         const QString tooltip = i18nc("@info:tooltip", "There are no tags in this repository.");
0126         tagRadioButton->setToolTip(tooltip);
0127         m_tagComboBox->setToolTip(tooltip);
0128     }
0129 
0130     //signals
0131     connect(m_branchRadioButton, &QAbstractButton::toggled,
0132             this, &CheckoutDialog::branchRadioButtonToggled);
0133     connect(m_branchComboBox, SIGNAL(currentIndexChanged(QString)),
0134             this, SLOT(setDefaultNewBranchName(QString)));
0135     connect(m_branchComboBox, SIGNAL(currentIndexChanged(QString)),
0136             this, SLOT(setOkButtonState()));
0137     connect(m_tagComboBox, SIGNAL(currentIndexChanged(QString)),
0138             this, SLOT(setDefaultNewBranchName(QString)));
0139     connect(m_newBranchCheckBox, &QCheckBox::stateChanged,
0140              this, &CheckoutDialog::newBranchCheckBoxStateToggled);
0141     connect(m_newBranchName, &QLineEdit::textChanged,
0142             this, &CheckoutDialog::setOkButtonState);
0143     connect(m_newBranchName, &QLineEdit::textEdited,
0144             this, &CheckoutDialog::noteUserEditedNewBranchName);
0145     //set initial widget states
0146     newBranchCheckBoxStateToggled(Qt::Unchecked);
0147 }
0148 
0149 QString CheckoutDialog::checkoutIdentifier() const
0150 {
0151     QString identifier = m_branchComboBox->isEnabled() ? m_branchComboBox->currentText() : m_tagComboBox->currentText();
0152     if (identifier.length()==0 || identifier.at(0)==QLatin1Char('(')) {
0153         identifier = QString();
0154     }
0155     return identifier;
0156 }
0157 
0158 bool CheckoutDialog::force() const
0159 {
0160     return m_forceCheckBox->isChecked();
0161 }
0162 
0163 QString CheckoutDialog::newBranchName() const
0164 {
0165     if (m_newBranchCheckBox->isChecked()) {
0166         return m_newBranchName->text().trimmed();
0167     } else {
0168         return QString();
0169     }
0170 }
0171 
0172 void CheckoutDialog::branchRadioButtonToggled(bool checked)
0173 {
0174     m_branchComboBox->setEnabled(checked);
0175     m_tagComboBox->setEnabled(!checked);
0176     setDefaultNewBranchName(checked ? m_branchComboBox->currentText() : m_tagComboBox->currentText());
0177     setOkButtonState();
0178 }
0179 
0180 void CheckoutDialog::newBranchCheckBoxStateToggled(int state)
0181 {
0182     m_newBranchName->setEnabled(state == Qt::Checked);
0183     m_branchSelectGroupBox->setTitle(
0184         state == Qt::Checked ?
0185         i18nc("@title:group", "Branch Base") :
0186         i18nc("@title:group", "Checkout"));
0187     if (state == Qt::Checked) {
0188         m_newBranchName->setFocus(Qt::TabFocusReason);
0189     }
0190     setOkButtonState();
0191 }
0192 
0193 void CheckoutDialog::setOkButtonState()
0194 {
0195     //default to enabled
0196     bool enableButton = true;
0197     bool newNameError = false;
0198     QPushButton *okButton = m_buttonBox->button(QDialogButtonBox::Ok);
0199 
0200     //------------disable on these conditions---------------//
0201     if (m_newBranchCheckBox->isChecked()) {
0202         const QString newBranchName = m_newBranchName->text().trimmed();
0203         if (newBranchName.isEmpty()){
0204             enableButton = false;
0205             newNameError = true;
0206             const QString tt = i18nc("@info:tooltip", "You must enter a valid name for the new branch first.");
0207             m_newBranchName->setToolTip(tt);
0208             okButton->setToolTip(tt);
0209         }
0210         if (m_branchNames.contains(newBranchName)) {
0211             enableButton = false;
0212             newNameError = true;
0213             const QString tt = i18nc("@info:tooltip", "A branch with the name '%1' already exists.", newBranchName);
0214             m_newBranchName->setToolTip(tt);
0215             okButton->setToolTip(tt);
0216         }
0217         if (newBranchName.contains(QRegularExpression(QStringLiteral("\\s")))) {
0218             enableButton = false;
0219             newNameError = true;
0220             const QString tt = i18nc("@info:tooltip", "Branch names may not contain any whitespace.");
0221             m_newBranchName->setToolTip(tt);
0222             okButton->setToolTip(tt);
0223         }
0224     } //if we create a new branch and no valid branch is selected we create one based on the currently checked out version
0225     else if (m_branchRadioButton->isChecked() && m_branchComboBox->currentText().at(0) == QLatin1Char('(')){
0226         enableButton = false;
0227         okButton->setToolTip(i18nc("@info:tooltip", "You must select a valid branch first."));
0228     }
0229     //------------------------------------------------------//
0230 
0231     setLineEditErrorModeActive(newNameError);
0232     okButton->setEnabled(enableButton);
0233     if (!newNameError) {
0234         m_newBranchName->setToolTip(QString());
0235     }
0236     if (enableButton) {
0237         okButton->setToolTip(QString());
0238     }
0239 }
0240 
0241 void CheckoutDialog::setDefaultNewBranchName(const QString& baseBranchName)
0242 {
0243     if (!m_userEditedNewBranchName) {
0244         if (baseBranchName.startsWith(QLatin1Char('('))) {
0245             m_newBranchName->setText(QString());
0246         } else {
0247             m_newBranchName->setText(i18nc("@item:intext Prepended to the current branch name "
0248             "to get the default name for a newly created branch", "branch") + QLatin1Char('_') + baseBranchName);
0249         }
0250     }
0251 }
0252 
0253 void CheckoutDialog::noteUserEditedNewBranchName()
0254 {
0255     m_userEditedNewBranchName = true;
0256 }
0257 
0258 void CheckoutDialog::setLineEditErrorModeActive(bool active)
0259 {
0260     m_newBranchName->setPalette(active ? m_errorColors : QPalette());
0261 }
0262 
0263 
0264 #include "moc_checkoutdialog.cpp"