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 Linguist 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 "formpreviewview.h"
30#include "messagemodel.h"
31
32#include <quiloader.h>
33
34#include <QtCore/QDebug>
35#include <QtCore/QTime>
36
37#include <QtWidgets/QAction>
38#include <QtWidgets/QApplication>
39#include <QtWidgets/QFontComboBox>
40#include <QtWidgets/QFrame>
41#include <QtWidgets/QGridLayout>
42#include <QtWidgets/QListWidget>
43#include <QtWidgets/QMdiArea>
44#include <QtWidgets/QMdiSubWindow>
45#include <QtWidgets/QMenu>
46#include <QtWidgets/QStackedLayout>
47#include <QtWidgets/QStackedWidget>
48#include <QtWidgets/QTableWidget>
49#include <QtWidgets/QTabWidget>
50#include <QtWidgets/QToolBox>
51#include <QtWidgets/QTreeWidget>
52#include <QtWidgets/QScrollArea>
53
54QT_BEGIN_NAMESPACE
55
56#if defined(Q_CC_SUN) || defined(Q_CC_HPACC) || defined(Q_CC_XLC)
57int qHash(const QUiTranslatableStringValue &tsv)
58#else
59static int qHash(const QUiTranslatableStringValue &tsv)
60#endif
61{
62 return qHash(key: tsv.value()) ^ qHash(key: tsv.qualifier());
63}
64
65static bool operator==(const QUiTranslatableStringValue &tsv1, const QUiTranslatableStringValue &tsv2)
66{
67 return tsv1.value() == tsv2.value() && tsv1.qualifier() == tsv2.qualifier();
68}
69
70#define INSERT_TARGET(_tsv, _type, _target, _prop) \
71 do { \
72 target.type = _type; \
73 target.target._target; \
74 target.prop._prop; \
75 (*targets)[qvariant_cast<QUiTranslatableStringValue>(_tsv)].append(target); \
76 } while (0)
77
78static void registerTreeItem(QTreeWidgetItem *item, TargetsHash *targets)
79{
80 const QUiItemRolePair *irs = QFormInternal::qUiItemRoles;
81
82 int cnt = item->columnCount();
83 for (int i = 0; i < cnt; ++i) {
84 for (unsigned j = 0; irs[j].shadowRole >= 0; j++) {
85 QVariant v = item->data(column: i, role: irs[j].shadowRole);
86 if (v.isValid()) {
87 TranslatableEntry target;
88 target.prop.treeIndex.column = i;
89 INSERT_TARGET(v, TranslatableTreeWidgetItem, treeWidgetItem = item, treeIndex.index = j);
90 }
91 }
92 }
93
94 cnt = item->childCount();
95 for (int j = 0; j < cnt; ++j)
96 registerTreeItem(item: item->child(index: j), targets);
97}
98
99#define REGISTER_ITEM_CORE(item, propType, targetName) \
100 const QUiItemRolePair *irs = QFormInternal::qUiItemRoles; \
101 for (unsigned j = 0; irs[j].shadowRole >= 0; j++) { \
102 QVariant v = item->data(irs[j].shadowRole); \
103 if (v.isValid()) \
104 INSERT_TARGET(v, propType, targetName = item, index = j); \
105 }
106
107static void registerListItem(QListWidgetItem *item, TargetsHash *targets)
108{
109 TranslatableEntry target;
110 REGISTER_ITEM_CORE(item, TranslatableListWidgetItem, listWidgetItem);
111}
112
113static void registerTableItem(QTableWidgetItem *item, TargetsHash *targets)
114{
115 if (!item)
116 return;
117
118 TranslatableEntry target;
119 REGISTER_ITEM_CORE(item, TranslatableTableWidgetItem, tableWidgetItem);
120}
121
122#define REGISTER_SUBWIDGET_PROP(mainWidget, propType, propName) \
123 do { \
124 QVariant v = mainWidget->widget(i)->property(propName); \
125 if (v.isValid()) \
126 INSERT_TARGET(v, propType, object = mainWidget, index = i); \
127 } while (0)
128
129static void buildTargets(QObject *o, TargetsHash *targets)
130{
131 TranslatableEntry target;
132
133 foreach (const QByteArray &prop, o->dynamicPropertyNames()) {
134 if (prop.startsWith(PROP_GENERIC_PREFIX)) {
135 const QByteArray propName = prop.mid(index: sizeof(PROP_GENERIC_PREFIX) - 1);
136 INSERT_TARGET(o->property(prop),
137 TranslatableProperty, object = o, name = qstrdup(propName.data()));
138 }
139 }
140 if (0) {
141#ifndef QT_NO_TABWIDGET
142 } else if (QTabWidget *tabw = qobject_cast<QTabWidget*>(object: o)) {
143 const int cnt = tabw->count();
144 for (int i = 0; i < cnt; ++i) {
145 REGISTER_SUBWIDGET_PROP(tabw, TranslatableTabPageText, PROP_TABPAGETEXT);
146# ifndef QT_NO_TOOLTIP
147 REGISTER_SUBWIDGET_PROP(tabw, TranslatableTabPageToolTip, PROP_TABPAGETOOLTIP);
148# endif
149# ifndef QT_NO_WHATSTHIS
150 REGISTER_SUBWIDGET_PROP(tabw, TranslatableTabPageWhatsThis, PROP_TABPAGEWHATSTHIS);
151# endif
152 }
153#endif
154#ifndef QT_NO_TOOLBOX
155 } else if (QToolBox *toolw = qobject_cast<QToolBox*>(object: o)) {
156 const int cnt = toolw->count();
157 for (int i = 0; i < cnt; ++i) {
158 REGISTER_SUBWIDGET_PROP(toolw, TranslatableToolItemText, PROP_TOOLITEMTEXT);
159# ifndef QT_NO_TOOLTIP
160 REGISTER_SUBWIDGET_PROP(toolw, TranslatableToolItemToolTip, PROP_TOOLITEMTOOLTIP);
161# endif
162 }
163#endif
164#ifndef QT_NO_COMBOBOX
165 } else if (QComboBox *combow = qobject_cast<QComboBox*>(object: o)) {
166 if (!qobject_cast<QFontComboBox*>(object: o)) {
167 const int cnt = combow->count();
168 for (int i = 0; i < cnt; ++i) {
169 const QVariant v = combow->itemData(index: i, role: Qt::DisplayPropertyRole);
170 if (v.isValid())
171 INSERT_TARGET(v, TranslatableComboBoxItem, comboBox = combow, index = i);
172 }
173 }
174#endif
175#ifndef QT_NO_LISTWIDGET
176 } else if (QListWidget *listw = qobject_cast<QListWidget*>(object: o)) {
177 const int cnt = listw->count();
178 for (int i = 0; i < cnt; ++i)
179 registerListItem(item: listw->item(row: i), targets);
180#endif
181#ifndef QT_NO_TABLEWIDGET
182 } else if (QTableWidget *tablew = qobject_cast<QTableWidget*>(object: o)) {
183 const int row_cnt = tablew->rowCount();
184 const int col_cnt = tablew->columnCount();
185 for (int j = 0; j < col_cnt; ++j)
186 registerTableItem(item: tablew->horizontalHeaderItem(column: j), targets);
187 for (int i = 0; i < row_cnt; ++i) {
188 registerTableItem(item: tablew->verticalHeaderItem(row: i), targets);
189 for (int j = 0; j < col_cnt; ++j)
190 registerTableItem(item: tablew->item(row: i, column: j), targets);
191 }
192#endif
193#ifndef QT_NO_TREEWIDGET
194 } else if (QTreeWidget *treew = qobject_cast<QTreeWidget*>(object: o)) {
195 if (QTreeWidgetItem *item = treew->headerItem())
196 registerTreeItem(item, targets);
197 const int cnt = treew->topLevelItemCount();
198 for (int i = 0; i < cnt; ++i)
199 registerTreeItem(item: treew->topLevelItem(index: i), targets);
200#endif
201 }
202 foreach (QObject *co, o->children())
203 buildTargets(o: co, targets);
204}
205
206static void destroyTargets(TargetsHash *targets)
207{
208 for (TargetsHash::Iterator it = targets->begin(), end = targets->end(); it != end; ++it)
209 foreach (const TranslatableEntry &target, *it)
210 if (target.type == TranslatableProperty)
211 delete target.prop.name;
212 targets->clear();
213}
214
215static void retranslateTarget(const TranslatableEntry &target, const QString &text)
216{
217 switch (target.type) {
218 case TranslatableProperty:
219 target.target.object->setProperty(name: target.prop.name, value: text);
220 break;
221#ifndef QT_NO_TABWIDGET
222 case TranslatableTabPageText:
223 target.target.tabWidget->setTabText(index: target.prop.index, text);
224 break;
225# ifndef QT_NO_TOOLTIP
226 case TranslatableTabPageToolTip:
227 target.target.tabWidget->setTabToolTip(index: target.prop.index, tip: text);
228 break;
229# endif
230# ifndef QT_NO_WHATSTHIS
231 case TranslatableTabPageWhatsThis:
232 target.target.tabWidget->setTabWhatsThis(index: target.prop.index, text);
233 break;
234# endif
235#endif // QT_NO_TABWIDGET
236#ifndef QT_NO_TOOLBOX
237 case TranslatableToolItemText:
238 target.target.toolBox->setItemText(index: target.prop.index, text);
239 break;
240# ifndef QT_NO_TOOLTIP
241 case TranslatableToolItemToolTip:
242 target.target.toolBox->setItemToolTip(index: target.prop.index, toolTip: text);
243 break;
244# endif
245#endif // QT_NO_TOOLBOX
246#ifndef QT_NO_COMBOBOX
247 case TranslatableComboBoxItem:
248 target.target.comboBox->setItemText(index: target.prop.index, text);
249 break;
250#endif
251#ifndef QT_NO_LISTWIDGET
252 case TranslatableListWidgetItem:
253 target.target.listWidgetItem->setData(role: target.prop.index, value: text);
254 break;
255#endif
256#ifndef QT_NO_TABLEWIDGET
257 case TranslatableTableWidgetItem:
258 target.target.tableWidgetItem->setData(role: target.prop.index, value: text);
259 break;
260#endif
261#ifndef QT_NO_TREEWIDGET
262 case TranslatableTreeWidgetItem:
263 target.target.treeWidgetItem->setData(column: target.prop.treeIndex.column, role: target.prop.treeIndex.index, value: text);
264 break;
265#endif
266 }
267}
268
269static void retranslateTargets(
270 const QList<TranslatableEntry> &targets, const QUiTranslatableStringValue &tsv,
271 const DataModel *dataModel, const QString &className)
272{
273 QString sourceText = QString::fromUtf8(str: tsv.value());
274 QString text;
275 if (MessageItem *msg = dataModel->findMessage(
276 context: className, sourcetext: sourceText, comment: QString::fromUtf8(str: tsv.qualifier())))
277 text = msg->translation();
278 if (text.isEmpty() && !tsv.value().isEmpty())
279 text = QLatin1Char('#') + sourceText;
280
281 foreach (const TranslatableEntry &target, targets)
282 retranslateTarget(target, text);
283}
284
285static void bringToFront(QWidget *w)
286{
287 for (; QWidget *pw = w->parentWidget(); w = pw) {
288#ifndef QT_NO_STACKEDWIDGET
289 if (QStackedWidget *stack = qobject_cast<QStackedWidget *>(object: pw)) {
290#ifndef QT_NO_TABWIDGET
291 // Updating QTabWidget's embedded QStackedWidget does not update its
292 // QTabBar, so handle tab widgets explicitly.
293 if (QTabWidget *tab = qobject_cast<QTabWidget *>(object: stack->parent()))
294 tab->setCurrentWidget(w);
295 else
296#endif
297 stack->setCurrentWidget(w);
298 continue;
299 }
300#endif
301#ifndef QT_NO_TOOLBOX
302 if (QScrollArea *sv = qobject_cast<QScrollArea *>(object: pw)) {
303 if (QToolBox *tb = qobject_cast<QToolBox *>(object: sv->parent()))
304 tb->setCurrentWidget(w);
305 }
306#endif
307 }
308}
309
310static void highlightTreeWidgetItem(QTreeWidgetItem *item, int col, bool on)
311{
312 QVariant br = item->data(column: col, role: Qt::BackgroundRole + 500);
313 QVariant fr = item->data(column: col, role: Qt::ForegroundRole + 500);
314 if (on) {
315 if (!br.isValid() && !fr.isValid()) {
316 item->setData(column: col, role: Qt::BackgroundRole + 500, value: item->data(column: col, role: Qt::BackgroundRole));
317 item->setData(column: col, role: Qt::ForegroundRole + 500, value: item->data(column: col, role: Qt::ForegroundRole));
318 QPalette pal = qApp->palette();
319 item->setData(column: col, role: Qt::BackgroundRole, value: pal.color(cr: QPalette::Dark));
320 item->setData(column: col, role: Qt::ForegroundRole, value: pal.color(cr: QPalette::Light));
321 }
322 } else {
323 if (br.isValid() || fr.isValid()) {
324 item->setData(column: col, role: Qt::BackgroundRole, value: br);
325 item->setData(column: col, role: Qt::ForegroundRole, value: fr);
326 item->setData(column: col, role: Qt::BackgroundRole + 500, value: QVariant());
327 item->setData(column: col, role: Qt::ForegroundRole + 500, value: QVariant());
328 }
329 }
330}
331
332template <class T>
333static void highlightWidgetItem(T *item, bool on)
334{
335 QVariant br = item->data(Qt::BackgroundRole + 500);
336 QVariant fr = item->data(Qt::ForegroundRole + 500);
337 if (on) {
338 if (!br.isValid() && !fr.isValid()) {
339 item->setData(Qt::BackgroundRole + 500, item->data(Qt::BackgroundRole));
340 item->setData(Qt::ForegroundRole + 500, item->data(Qt::ForegroundRole));
341 QPalette pal = qApp->palette();
342 item->setData(Qt::BackgroundRole, pal.color(cr: QPalette::Dark));
343 item->setData(Qt::ForegroundRole, pal.color(cr: QPalette::Light));
344 }
345 } else {
346 if (br.isValid() || fr.isValid()) {
347 item->setData(Qt::BackgroundRole, br);
348 item->setData(Qt::ForegroundRole, fr);
349 item->setData(Qt::BackgroundRole + 500, QVariant());
350 item->setData(Qt::ForegroundRole + 500, QVariant());
351 }
352 }
353}
354
355#define AUTOFILL_BACKUP_PROP "_q_linguist_autoFillBackup"
356#define PALETTE_BACKUP_PROP "_q_linguist_paletteBackup"
357#define FONT_BACKUP_PROP "_q_linguist_fontBackup"
358
359static void highlightWidget(QWidget *w, bool on);
360
361static void highlightAction(QAction *a, bool on)
362{
363 QVariant bak = a->property(FONT_BACKUP_PROP);
364 if (on) {
365 if (!bak.isValid()) {
366 QFont fnt = qApp->font();
367 a->setProperty(FONT_BACKUP_PROP, value: QVariant::fromValue(value: a->font().resolve(fnt)));
368 fnt.setBold(true);
369 fnt.setItalic(true);
370 a->setFont(fnt);
371 }
372 } else {
373 if (bak.isValid()) {
374 a->setFont(qvariant_cast<QFont>(v: bak));
375 a->setProperty(FONT_BACKUP_PROP, value: QVariant());
376 }
377 }
378 foreach (QWidget *w, a->associatedWidgets())
379 highlightWidget(w, on);
380}
381
382static void highlightWidget(QWidget *w, bool on)
383{
384 QVariant bak = w->property(PALETTE_BACKUP_PROP);
385 if (on) {
386 if (!bak.isValid()) {
387 QPalette pal = qApp->palette();
388 foreach (QObject *co, w->children())
389 if (QWidget *cw = qobject_cast<QWidget *>(o: co))
390 cw->setPalette(cw->palette().resolve(pal));
391 w->setProperty(PALETTE_BACKUP_PROP, value: QVariant::fromValue(value: w->palette().resolve(pal)));
392 w->setProperty(AUTOFILL_BACKUP_PROP, value: QVariant::fromValue(value: w->autoFillBackground()));
393 QColor col1 = pal.color(cr: QPalette::Dark);
394 QColor col2 = pal.color(cr: QPalette::Light);
395 pal.setColor(acr: QPalette::Base, acolor: col1);
396 pal.setColor(acr: QPalette::Window, acolor: col1);
397 pal.setColor(acr: QPalette::Button, acolor: col1);
398 pal.setColor(acr: QPalette::Text, acolor: col2);
399 pal.setColor(acr: QPalette::WindowText, acolor: col2);
400 pal.setColor(acr: QPalette::ButtonText, acolor: col2);
401 pal.setColor(acr: QPalette::BrightText, acolor: col2);
402 w->setPalette(pal);
403 w->setAutoFillBackground(true);
404 }
405 } else {
406 if (bak.isValid()) {
407 w->setPalette(qvariant_cast<QPalette>(v: bak));
408 w->setAutoFillBackground(qvariant_cast<bool>(v: w->property(AUTOFILL_BACKUP_PROP)));
409 w->setProperty(PALETTE_BACKUP_PROP, value: QVariant());
410 w->setProperty(AUTOFILL_BACKUP_PROP, value: QVariant());
411 }
412 }
413 if (QMenu *m = qobject_cast<QMenu *>(object: w))
414 if (m->menuAction())
415 highlightAction(a: m->menuAction(), on);
416}
417
418static void highlightTarget(const TranslatableEntry &target, bool on)
419{
420 switch (target.type) {
421 case TranslatableProperty:
422 if (QAction *a = qobject_cast<QAction *>(object: target.target.object)) {
423 highlightAction(a, on);
424 } else if (QWidget *w = qobject_cast<QWidget *>(o: target.target.object)) {
425 bringToFront(w);
426 highlightWidget(w, on);
427 }
428 break;
429#ifndef QT_NO_COMBOBOX
430 case TranslatableComboBoxItem:
431 static_cast<QComboBox *>(target.target.object)->setCurrentIndex(target.prop.index);
432 goto frontAndHighlight;
433#endif
434#ifndef QT_NO_TABWIDGET
435 case TranslatableTabPageText:
436 static_cast<QTabWidget *>(target.target.object)->setCurrentIndex(target.prop.index);
437 goto frontAndHighlight;
438# ifndef QT_NO_TOOLTIP
439 case TranslatableTabPageToolTip:
440# endif
441# ifndef QT_NO_WHATSTHIS
442 case TranslatableTabPageWhatsThis:
443# endif
444#endif // QT_NO_TABWIDGET
445#ifndef QT_NO_TOOLBOX
446 case TranslatableToolItemText:
447# ifndef QT_NO_TOOLTIP
448 case TranslatableToolItemToolTip:
449# endif
450#endif // QT_NO_TOOLBOX
451#if !defined(QT_NO_COMBOBOX) || !defined(QT_NO_TABWIDGET)
452 frontAndHighlight:
453#endif
454 bringToFront(w: static_cast<QWidget *>(target.target.object));
455 highlightWidget(w: static_cast<QWidget *>(target.target.object), on);
456 break;
457#ifndef QT_NO_LISTWIDGET
458 case TranslatableListWidgetItem:
459 bringToFront(w: target.target.listWidgetItem->listWidget());
460 highlightWidgetItem(item: target.target.listWidgetItem, on);
461 break;
462#endif
463#ifndef QT_NO_TABLEWIDGET
464 case TranslatableTableWidgetItem:
465 bringToFront(w: target.target.tableWidgetItem->tableWidget());
466 highlightWidgetItem(item: target.target.tableWidgetItem, on);
467 break;
468#endif
469#ifndef QT_NO_TREEWIDGET
470 case TranslatableTreeWidgetItem:
471 bringToFront(w: target.target.treeWidgetItem->treeWidget());
472 highlightTreeWidgetItem(item: target.target.treeWidgetItem, col: target.prop.treeIndex.column, on);
473 break;
474#endif
475 }
476}
477
478static void highlightTargets(const QList<TranslatableEntry> &targets, bool on)
479{
480 foreach (const TranslatableEntry &target, targets)
481 highlightTarget(target, on);
482}
483
484FormPreviewView::FormPreviewView(QWidget *parent, MultiDataModel *dataModel)
485 : QMainWindow(parent), m_form(0), m_dataModel(dataModel)
486{
487 m_mdiSubWindow = new QMdiSubWindow;
488 m_mdiSubWindow->setWindowFlags(m_mdiSubWindow->windowFlags() & ~Qt::WindowSystemMenuHint);
489 m_mdiArea = new QMdiArea(this);
490 m_mdiArea->addSubWindow(widget: m_mdiSubWindow);
491 setCentralWidget(m_mdiArea);
492 m_mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
493 m_mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
494}
495
496void FormPreviewView::setSourceContext(int model, MessageItem *messageItem)
497{
498 if (model < 0 || !messageItem) {
499 m_lastModel = -1;
500 return;
501 }
502
503 QDir dir = QFileInfo(m_dataModel->srcFileName(model)).dir();
504 QString fileName = QDir::cleanPath(path: dir.absoluteFilePath(fileName: messageItem->fileName()));
505 if (m_lastFormName != fileName) {
506 delete m_form;
507 m_form = 0;
508 m_lastFormName.clear();
509 m_highlights.clear();
510 destroyTargets(targets: &m_targets);
511
512 static QUiLoader *uiLoader;
513 if (!uiLoader) {
514 uiLoader = new QUiLoader(this);
515 uiLoader->setLanguageChangeEnabled(true);
516 uiLoader->setTranslationEnabled(false);
517 }
518
519 QFile file(fileName);
520 if (!file.open(flags: QIODevice::ReadOnly)) {
521 qDebug() << "CANNOT OPEN FORM" << fileName;
522 m_mdiSubWindow->hide();
523 return;
524 }
525 m_form = uiLoader->load(device: &file, parentWidget: m_mdiSubWindow);
526 if (!m_form) {
527 qDebug() << "CANNOT LOAD FORM" << fileName;
528 m_mdiSubWindow->hide();
529 return;
530 }
531 file.close();
532 buildTargets(o: m_form, targets: &m_targets);
533
534 setToolTip(fileName);
535
536 m_form->setWindowFlags(Qt::Widget);
537 m_form->setWindowModality(Qt::NonModal);
538 m_form->setFocusPolicy(Qt::NoFocus);
539 m_form->show(); // needed, otherwide the Qt::NoFocus is not propagated.
540 m_mdiSubWindow->setWidget(m_form);
541 m_mdiSubWindow->setWindowTitle(m_form->windowTitle());
542 m_mdiSubWindow->show();
543 m_mdiArea->cascadeSubWindows();
544 m_lastFormName = fileName;
545 m_lastClassName = messageItem->context();
546 m_lastModel = -1;
547 } else {
548 highlightTargets(targets: m_highlights, on: false);
549 }
550 QUiTranslatableStringValue tsv;
551 tsv.setValue(messageItem->text().toUtf8());
552 tsv.setQualifier(messageItem->comment().toUtf8());
553 m_highlights = m_targets.value(akey: tsv);
554 if (m_lastModel != model) {
555 for (TargetsHash::Iterator it = m_targets.begin(), end = m_targets.end(); it != end; ++it)
556 retranslateTargets(targets: *it, tsv: it.key(), dataModel: m_dataModel->model(i: model), className: m_lastClassName);
557 m_lastModel = model;
558 } else {
559 retranslateTargets(targets: m_highlights, tsv, dataModel: m_dataModel->model(i: model), className: m_lastClassName);
560 }
561 highlightTargets(targets: m_highlights, on: true);
562}
563
564QT_END_NAMESPACE
565

source code of qttools/src/linguist/linguist/formpreviewview.cpp