--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hbcore/gui/hbcssinspector_p.cpp Mon Apr 19 14:02:13 2010 +0300
@@ -0,0 +1,576 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (developer.feedback@nokia.com)
+**
+** This file is part of the HbCore module of the UI Extensions for Mobile.
+**
+** GNU Lesser General Public License Usage
+** 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 developer.feedback@nokia.com.
+**
+****************************************************************************/
+
+#include "hbcssinspector_p.h"
+
+#ifdef CSS_INSPECTOR
+#include <hbanchor_p.h>
+#include <hbanchorarrowdrawer_p.h>
+#include <hbcolorscheme.h>
+#include <hbcssformatter_p.h>
+#include <hbevent.h>
+#include <hbinstance.h>
+#include <hblayeredstyleloader_p.h>
+#include <hbmeshlayoutdebug_p.h>
+#include <hbnamespace_p.h>
+#include <hbwidgetloadersyntax_p.h>
+#include <hbxmlloaderabstractsyntax_p.h>
+
+#include <QBrush>
+#include <QCheckBox>
+#include <QGraphicsLayout>
+#include <QGraphicsScene>
+#include <QGraphicsSceneMouseEvent>
+#include <QGridLayout>
+#include <QGroupBox>
+#include <QLabel>
+#include <QPen>
+#include <QPainter>
+#include <QPointer>
+#include <QRadioButton>
+#include <QTextEdit>
+#include <QXmlStreamWriter>
+
+
+#define NODEPTR_N(x) HbCss::StyleSelector::NodePtr n = {n.ptr = (void *)x};
+
+const QString CSS_HTML_HEADER = "<style type=\"text/css\"> \
+ .overridden {color:#999; text-decoration:line-through;} \
+ .selectors {background-color: #e0e0e0; margin:0;} \
+ .selector {color:#000;} \
+ .pseudo {font-weight:bold;} \
+ .attr {font-style:italic;} \
+ .property {color:#00f; font-weight:bold;} \
+ .value {color:#f00;} \
+ .overridden .property, .overridden .value {color:#999;} \
+ </style>";
+const QString WIDGETML_HTML_HEADER = "<style type=\"text/css\"> \
+ pre {color:#999; font-family:Arial;} \
+ span {color:#000; font-weight:bold;} \
+ </style><pre>";
+const QString WIDGETML_HTML_FOOTER = "</pre>";
+
+const int ITEMNAME = 0xfffe; // Copied from hbstyle.cpp!!
+const qreal HOVER_BOX_PEN_WIDTH = 2.0;
+const QChar BIG_NUMBER_CHAR = 0x221E;
+const QString TEXT_COLOR = "qtc_default_main_pane_normal";
+const QString LINE_COLOR = "qtc_view_visited_normal";
+const int ABOVE_POPUP_ZVALUE = 5000;
+
+
+static QString cssItemText(const QGraphicsItem *item)
+{
+ QString txt;
+ if (item->isWidget()) {
+ const QGraphicsWidget *widget = static_cast<const QGraphicsWidget *>(item);
+ // Add classname
+ txt += widget->metaObject()->className();
+
+ // Add objectname
+ QString objName = widget->objectName();
+ if (objName.length() > 0) {
+ txt.append("#");
+ txt.append(objName);
+ }
+
+ // Add itemname
+ QString itemName = widget->data(ITEMNAME).toString();
+ if (itemName.length() > 0) {
+ txt.append("::");
+ txt.append(itemName);
+ }
+ }
+ return txt;
+}
+
+
+static QString convertHintToHintText(const QGraphicsItem *item, qreal hint)
+{
+ QString hintText;
+ qreal unit = HbDeviceProfile::profile(item).unitValue();
+
+ if (unit != 0) {
+ hint = (hint / unit);
+ }
+ hintText = QString::number(hint, 'g', 2);
+ if (hintText.contains('+')) {
+ hintText = BIG_NUMBER_CHAR;
+ } else {
+ if (hint !=0 && unit != 0) {
+ hintText += "un";
+ }
+ }
+ return hintText;
+}
+
+
+static QString cssItemHintText(const QGraphicsItem *item)
+{
+ QString sizeHint;
+ if (item->isWidget()) {
+ const QGraphicsLayout *layout = (static_cast<const QGraphicsWidget *>(item))->layout();
+ if(layout) {
+ sizeHint += "(" + convertHintToHintText( item, layout->minimumWidth() ) + "," + convertHintToHintText( item, layout->minimumHeight() ) + ")|";
+ sizeHint += "(" + convertHintToHintText( item, layout->preferredWidth() ) + "," + convertHintToHintText( item, layout->preferredHeight() ) +")|";
+ sizeHint += "(" + convertHintToHintText( item, layout->maximumWidth() ) + "," +convertHintToHintText( item, layout->maximumHeight() ) + ")";
+ }
+ }
+ return sizeHint;
+}
+
+
+static QString anchorEdgeName(HbAnchorLayout::Edge edge)
+{
+ QString name;
+ switch (edge) {
+ // these are hardwired inside the parser
+ case Hb::TopEdge: name = QString("TOP"); break;
+ case Hb::BottomEdge: name = QString("BOTTOM"); break;
+ case Hb::LeftEdge: name = QString("LEFT"); break;
+ case Hb::RightEdge: name = QString("RIGHT"); break;
+ case Hb::CenterHEdge: name = QString("CENTERH"); break;
+ case Hb::CenterVEdge: name = QString("CENTERV"); break;
+ }
+ return name;
+}
+
+
+static QString meshItemsToHtmlInfo(HbMeshLayout *mesh, const QString itemName, const QString layoutName)
+{
+ QString html;
+ QString widgetML;
+ QXmlStreamWriter xmlWriter(&widgetML);
+ xmlWriter.setAutoFormatting(true);
+
+ HbWidgetLoaderSyntax syntax(new HbWidgetLoaderActions());
+
+ QString str = syntax.lexemValue(HbXmlLoaderAbstractSyntax::TYPE_HBWIDGET);
+ xmlWriter.writeStartElement(syntax.lexemValue(HbXmlLoaderAbstractSyntax::TYPE_HBWIDGET));
+ xmlWriter.writeAttribute(syntax.lexemValue(HbXmlLoaderAbstractSyntax::ATTR_VERSION), HbWidgetLoaderSyntax::version());
+ xmlWriter.writeAttribute(syntax.lexemValue(HbXmlLoaderAbstractSyntax::ATTR_TYPE), itemName);
+
+ xmlWriter.writeStartElement(syntax.lexemValue(HbXmlLoaderAbstractSyntax::TYPE_LAYOUT));
+ xmlWriter.writeAttribute(syntax.lexemValue(HbXmlLoaderAbstractSyntax::ATTR_NAME), layoutName);
+ xmlWriter.writeAttribute(syntax.lexemValue(HbXmlLoaderAbstractSyntax::ATTR_TYPE), "mesh");
+
+ if (mesh) {
+ QList<HbAnchor> anchors = HbMeshLayoutDebug::getAnchors(mesh);
+ for (int i=0; i<anchors.count(); i++) {
+ HbAnchor anchor(anchors.at(i));
+
+ xmlWriter.writeStartElement(syntax.lexemValue(HbXmlLoaderAbstractSyntax::ML_MESHITEM));
+
+ xmlWriter.writeAttribute(
+ syntax.lexemValue(HbXmlLoaderAbstractSyntax::ML_SRC_NAME),
+ HbStyle::itemName(anchor.mStartItem->graphicsItem()));
+ xmlWriter.writeAttribute(
+ syntax.lexemValue(HbXmlLoaderAbstractSyntax::ML_SRC_EDGE),
+ anchorEdgeName(anchor.mStartEdge));
+ xmlWriter.writeAttribute(
+ syntax.lexemValue(HbXmlLoaderAbstractSyntax::ML_DST_NAME),
+ HbStyle::itemName(anchor.mEndItem->graphicsItem()));
+ xmlWriter.writeAttribute(
+ syntax.lexemValue(HbXmlLoaderAbstractSyntax::ML_DST_EDGE),
+ anchorEdgeName(anchor.mEndEdge));
+
+ xmlWriter.writeEndElement(); // meshitem
+ }
+
+ }
+ xmlWriter.writeEndElement(); // layout
+ xmlWriter.writeEndElement(); // widgetml
+
+ html = widgetML;
+ html.remove(0, html.indexOf("<")); // trim whitespace
+ html.replace("<", "<");
+ html.replace(">", ">");
+ html.replace(QRegExp("\"([^\"]*)\""), "\"<span>\\1</span>\""); // Add span elements around things in quotes
+ html.replace("\"", """);
+ html.replace("\n\n", "\n");
+ html.replace("\n", "<br/>");
+
+ html.insert(0, WIDGETML_HTML_HEADER);
+ html.append(WIDGETML_HTML_FOOTER);
+
+ return html;
+}
+
+
+
+/******************************************************************************************/
+
+
+
+HbCssInfoDrawer::HbCssInfoDrawer(QGraphicsItem *parent)
+ : HbWidgetBase(parent), mShowItemText(true), mShowHintText(true), mShowBox(true)
+{
+ updateColors();
+ setVisible(false);
+}
+
+
+HbCssInfoDrawer::~HbCssInfoDrawer()
+{
+}
+
+
+void HbCssInfoDrawer::changeEvent(QEvent *event)
+{
+ if (event->type() == HbEvent::ThemeChanged)
+ updateColors();
+ HbWidgetBase::changeEvent(event);
+}
+
+
+void HbCssInfoDrawer::updateColors()
+{
+ mTextColor = HbColorScheme::color(TEXT_COLOR);
+ mBoxColor = HbColorScheme::color(LINE_COLOR);
+}
+
+
+void HbCssInfoDrawer::updateFocusItem(const QGraphicsItem *item)
+{
+ // update text and geometry
+ if (item) {
+ this->setVisible(true);
+ this->setGeometry(item->sceneBoundingRect());
+ mItemText = cssItemText(item);
+ mHintText = cssItemHintText(item);
+ } else {
+ this->setVisible(false);
+ mItemText = "";
+ mHintText = "";
+ }
+}
+
+
+void HbCssInfoDrawer::paint(QPainter *painter,
+ const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ Q_UNUSED(option);
+ Q_UNUSED(widget);
+
+ if (mShowBox) {
+ painter->setPen(QPen(mBoxColor, HOVER_BOX_PEN_WIDTH));
+ QRectF boxRect = this->boundingRect();
+ boxRect.adjust(HOVER_BOX_PEN_WIDTH/2, HOVER_BOX_PEN_WIDTH/2,
+ -HOVER_BOX_PEN_WIDTH/2, -HOVER_BOX_PEN_WIDTH/2);
+ painter->drawRect(boxRect);
+ }
+
+ painter->setPen(mTextColor);
+ int fontSize = painter->fontInfo().pixelSize();
+ int boxHeight = (int)(this->boundingRect().height());
+
+ if (mShowItemText && boxHeight - fontSize > 0) {
+ QPointF pos = this->boundingRect().topLeft();
+ pos += QPointF(HOVER_BOX_PEN_WIDTH, fontSize);
+ painter->drawText(pos, mItemText);
+ }
+
+ bool roomForSizeHint = (boxHeight - (2*fontSize) > 0 )
+ || (!mShowItemText && (boxHeight - fontSize) > 0);
+ if (mShowHintText && roomForSizeHint) {
+ QPointF pos = this->boundingRect().bottomLeft();
+ pos += QPointF(HOVER_BOX_PEN_WIDTH, -HOVER_BOX_PEN_WIDTH);
+ painter->drawText(pos, mHintText);
+ }
+}
+
+
+
+/******************************************************************************************/
+
+
+
+HbCssInspectorWindow *HbCssInspectorWindow::instance()
+{
+ static QPointer<HbCssInspectorWindow> window = 0;
+ if (!window) {
+ window = new HbCssInspectorWindow;
+ }
+ return window;
+}
+
+
+void HbCssInspectorWindow::refresh()
+{
+ if (this->isVisible()) {
+ removeFilters();
+ addFilters();
+ }
+}
+
+
+void HbCssInspectorWindow::removeFilters()
+{
+ foreach (HoveredWidgetFilter *filter, mInstalledFilters)
+ delete filter;
+ mInstalledFilters.clear();
+}
+
+
+void HbCssInspectorWindow::addFilters()
+{
+ foreach (HbMainWindow *window, hbInstance->allMainWindows()) {
+ HoveredWidgetFilter *filter = new HoveredWidgetFilter(window->scene());
+ window->scene()->installEventFilter(filter);
+ mInstalledFilters.append(filter);
+ connect(filter, SIGNAL(newItemHovered(const QGraphicsItem*)), SLOT(updateFocusItem(const QGraphicsItem*)));
+ connect(mNameCheck, SIGNAL(toggled(bool)), filter->mCssInfoDrawer, SLOT(setItemTextVisible(bool)));
+ connect(mSizeHintCheck, SIGNAL(toggled(bool)), filter->mCssInfoDrawer, SLOT(setHintTextVisible(bool)));
+ connect(mArrowsCheck, SIGNAL(toggled(bool)), filter->mArrowDrawer, SLOT(setDrawArrows(bool)));
+ connect(mOutlinesCheck, SIGNAL(toggled(bool)), filter->mArrowDrawer, SLOT(setDrawOutlines(bool)));
+ connect(mSpacersCheck, SIGNAL(toggled(bool)), filter->mArrowDrawer, SLOT(setDrawSpacers(bool)));
+ connect(mLiveRadio, SIGNAL(toggled(bool)), filter, SLOT(setLiveMode(bool)));
+ connect(mBlockRadio, SIGNAL(toggled(bool)), filter, SLOT(setBlockingMode(bool)));
+ }
+}
+
+
+HbCssInspectorWindow::HbCssInspectorWindow(QWidget *parent)
+ : QWidget(parent), mLayoutWidgetMLBox(0), mLayoutCssBox(0), mColorsCssBox(0)
+{
+ QGroupBox *settings = new QGroupBox(tr("Settings"), this);
+ QGridLayout *settingLayout = new QGridLayout(settings);
+ mArrowsCheck = new QCheckBox(tr("Draw arrows"), this);
+ mOutlinesCheck = new QCheckBox(tr("Draw subitem outlines"), this);
+ mSpacersCheck = new QCheckBox(tr("Draw spacers"), this);
+ mNameCheck = new QCheckBox(tr("Show object name"), this);
+ mSizeHintCheck = new QCheckBox(tr("Show size hint"), this);
+ mLiveRadio = new QRadioButton(tr("Live mode"), this);
+ mClickRadio = new QRadioButton(tr("Click locking mode"), this);
+ mBlockRadio = new QRadioButton(tr("Click locking mode (block events)"), this);
+ settingLayout->addWidget(mArrowsCheck, 0, 0);
+ settingLayout->addWidget(mOutlinesCheck, 1, 0);
+ settingLayout->addWidget(mSpacersCheck, 2, 0);
+ settingLayout->addWidget(mNameCheck, 0, 1);
+ settingLayout->addWidget(mSizeHintCheck, 1, 1);
+ settingLayout->addWidget(mLiveRadio, 0, 2);
+ settingLayout->addWidget(mClickRadio, 1, 2);
+ settingLayout->addWidget(mBlockRadio, 2, 2);
+ mArrowsCheck->setChecked(true);
+ mOutlinesCheck->setChecked(true);
+ mSpacersCheck->setChecked(true);
+ mNameCheck->setChecked(true);
+ mSizeHintCheck->setChecked(true);
+ mLiveRadio->setChecked(true);
+
+ QLabel *lblWidgetML = new QLabel(tr("WidgetML"), this);
+ mLayoutWidgetMLBox = new QTextEdit(this);
+ mLayoutWidgetMLBox->setReadOnly(true);
+
+ QLabel *lblLayout = new QLabel(tr("Layouts CSS stack (+all)"), this);
+ mLayoutCssBox = new QTextEdit(this);
+ mLayoutCssBox->setReadOnly(true);
+
+ QLabel *lblColors = new QLabel(tr("Colors CSS stack (+all)"), this);
+ mColorsCssBox = new QTextEdit(this);
+ mColorsCssBox->setReadOnly(true);
+
+ mPathLabel = new QLabel("", this);
+ mSizeHintLabel = new QLabel("", this);
+ mSizeHintLabel->setAlignment(Qt::AlignRight);
+
+ QGridLayout *layout = new QGridLayout(this);
+ layout->addWidget(settings, 0, 0, 1, 4);
+ layout->addWidget(lblWidgetML, 1, 0);
+ layout->addWidget(mLayoutWidgetMLBox, 2, 0, 1, 4);
+ layout->addWidget(lblLayout, 3, 0, 1, 2);
+ layout->addWidget(lblColors, 3, 2, 1, 2);
+ layout->addWidget(mLayoutCssBox, 4, 0, 1, 2);
+ layout->addWidget(mColorsCssBox, 4, 2, 1, 2);
+ layout->addWidget(mPathLabel, 5, 0, 1, 3);
+ layout->addWidget(mSizeHintLabel, 5, 3, 1, 1);
+ layout->setRowStretch(2, 2);
+ layout->setRowStretch(4, 3);
+
+ setLayout(layout);
+}
+
+
+HbCssInspectorWindow::~HbCssInspectorWindow()
+{
+}
+
+
+void HbCssInspectorWindow::setVisible(bool visible)
+{
+ if (visible) {
+ addFilters();
+ } else {
+ removeFilters();
+ }
+ QWidget::setVisible(visible);
+}
+
+
+void HbCssInspectorWindow::updateFocusItem(const QGraphicsItem *item)
+{
+ if (item != 0 && item->isWidget()) {
+ const QGraphicsWidget *widget = static_cast<const QGraphicsWidget *>(item);
+ NODEPTR_N(widget);
+ HbDeviceProfile profile(HbDeviceProfile::profile(widget));
+
+ HbLayeredStyleLoader *layoutStack = HbLayeredStyleLoader::getStack(HbLayeredStyleLoader::Concern_Layouts);
+ if (layoutStack) {
+ // Update layout CSS box
+ HbVector<HbCss::StyleRule> layoutRules = layoutStack->styleRulesForNode(n, profile.orientation());
+ mLayoutCssBox->setHtml(CSS_HTML_HEADER + HbCssFormatter::styleRulesToHtml(layoutRules));
+
+ // Get layoutname from CSS
+ HbVector<HbCss::Declaration> decls;
+ foreach (const HbCss::StyleRule &rule, layoutRules)
+ decls += rule.declarations;
+ HbCss::ValueExtractor extractor(decls, profile);
+ QString layoutName;
+ QString sectionName;
+ extractor.extractLayout(&layoutName, §ionName);
+
+ // Update widgetML box
+ QString html;
+ if (widget->layout()) {
+ QString itemName = widget->metaObject()->className();
+ HbMeshLayout *mesh = dynamic_cast<HbMeshLayout *>(widget->layout());
+ html = meshItemsToHtmlInfo(mesh, itemName, layoutName);
+ }
+ mLayoutWidgetMLBox->setHtml(html);
+ }
+
+ // Update colours CSS box
+ HbLayeredStyleLoader *colorsStack = HbLayeredStyleLoader::getStack(HbLayeredStyleLoader::Concern_Colors);
+ if (colorsStack) {
+ HbVector<HbCss::StyleRule> colorRules = colorsStack->styleRulesForNode(n, profile.orientation());
+ mColorsCssBox->setHtml(CSS_HTML_HEADER + HbCssFormatter::styleRulesToHtml(colorRules));
+ }
+
+ // Update text labels
+ mSizeHintLabel->setText(cssItemHintText(item));
+ const QGraphicsItem *pathItem = item;
+ QString cssPath = cssItemText(pathItem);
+ while (pathItem->parentItem()) {
+ pathItem = pathItem->parentItem();
+ cssPath.prepend(" > ");
+ cssPath.prepend(cssItemText(pathItem));
+ }
+ mPathLabel->setText(cssPath);
+ } else {
+ mLayoutWidgetMLBox->setText("");
+ mLayoutCssBox->setText("");
+ mColorsCssBox->setText("");
+ mPathLabel->setText("");
+ mSizeHintLabel->setText("");
+ }
+}
+
+
+
+/******************************************************************************************/
+
+
+
+bool HoveredWidgetFilter::eventFilter(QObject *obj, QEvent *event)
+{
+ if ((event->type() == QEvent::GraphicsSceneMouseMove && mLiveMode)
+ || (event->type() == QEvent::GraphicsSceneMousePress && !mLiveMode)){
+ QGraphicsSceneMouseEvent *mouseEvent = static_cast<QGraphicsSceneMouseEvent *>(event);
+ QPointF eventPos = mouseEvent->scenePos();
+
+ // Skip the drawn box/texts, arrow drawers, and popup managers
+ QList<QGraphicsItem *> items = mScene->items(eventPos);
+ QGraphicsItem *item = 0;
+ foreach (QGraphicsItem *thisItem, items) {
+ QString widgetName;
+ if (thisItem->isWidget()) {
+ const QGraphicsWidget *widget = static_cast<const QGraphicsWidget *>(thisItem);
+ widgetName = widget->metaObject()->className();
+ if (thisItem != mCssInfoDrawer
+ && thisItem != mArrowDrawer
+ && widgetName != "HbPopupLayoutManager"
+ && widgetName != "HbAnchorArrowDrawer") {
+ item = thisItem;
+ break;
+ }
+ }
+ }
+
+ if (item) {
+ const QGraphicsWidget *widget = static_cast<const QGraphicsWidget *>(item);
+ QString itemText(widget->metaObject()->className());
+
+ // Ignore primitives
+ while (itemText == "QGraphicsWidget"
+ || itemText == "HbWidgetBase"
+ || itemText == "HbTextItem"
+ || itemText == "HbIconItem"
+ || itemText == "HbFrameItem") {
+ item = item->parentItem();
+ widget = static_cast<const QGraphicsWidget *>(item);
+ itemText = widget ? widget->metaObject()->className() : "";
+ }
+
+ if (item && item != mCurrentItem) {
+ mCurrentItem = item;
+ emit newItemHovered(item);
+ }
+ }
+
+ if (mBlockingMode) {
+ return true;
+ }
+ } else if(event->type() == QEvent::Leave && mLiveMode) {
+ emit newItemHovered(0);
+ mCurrentItem = 0;
+ }
+
+ return QObject::eventFilter(obj, event);
+}
+
+HoveredWidgetFilter::HoveredWidgetFilter(QGraphicsScene *scene)
+ : mScene(scene), mCurrentItem(0), mArrowDrawer(0), mCssInfoDrawer(0), mLiveMode(true), mBlockingMode(false)
+{
+ mCssInfoDrawer = new HbCssInfoDrawer(0);
+ mScene->addItem(mCssInfoDrawer);
+ mArrowDrawer = new HbAnchorArrowDrawer(0);
+ mScene->addItem(mArrowDrawer);
+
+ // Ensure CSS inspector items are above popups in z-order
+ mCssInfoDrawer->setZValue(HbPrivate::PopupZValueRangeEnd + ABOVE_POPUP_ZVALUE);
+ mArrowDrawer->setZValue(HbPrivate::PopupZValueRangeEnd + ABOVE_POPUP_ZVALUE);
+
+ connect(this, SIGNAL(newItemHovered(const QGraphicsItem*)), mCssInfoDrawer, SLOT(updateFocusItem(const QGraphicsItem*)));
+ connect(this, SIGNAL(newItemHovered(const QGraphicsItem*)), mArrowDrawer, SLOT(updateFocusItem(const QGraphicsItem*)));
+}
+
+HoveredWidgetFilter::~HoveredWidgetFilter()
+{
+ mScene->removeItem(mCssInfoDrawer);
+ mScene->removeItem(mArrowDrawer);
+ delete mCssInfoDrawer;
+ delete mArrowDrawer;
+}
+
+#endif