1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the Qt Designer of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21 ** included in the packaging of this file. Please review the following
22 ** information to ensure the GNU General Public License requirements will
23 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24 **
25 ** $QT_END_LICENSE$
26 **
27 ****************************************************************************/
28 
29 #include "codedialog_p.h"
30 #include "qdesigner_utils_p.h"
31 #include "iconloader_p.h"
32 
33 #include <texteditfindwidget.h>
34 
35 #include <QtWidgets/qaction.h>
36 #include <QtWidgets/qapplication.h>
37 #if QT_CONFIG(clipboard)
38 #include <QtGui/qclipboard.h>
39 #endif
40 #include <QtWidgets/qdialogbuttonbox.h>
41 #include <QtWidgets/qfiledialog.h>
42 #include <QtGui/qicon.h>
43 #include <QtGui/qevent.h>
44 #include <QtWidgets/qmessagebox.h>
45 #include <QtWidgets/qpushbutton.h>
46 #include <QtWidgets/qtextedit.h>
47 #include <QtWidgets/qtoolbar.h>
48 #include <QtWidgets/qboxlayout.h>
49 
50 #include <QtCore/qdebug.h>
51 #include <QtCore/qdir.h>
52 #include <QtCore/qmimedatabase.h>
53 #include <QtCore/qtemporaryfile.h>
54 
55 QT_BEGIN_NAMESPACE
56 
57 namespace qdesigner_internal {
58 // ----------------- CodeDialogPrivate
59 struct CodeDialog::CodeDialogPrivate {
60     CodeDialogPrivate();
61 
62     QTextEdit *m_textEdit;
63     TextEditFindWidget *m_findWidget;
64     QString m_formFileName;
65     QString m_mimeType;
66 };
67 
CodeDialogPrivate()68 CodeDialog::CodeDialogPrivate::CodeDialogPrivate()
69     : m_textEdit(new QTextEdit)
70     , m_findWidget(new TextEditFindWidget)
71 {
72 }
73 
74 // ----------------- CodeDialog
CodeDialog(QWidget * parent)75 CodeDialog::CodeDialog(QWidget *parent) :
76     QDialog(parent),
77     m_impl(new CodeDialogPrivate)
78 {
79     setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
80     QVBoxLayout *vBoxLayout = new QVBoxLayout;
81 
82     // Edit tool bar
83     QToolBar *toolBar = new QToolBar;
84 
85     const QIcon saveIcon = createIconSet(QStringLiteral("filesave.png"));
86     QAction *saveAction = toolBar->addAction(saveIcon, tr("Save..."));
87     connect(saveAction, &QAction::triggered, this, &CodeDialog::slotSaveAs);
88 
89 #if QT_CONFIG(clipboard)
90     const QIcon copyIcon = createIconSet(QStringLiteral("editcopy.png"));
91     QAction *copyAction = toolBar->addAction(copyIcon, tr("Copy All"));
92     connect(copyAction, &QAction::triggered, this, &CodeDialog::copyAll);
93 #endif
94 
95     toolBar->addAction(m_impl->m_findWidget->createFindAction(toolBar));
96 
97     vBoxLayout->addWidget(toolBar);
98 
99     // Edit
100     m_impl->m_textEdit->setReadOnly(true);
101     m_impl->m_textEdit->setMinimumSize(QSize(
102                 m_impl->m_findWidget->minimumSize().width(),
103                 500));
104     vBoxLayout->addWidget(m_impl->m_textEdit);
105 
106     // Find
107     m_impl->m_findWidget->setTextEdit(m_impl->m_textEdit);
108     vBoxLayout->addWidget(m_impl->m_findWidget);
109 
110     // Button box
111     QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close);
112     connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
113 
114     // Disable auto default
115     QPushButton *closeButton = buttonBox->button(QDialogButtonBox::Close);
116     closeButton->setAutoDefault(false);
117     vBoxLayout->addWidget(buttonBox);
118 
119     setLayout(vBoxLayout);
120 }
121 
~CodeDialog()122 CodeDialog::~CodeDialog()
123 {
124     delete m_impl;
125 }
126 
setCode(const QString & code)127 void CodeDialog::setCode(const QString &code)
128 {
129     m_impl->m_textEdit->setPlainText(code);
130 }
131 
code() const132 QString CodeDialog::code() const
133 {
134    return m_impl->m_textEdit->toPlainText();
135 }
136 
setFormFileName(const QString & f)137 void CodeDialog::setFormFileName(const QString &f)
138 {
139     m_impl->m_formFileName = f;
140 }
141 
formFileName() const142 QString CodeDialog::formFileName() const
143 {
144     return m_impl->m_formFileName;
145 }
146 
setMimeType(const QString & m)147 void CodeDialog::setMimeType(const QString &m)
148 {
149     m_impl->m_mimeType = m;
150 }
151 
generateCode(const QDesignerFormWindowInterface * fw,UicLanguage language,QString * code,QString * errorMessage)152 bool CodeDialog::generateCode(const QDesignerFormWindowInterface *fw,
153                               UicLanguage language,
154                               QString *code,
155                               QString *errorMessage)
156 {
157     // Generate temporary file name similar to form file name
158     // (for header guards)
159     QString tempPattern = QDir::tempPath();
160     if (!tempPattern.endsWith(QDir::separator())) // platform-dependant
161         tempPattern += QDir::separator();
162     const QString fileName = fw->fileName();
163     if (fileName.isEmpty()) {
164         tempPattern += QStringLiteral("designer");
165     } else {
166         tempPattern += QFileInfo(fileName).baseName();
167     }
168     tempPattern += QStringLiteral("XXXXXX.ui");
169     // Write to temp file
170     QTemporaryFile tempFormFile(tempPattern);
171 
172     tempFormFile.setAutoRemove(true);
173     if (!tempFormFile.open()) {
174         *errorMessage = tr("A temporary form file could not be created in %1.").arg(QDir::tempPath());
175         return false;
176     }
177     const QString tempFormFileName = tempFormFile.fileName();
178     tempFormFile.write(fw->contents().toUtf8());
179     if (!tempFormFile.flush())  {
180         *errorMessage = tr("The temporary form file %1 could not be written.").arg(tempFormFileName);
181         return false;
182     }
183     tempFormFile.close();
184     // Run uic
185     QByteArray rc;
186     if (!runUIC(tempFormFileName, language, rc, *errorMessage))
187         return false;
188     *code = QString::fromUtf8(rc);
189     return true;
190 }
191 
showCodeDialog(const QDesignerFormWindowInterface * fw,UicLanguage language,QWidget * parent,QString * errorMessage)192 bool CodeDialog::showCodeDialog(const QDesignerFormWindowInterface *fw,
193                                 UicLanguage language,
194                                 QWidget *parent,
195                                 QString *errorMessage)
196 {
197     QString code;
198     if (!generateCode(fw, language, &code, errorMessage))
199         return false;
200 
201     auto dialog = new CodeDialog(parent);
202     dialog->setModal(false);
203     dialog->setAttribute(Qt::WA_DeleteOnClose);
204     dialog->setCode(code);
205     dialog->setFormFileName(fw->fileName());
206     QString languageName;
207     switch (language) {
208     case UicLanguage::Cpp:
209         languageName = QLatin1String("C++");
210         dialog->setMimeType(QLatin1String("text/x-chdr"));
211         break;
212     case UicLanguage::Python:
213         languageName = QLatin1String("Python");
214         dialog->setMimeType(QLatin1String("text/x-python"));
215         break;
216     }
217     dialog->setWindowTitle(tr("%1 - [%2 Code]").
218                            arg(fw->mainContainer()->windowTitle(), languageName));
219     dialog->show();
220     return true;
221 }
222 
slotSaveAs()223 void CodeDialog::slotSaveAs()
224 {
225     // build the default relative name 'ui_sth.h'
226     QMimeDatabase mimeDb;
227     const QString suffix = mimeDb.mimeTypeForName(m_impl->m_mimeType).preferredSuffix();
228 
229     // file dialog
230     QFileDialog fileDialog(this, tr("Save Code"));
231     fileDialog.setMimeTypeFilters(QStringList(m_impl->m_mimeType));
232     fileDialog.setAcceptMode(QFileDialog::AcceptSave);
233     fileDialog.setDefaultSuffix(suffix);
234     const QString uiFile = formFileName();
235     if (!uiFile.isEmpty()) {
236         QFileInfo uiFi(uiFile);
237         fileDialog.setDirectory(uiFi.absolutePath());
238         fileDialog.selectFile(QLatin1String("ui_") + uiFi.baseName()
239                               + QLatin1Char('.') + suffix);
240     }
241 
242     while (true) {
243         if (fileDialog.exec() != QDialog::Accepted)
244             break;
245         const QString fileName = fileDialog.selectedFiles().constFirst();
246 
247          QFile file(fileName);
248          if (!file.open(QIODevice::WriteOnly|QIODevice::Text)) {
249              warning(tr("The file %1 could not be opened: %2")
250                      .arg(fileName, file.errorString()));
251              continue;
252          }
253          file.write(code().toUtf8());
254          if (!file.flush()) {
255              warning(tr("The file %1 could not be written: %2")
256                      .arg(fileName, file.errorString()));
257              continue;
258          }
259          file.close();
260          break;
261     }
262 }
263 
warning(const QString & msg)264 void CodeDialog::warning(const QString &msg)
265 {
266      QMessageBox::warning(
267              this, tr("%1 - Error").arg(windowTitle()),
268              msg, QMessageBox::Close);
269 }
270 
271 #if QT_CONFIG(clipboard)
copyAll()272 void CodeDialog::copyAll()
273 {
274     QApplication::clipboard()->setText(code());
275 }
276 #endif
277 
278 } // namespace qdesigner_internal
279 
280 QT_END_NAMESPACE
281