examples/graphicsview/diagramscene/mainwindow.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/graphicsview/diagramscene/mainwindow.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,652 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtGui>
+#include <QLabel>
+
+#include "mainwindow.h"
+#include "diagramitem.h"
+#include "diagramscene.h"
+#include "diagramtextitem.h"
+
+const int InsertTextButton = 10;
+
+//! [0]
+MainWindow::MainWindow()
+{
+    createActions();
+    createToolBox();
+    createMenus();
+
+    scene = new DiagramScene(itemMenu);
+    scene->setSceneRect(QRectF(0, 0, 5000, 5000));
+    connect(scene, SIGNAL(itemInserted(DiagramItem *)),
+            this, SLOT(itemInserted(DiagramItem *)));
+    connect(scene, SIGNAL(textInserted(QGraphicsTextItem *)),
+        this, SLOT(textInserted(QGraphicsTextItem *)));
+    connect(scene, SIGNAL(itemSelected(QGraphicsItem *)),
+        this, SLOT(itemSelected(QGraphicsItem *)));
+    createToolbars();
+
+    QHBoxLayout *layout = new QHBoxLayout;
+    layout->addWidget(toolBox);
+    view = new QGraphicsView(scene);
+    layout->addWidget(view);
+
+    QWidget *widget = new QWidget;
+    widget->setLayout(layout);
+
+    setCentralWidget(widget);
+    setWindowTitle(tr("Diagramscene"));
+    setUnifiedTitleAndToolBarOnMac(true);
+}
+//! [0]
+
+//! [1]
+void MainWindow::backgroundButtonGroupClicked(QAbstractButton *button)
+{
+    QList<QAbstractButton *> buttons = backgroundButtonGroup->buttons();
+    foreach (QAbstractButton *myButton, buttons) {
+    if (myButton != button)
+        button->setChecked(false);
+    }
+    QString text = button->text();
+    if (text == tr("Blue Grid"))
+        scene->setBackgroundBrush(QPixmap(":/images/background1.png"));
+    else if (text == tr("White Grid"))
+        scene->setBackgroundBrush(QPixmap(":/images/background2.png"));
+    else if (text == tr("Gray Grid"))
+        scene->setBackgroundBrush(QPixmap(":/images/background3.png"));
+    else
+        scene->setBackgroundBrush(QPixmap(":/images/background4.png"));
+
+    scene->update();
+    view->update();
+}
+//! [1]
+
+//! [2]
+void MainWindow::buttonGroupClicked(int id)
+{
+    QList<QAbstractButton *> buttons = buttonGroup->buttons();
+    foreach (QAbstractButton *button, buttons) {
+    if (buttonGroup->button(id) != button)
+        button->setChecked(false);
+    }
+    if (id == InsertTextButton) {
+        scene->setMode(DiagramScene::InsertText);
+    } else {
+        scene->setItemType(DiagramItem::DiagramType(id));
+        scene->setMode(DiagramScene::InsertItem);
+    }
+}
+//! [2]
+
+//! [3]
+void MainWindow::deleteItem()
+{
+    foreach (QGraphicsItem *item, scene->selectedItems()) {
+        if (item->type() == DiagramItem::Type) {
+            qgraphicsitem_cast<DiagramItem *>(item)->removeArrows();
+        }
+        scene->removeItem(item);
+    }
+}
+//! [3]
+
+//! [4]
+void MainWindow::pointerGroupClicked(int)
+{
+    scene->setMode(DiagramScene::Mode(pointerTypeGroup->checkedId()));
+}
+//! [4]
+
+//! [5]
+void MainWindow::bringToFront()
+{
+    if (scene->selectedItems().isEmpty())
+        return;
+
+    QGraphicsItem *selectedItem = scene->selectedItems().first();
+    QList<QGraphicsItem *> overlapItems = selectedItem->collidingItems();
+
+    qreal zValue = 0;
+    foreach (QGraphicsItem *item, overlapItems) {
+        if (item->zValue() >= zValue &&
+            item->type() == DiagramItem::Type)
+            zValue = item->zValue() + 0.1;
+    }
+    selectedItem->setZValue(zValue);
+}
+//! [5]
+
+//! [6]
+void MainWindow::sendToBack()
+{
+    if (scene->selectedItems().isEmpty())
+        return;
+
+    QGraphicsItem *selectedItem = scene->selectedItems().first();
+    QList<QGraphicsItem *> overlapItems = selectedItem->collidingItems();
+
+    qreal zValue = 0;
+    foreach (QGraphicsItem *item, overlapItems) {
+        if (item->zValue() <= zValue &&
+            item->type() == DiagramItem::Type)
+            zValue = item->zValue() - 0.1;
+    }
+    selectedItem->setZValue(zValue);
+}
+//! [6]
+
+//! [7]
+void MainWindow::itemInserted(DiagramItem *item)
+{
+    pointerTypeGroup->button(int(DiagramScene::MoveItem))->setChecked(true);
+    scene->setMode(DiagramScene::Mode(pointerTypeGroup->checkedId()));
+    buttonGroup->button(int(item->diagramType()))->setChecked(false);
+}
+//! [7]
+
+//! [8]
+void MainWindow::textInserted(QGraphicsTextItem *)
+{
+    buttonGroup->button(InsertTextButton)->setChecked(false);
+    scene->setMode(DiagramScene::Mode(pointerTypeGroup->checkedId()));
+}
+//! [8]
+
+//! [9]
+void MainWindow::currentFontChanged(const QFont &)
+{
+    handleFontChange();
+}
+//! [9]
+
+//! [10]
+void MainWindow::fontSizeChanged(const QString &)
+{
+    handleFontChange();
+}
+//! [10]
+
+//! [11]
+void MainWindow::sceneScaleChanged(const QString &scale)
+{
+    double newScale = scale.left(scale.indexOf(tr("%"))).toDouble() / 100.0;
+    QMatrix oldMatrix = view->matrix();
+    view->resetMatrix();
+    view->translate(oldMatrix.dx(), oldMatrix.dy());
+    view->scale(newScale, newScale);
+}
+//! [11]
+
+//! [12]
+void MainWindow::textColorChanged()
+{
+    textAction = qobject_cast<QAction *>(sender());
+    fontColorToolButton->setIcon(createColorToolButtonIcon(
+                ":/images/textpointer.png",
+                qVariantValue<QColor>(textAction->data())));
+    textButtonTriggered();
+}
+//! [12]
+
+//! [13]
+void MainWindow::itemColorChanged()
+{
+    fillAction = qobject_cast<QAction *>(sender());
+    fillColorToolButton->setIcon(createColorToolButtonIcon(
+                 ":/images/floodfill.png",
+                 qVariantValue<QColor>(fillAction->data())));
+    fillButtonTriggered();
+}
+//! [13]
+
+//! [14]
+void MainWindow::lineColorChanged()
+{
+    lineAction = qobject_cast<QAction *>(sender());
+    lineColorToolButton->setIcon(createColorToolButtonIcon(
+                 ":/images/linecolor.png",
+                 qVariantValue<QColor>(lineAction->data())));
+    lineButtonTriggered();
+}
+//! [14]
+
+//! [15]
+void MainWindow::textButtonTriggered()
+{
+    scene->setTextColor(qVariantValue<QColor>(textAction->data()));
+}
+//! [15]
+
+//! [16]
+void MainWindow::fillButtonTriggered()
+{
+    scene->setItemColor(qVariantValue<QColor>(fillAction->data()));
+}
+//! [16]
+
+//! [17]
+void MainWindow::lineButtonTriggered()
+{
+    scene->setLineColor(qVariantValue<QColor>(lineAction->data()));
+}
+//! [17]
+
+//! [18]
+void MainWindow::handleFontChange()
+{
+    QFont font = fontCombo->currentFont();
+    font.setPointSize(fontSizeCombo->currentText().toInt());
+    font.setWeight(boldAction->isChecked() ? QFont::Bold : QFont::Normal);
+    font.setItalic(italicAction->isChecked());
+    font.setUnderline(underlineAction->isChecked());
+
+    scene->setFont(font);
+}
+//! [18]
+
+//! [19]
+void MainWindow::itemSelected(QGraphicsItem *item)
+{
+    DiagramTextItem *textItem =
+    qgraphicsitem_cast<DiagramTextItem *>(item);
+
+    QFont font = textItem->font();
+    QColor color = textItem->defaultTextColor();
+    fontCombo->setCurrentFont(font);
+    fontSizeCombo->setEditText(QString().setNum(font.pointSize()));
+    boldAction->setChecked(font.weight() == QFont::Bold);
+    italicAction->setChecked(font.italic());
+    underlineAction->setChecked(font.underline());
+}
+//! [19]
+
+//! [20]
+void MainWindow::about()
+{
+    QMessageBox::about(this, tr("About Diagram Scene"),
+                       tr("The <b>Diagram Scene</b> example shows "
+                          "use of the graphics framework."));
+}
+//! [20]
+
+//! [21]
+void MainWindow::createToolBox()
+{
+    buttonGroup = new QButtonGroup;
+    buttonGroup->setExclusive(false);
+    connect(buttonGroup, SIGNAL(buttonClicked(int)),
+            this, SLOT(buttonGroupClicked(int)));
+    QGridLayout *layout = new QGridLayout;
+    layout->addWidget(createCellWidget(tr("Conditional"),
+                               DiagramItem::Conditional), 0, 0);
+    layout->addWidget(createCellWidget(tr("Process"),
+                      DiagramItem::Step),0, 1);
+    layout->addWidget(createCellWidget(tr("Input/Output"),
+                      DiagramItem::Io), 1, 0);
+//! [21]
+
+    QToolButton *textButton = new QToolButton;
+    textButton->setCheckable(true);
+    buttonGroup->addButton(textButton, InsertTextButton);
+    textButton->setIcon(QIcon(QPixmap(":/images/textpointer.png")
+                        .scaled(30, 30)));
+    textButton->setIconSize(QSize(50, 50));
+    QGridLayout *textLayout = new QGridLayout;
+    textLayout->addWidget(textButton, 0, 0, Qt::AlignHCenter);
+    textLayout->addWidget(new QLabel(tr("Text")), 1, 0, Qt::AlignCenter);
+    QWidget *textWidget = new QWidget;
+    textWidget->setLayout(textLayout);
+    layout->addWidget(textWidget, 1, 1);
+
+    layout->setRowStretch(3, 10);
+    layout->setColumnStretch(2, 10);
+
+    QWidget *itemWidget = new QWidget;
+    itemWidget->setLayout(layout);
+
+    backgroundButtonGroup = new QButtonGroup;
+    connect(backgroundButtonGroup, SIGNAL(buttonClicked(QAbstractButton *)),
+            this, SLOT(backgroundButtonGroupClicked(QAbstractButton *)));
+
+    QGridLayout *backgroundLayout = new QGridLayout;
+    backgroundLayout->addWidget(createBackgroundCellWidget(tr("Blue Grid"),
+                ":/images/background1.png"), 0, 0);
+    backgroundLayout->addWidget(createBackgroundCellWidget(tr("White Grid"),
+                ":/images/background2.png"), 0, 1);
+    backgroundLayout->addWidget(createBackgroundCellWidget(tr("Gray Grid"),
+                    ":/images/background3.png"), 1, 0);
+    backgroundLayout->addWidget(createBackgroundCellWidget(tr("No Grid"),
+                ":/images/background4.png"), 1, 1);
+
+    backgroundLayout->setRowStretch(2, 10);
+    backgroundLayout->setColumnStretch(2, 10);
+
+    QWidget *backgroundWidget = new QWidget;
+    backgroundWidget->setLayout(backgroundLayout);
+
+
+//! [22]
+    toolBox = new QToolBox;
+    toolBox->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Ignored));
+    toolBox->setMinimumWidth(itemWidget->sizeHint().width());
+    toolBox->addItem(itemWidget, tr("Basic Flowchart Shapes"));
+    toolBox->addItem(backgroundWidget, tr("Backgrounds"));
+}
+//! [22]
+
+//! [23]
+void MainWindow::createActions()
+{
+    toFrontAction = new QAction(QIcon(":/images/bringtofront.png"),
+                                tr("Bring to &Front"), this);
+    toFrontAction->setShortcut(tr("Ctrl+F"));
+    toFrontAction->setStatusTip(tr("Bring item to front"));
+    connect(toFrontAction, SIGNAL(triggered()),
+            this, SLOT(bringToFront()));
+//! [23]
+
+    sendBackAction = new QAction(QIcon(":/images/sendtoback.png"),
+                                 tr("Send to &Back"), this);
+    sendBackAction->setShortcut(tr("Ctrl+B"));
+    sendBackAction->setStatusTip(tr("Send item to back"));
+    connect(sendBackAction, SIGNAL(triggered()),
+        this, SLOT(sendToBack()));
+
+    deleteAction = new QAction(QIcon(":/images/delete.png"),
+                               tr("&Delete"), this);
+    deleteAction->setShortcut(tr("Delete"));
+    deleteAction->setStatusTip(tr("Delete item from diagram"));
+    connect(deleteAction, SIGNAL(triggered()),
+        this, SLOT(deleteItem()));
+
+    exitAction = new QAction(tr("E&xit"), this);
+    exitAction->setShortcuts(QKeySequence::Quit);
+    exitAction->setStatusTip(tr("Quit Scenediagram example"));
+    connect(exitAction, SIGNAL(triggered()), this, SLOT(close()));
+
+    boldAction = new QAction(tr("Bold"), this);
+    boldAction->setCheckable(true);
+    QPixmap pixmap(":/images/bold.png");
+    boldAction->setIcon(QIcon(pixmap));
+    boldAction->setShortcut(tr("Ctrl+B"));
+    connect(boldAction, SIGNAL(triggered()),
+            this, SLOT(handleFontChange()));
+
+    italicAction = new QAction(QIcon(":/images/italic.png"),
+                               tr("Italic"), this);
+    italicAction->setCheckable(true);
+    italicAction->setShortcut(tr("Ctrl+I"));
+    connect(italicAction, SIGNAL(triggered()),
+            this, SLOT(handleFontChange()));
+
+    underlineAction = new QAction(QIcon(":/images/underline.png"),
+                                  tr("Underline"), this);
+    underlineAction->setCheckable(true);
+    underlineAction->setShortcut(tr("Ctrl+U"));
+    connect(underlineAction, SIGNAL(triggered()),
+            this, SLOT(handleFontChange()));
+
+    aboutAction = new QAction(tr("A&bout"), this);
+    aboutAction->setShortcut(tr("Ctrl+B"));
+    connect(aboutAction, SIGNAL(triggered()),
+            this, SLOT(about()));
+}
+
+//! [24]
+void MainWindow::createMenus()
+{
+    fileMenu = menuBar()->addMenu(tr("&File"));
+    fileMenu->addAction(exitAction);
+
+    itemMenu = menuBar()->addMenu(tr("&Item"));
+    itemMenu->addAction(deleteAction);
+    itemMenu->addSeparator();
+    itemMenu->addAction(toFrontAction);
+    itemMenu->addAction(sendBackAction);
+
+    aboutMenu = menuBar()->addMenu(tr("&Help"));
+    aboutMenu->addAction(aboutAction);
+}
+//! [24]
+
+//! [25]
+void MainWindow::createToolbars()
+{
+//! [25]
+    editToolBar = addToolBar(tr("Edit"));
+    editToolBar->addAction(deleteAction);
+    editToolBar->addAction(toFrontAction);
+    editToolBar->addAction(sendBackAction);
+
+    fontCombo = new QFontComboBox();
+    fontSizeCombo = new QComboBox();
+    connect(fontCombo, SIGNAL(currentFontChanged(const QFont &)),
+            this, SLOT(currentFontChanged(const QFont &)));
+
+    fontSizeCombo = new QComboBox;
+    fontSizeCombo->setEditable(true);
+    for (int i = 8; i < 30; i = i + 2)
+        fontSizeCombo->addItem(QString().setNum(i));
+    QIntValidator *validator = new QIntValidator(2, 64, this);
+    fontSizeCombo->setValidator(validator);
+    connect(fontSizeCombo, SIGNAL(currentIndexChanged(const QString &)),
+            this, SLOT(fontSizeChanged(const QString &)));
+
+    fontColorToolButton = new QToolButton;
+    fontColorToolButton->setPopupMode(QToolButton::MenuButtonPopup);
+    fontColorToolButton->setMenu(createColorMenu(SLOT(textColorChanged()),
+                                                 Qt::black));
+    textAction = fontColorToolButton->menu()->defaultAction();
+    fontColorToolButton->setIcon(createColorToolButtonIcon(
+    ":/images/textpointer.png", Qt::black));
+    fontColorToolButton->setAutoFillBackground(true);
+    connect(fontColorToolButton, SIGNAL(clicked()),
+            this, SLOT(textButtonTriggered()));
+
+//! [26]
+    fillColorToolButton = new QToolButton;
+    fillColorToolButton->setPopupMode(QToolButton::MenuButtonPopup);
+    fillColorToolButton->setMenu(createColorMenu(SLOT(itemColorChanged()),
+                         Qt::white));
+    fillAction = fillColorToolButton->menu()->defaultAction();
+    fillColorToolButton->setIcon(createColorToolButtonIcon(
+    ":/images/floodfill.png", Qt::white));
+    connect(fillColorToolButton, SIGNAL(clicked()),
+            this, SLOT(fillButtonTriggered()));
+//! [26]
+
+    lineColorToolButton = new QToolButton;
+    lineColorToolButton->setPopupMode(QToolButton::MenuButtonPopup);
+    lineColorToolButton->setMenu(createColorMenu(SLOT(lineColorChanged()),
+                                 Qt::black));
+    lineAction = lineColorToolButton->menu()->defaultAction();
+    lineColorToolButton->setIcon(createColorToolButtonIcon(
+        ":/images/linecolor.png", Qt::black));
+    connect(lineColorToolButton, SIGNAL(clicked()),
+            this, SLOT(lineButtonTriggered()));
+
+    textToolBar = addToolBar(tr("Font"));
+    textToolBar->addWidget(fontCombo);
+    textToolBar->addWidget(fontSizeCombo);
+    textToolBar->addAction(boldAction);
+    textToolBar->addAction(italicAction);
+    textToolBar->addAction(underlineAction);
+
+    colorToolBar = addToolBar(tr("Color"));
+    colorToolBar->addWidget(fontColorToolButton);
+    colorToolBar->addWidget(fillColorToolButton);
+    colorToolBar->addWidget(lineColorToolButton);
+
+    QToolButton *pointerButton = new QToolButton;
+    pointerButton->setCheckable(true);
+    pointerButton->setChecked(true);
+    pointerButton->setIcon(QIcon(":/images/pointer.png"));
+    QToolButton *linePointerButton = new QToolButton;
+    linePointerButton->setCheckable(true);
+    linePointerButton->setIcon(QIcon(":/images/linepointer.png"));
+
+    pointerTypeGroup = new QButtonGroup;
+    pointerTypeGroup->addButton(pointerButton, int(DiagramScene::MoveItem));
+    pointerTypeGroup->addButton(linePointerButton,
+                                int(DiagramScene::InsertLine));
+    connect(pointerTypeGroup, SIGNAL(buttonClicked(int)),
+            this, SLOT(pointerGroupClicked(int)));
+
+    sceneScaleCombo = new QComboBox;
+    QStringList scales;
+    scales << tr("50%") << tr("75%") << tr("100%") << tr("125%") << tr("150%");
+    sceneScaleCombo->addItems(scales);
+    sceneScaleCombo->setCurrentIndex(2);
+    connect(sceneScaleCombo, SIGNAL(currentIndexChanged(const QString &)),
+            this, SLOT(sceneScaleChanged(const QString &)));
+
+    pointerToolbar = addToolBar(tr("Pointer type"));
+    pointerToolbar->addWidget(pointerButton);
+    pointerToolbar->addWidget(linePointerButton);
+    pointerToolbar->addWidget(sceneScaleCombo);
+//! [27]
+}
+//! [27]
+
+//! [28]
+QWidget *MainWindow::createBackgroundCellWidget(const QString &text,
+                        const QString &image)
+{
+    QToolButton *button = new QToolButton;
+    button->setText(text);
+    button->setIcon(QIcon(image));
+    button->setIconSize(QSize(50, 50));
+    button->setCheckable(true);
+    backgroundButtonGroup->addButton(button);
+
+    QGridLayout *layout = new QGridLayout;
+    layout->addWidget(button, 0, 0, Qt::AlignHCenter);
+    layout->addWidget(new QLabel(text), 1, 0, Qt::AlignCenter);
+
+    QWidget *widget = new QWidget;
+    widget->setLayout(layout);
+
+    return widget;
+}
+//! [28]
+
+//! [29]
+QWidget *MainWindow::createCellWidget(const QString &text,
+                      DiagramItem::DiagramType type)
+{
+
+    DiagramItem item(type, itemMenu);
+    QIcon icon(item.image());
+
+    QToolButton *button = new QToolButton;
+    button->setIcon(icon);
+    button->setIconSize(QSize(50, 50));
+    button->setCheckable(true);
+    buttonGroup->addButton(button, int(type));
+
+    QGridLayout *layout = new QGridLayout;
+    layout->addWidget(button, 0, 0, Qt::AlignHCenter);
+    layout->addWidget(new QLabel(text), 1, 0, Qt::AlignCenter);
+
+    QWidget *widget = new QWidget;
+    widget->setLayout(layout);
+
+    return widget;
+}
+//! [29]
+
+//! [30]
+QMenu *MainWindow::createColorMenu(const char *slot, QColor defaultColor)
+{
+    QList<QColor> colors;
+    colors << Qt::black << Qt::white << Qt::red << Qt::blue << Qt::yellow;
+    QStringList names;
+    names << tr("black") << tr("white") << tr("red") << tr("blue")
+          << tr("yellow");
+
+    QMenu *colorMenu = new QMenu;
+    for (int i = 0; i < colors.count(); ++i) {
+        QAction *action = new QAction(names.at(i), this);
+        action->setData(colors.at(i));
+        action->setIcon(createColorIcon(colors.at(i)));
+        connect(action, SIGNAL(triggered()),
+                this, slot);
+        colorMenu->addAction(action);
+        if (colors.at(i) == defaultColor) {
+            colorMenu->setDefaultAction(action);
+        }
+    }
+    return colorMenu;
+}
+//! [30]
+
+//! [31]
+QIcon MainWindow::createColorToolButtonIcon(const QString &imageFile,
+                        QColor color)
+{
+    QPixmap pixmap(50, 80);
+    pixmap.fill(Qt::transparent);
+    QPainter painter(&pixmap);
+    QPixmap image(imageFile);
+    QRect target(0, 0, 50, 60);
+    QRect source(0, 0, 42, 42);
+    painter.fillRect(QRect(0, 60, 50, 80), color);
+    painter.drawPixmap(target, image, source);
+
+    return QIcon(pixmap);
+}
+//! [31]
+
+//! [32]
+QIcon MainWindow::createColorIcon(QColor color)
+{
+    QPixmap pixmap(20, 20);
+    QPainter painter(&pixmap);
+    painter.setPen(Qt::NoPen);
+    painter.fillRect(QRect(0, 0, 20, 20), color);
+
+    return QIcon(pixmap);
+}
+//! [32]