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