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 | |
68 | CodeDialog::CodeDialogPrivate::CodeDialogPrivate() |
69 | : m_textEdit(new QTextEdit) |
70 | , m_findWidget(new TextEditFindWidget) |
71 | { |
72 | } |
73 | |
74 | // ----------------- CodeDialog |
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(icon: saveIcon, text: tr(s: "Save..." )); |
87 | connect(sender: saveAction, signal: &QAction::triggered, receiver: this, slot: &CodeDialog::slotSaveAs); |
88 | |
89 | #if QT_CONFIG(clipboard) |
90 | const QIcon copyIcon = createIconSet(QStringLiteral("editcopy.png" )); |
91 | QAction *copyAction = toolBar->addAction(icon: copyIcon, text: tr(s: "Copy All" )); |
92 | connect(sender: copyAction, signal: &QAction::triggered, receiver: this, slot: &CodeDialog::copyAll); |
93 | #endif |
94 | |
95 | toolBar->addAction(action: m_impl->m_findWidget->createFindAction(parent: 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(sender: buttonBox, signal: &QDialogButtonBox::rejected, receiver: this, slot: &QDialog::reject); |
113 | |
114 | // Disable auto default |
115 | QPushButton *closeButton = buttonBox->button(which: QDialogButtonBox::Close); |
116 | closeButton->setAutoDefault(false); |
117 | vBoxLayout->addWidget(buttonBox); |
118 | |
119 | setLayout(vBoxLayout); |
120 | } |
121 | |
122 | CodeDialog::~CodeDialog() |
123 | { |
124 | delete m_impl; |
125 | } |
126 | |
127 | void CodeDialog::setCode(const QString &code) |
128 | { |
129 | m_impl->m_textEdit->setPlainText(code); |
130 | } |
131 | |
132 | QString CodeDialog::code() const |
133 | { |
134 | return m_impl->m_textEdit->toPlainText(); |
135 | } |
136 | |
137 | void CodeDialog::setFormFileName(const QString &f) |
138 | { |
139 | m_impl->m_formFileName = f; |
140 | } |
141 | |
142 | QString CodeDialog::formFileName() const |
143 | { |
144 | return m_impl->m_formFileName; |
145 | } |
146 | |
147 | void CodeDialog::setMimeType(const QString &m) |
148 | { |
149 | m_impl->m_mimeType = m; |
150 | } |
151 | |
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(c: 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(s: "A temporary form file could not be created in %1." ).arg(a: QDir::tempPath()); |
175 | return false; |
176 | } |
177 | const QString tempFormFileName = tempFormFile.fileName(); |
178 | tempFormFile.write(data: fw->contents().toUtf8()); |
179 | if (!tempFormFile.flush()) { |
180 | *errorMessage = tr(s: "The temporary form file %1 could not be written." ).arg(a: tempFormFileName); |
181 | return false; |
182 | } |
183 | tempFormFile.close(); |
184 | // Run uic |
185 | QByteArray rc; |
186 | if (!runUIC(fileName: tempFormFileName, language, ba&: rc, errorMessage&: *errorMessage)) |
187 | return false; |
188 | *code = QString::fromUtf8(str: rc); |
189 | return true; |
190 | } |
191 | |
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: &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(s: "%1 - [%2 Code]" ). |
218 | arg(args: fw->mainContainer()->windowTitle(), args&: languageName)); |
219 | dialog->show(); |
220 | return true; |
221 | } |
222 | |
223 | void CodeDialog::slotSaveAs() |
224 | { |
225 | // build the default relative name 'ui_sth.h' |
226 | QMimeDatabase mimeDb; |
227 | const QString suffix = mimeDb.mimeTypeForName(nameOrAlias: m_impl->m_mimeType).preferredSuffix(); |
228 | |
229 | // file dialog |
230 | QFileDialog fileDialog(this, tr(s: "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(filename: 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(flags: QIODevice::WriteOnly|QIODevice::Text)) { |
249 | warning(msg: tr(s: "The file %1 could not be opened: %2" ) |
250 | .arg(args: fileName, args: file.errorString())); |
251 | continue; |
252 | } |
253 | file.write(data: code().toUtf8()); |
254 | if (!file.flush()) { |
255 | warning(msg: tr(s: "The file %1 could not be written: %2" ) |
256 | .arg(args: fileName, args: file.errorString())); |
257 | continue; |
258 | } |
259 | file.close(); |
260 | break; |
261 | } |
262 | } |
263 | |
264 | void CodeDialog::warning(const QString &msg) |
265 | { |
266 | QMessageBox::warning( |
267 | parent: this, title: tr(s: "%1 - Error" ).arg(a: windowTitle()), |
268 | text: msg, buttons: QMessageBox::Close); |
269 | } |
270 | |
271 | #if QT_CONFIG(clipboard) |
272 | void CodeDialog::copyAll() |
273 | { |
274 | QApplication::clipboard()->setText(code()); |
275 | } |
276 | #endif |
277 | |
278 | } // namespace qdesigner_internal |
279 | |
280 | QT_END_NAMESPACE |
281 | |