/*************************************************************************** * Copyright (c) 2009 Werner Mayer * * * * This file is part of the FreeCAD CAx development system. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; see the file COPYING.LIB. If not, * * write to the Free Software Foundation, Inc., 59 Temple Place, * * Suite 330, Boston, MA 02111-1307, USA * * * ***************************************************************************/ #include #ifdef FC_OS_WIN32 # include #endif #ifdef FC_OS_MACOSX # include #else # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "SoTextLabel.h" #include "SoFCInteractiveElement.h" #include "Tools.h" using namespace Gui; SO_NODE_SOURCE(SoTextLabel) void SoTextLabel::initClass() { SO_NODE_INIT_CLASS(SoTextLabel, SoText2, "Text2"); } SoTextLabel::SoTextLabel() { SO_NODE_CONSTRUCTOR(SoTextLabel); SO_NODE_ADD_FIELD(backgroundColor, (SbVec3f(1.0f, 1.0f, 1.0f))); SO_NODE_ADD_FIELD(background, (true)); SO_NODE_ADD_FIELD(frameSize, (10.0f)); } /** * Renders the label. */ void SoTextLabel::GLRender(SoGLRenderAction* action) { if (!this->shouldGLRender(action)) { return; } // only draw text without background if (!this->background.getValue()) { inherited::GLRender(action); return; } SoState* state = action->getState(); state->push(); SoLazyElement::setLightModel(state, SoLazyElement::BASE_COLOR); SbBox3f box; SbVec3f center; this->computeBBox(action, box, center); if (!SoCullElement::cullTest(state, box, true)) { SoMaterialBundle mb(action); mb.sendFirst(); const SbMatrix& mat = SoModelMatrixElement::get(state); const SbMatrix& projmatrix = (mat * SoViewingMatrixElement::get(state) * SoProjectionMatrixElement::get(state)); const SbViewportRegion& vp = SoViewportRegionElement::get(state); SbVec2s vpsize = vp.getViewportSizePixels(); // font stuff SbName fontname = SoFontNameElement::get(state); int lines = this->string.getNum(); // get left bottom corner of the label SbVec3f nilpoint(0.0f, 0.0f, 0.0f); projmatrix.multVecMatrix(nilpoint, nilpoint); nilpoint[0] = (nilpoint[0] + 1.0f) * 0.5f * vpsize[0]; nilpoint[1] = (nilpoint[1] + 1.0f) * 0.5f * vpsize[1]; // Unfortunately, the size of the label is stored in the pimpl class of // SoText2 which cannot be accessed directly. However, there is a trick // to get the required information: set model, viewing and projection // matrix to the identity matrix and also view volume to some default // values. SoText2::computeBBox() then calls SoText2P::getQuad which // returns the sizes in form of the bounding box. These values can be // reverse-engineered to get width and height. state->push(); SoModelMatrixElement::set(state, this, SbMatrix::identity()); SoViewingMatrixElement::set(state, this, SbMatrix::identity()); SoProjectionMatrixElement::set(state, this, SbMatrix::identity()); SbViewVolume vv; vv.ortho(-1, 1, -1, 1, -1, 1); SoViewVolumeElement::set(state, this, vv); SbBox3f box; SbVec3f center; this->computeBBox(action, box, center); state->pop(); float xmin, ymin, zmin, xmax, ymax, zmax; box.getBounds(xmin, ymin, zmin, xmax, ymax, zmax); SbVec3f v0(xmin, ymax, zmax); SbVec3f v1(xmax, ymax, zmax); SbVec3f v2(xmax, ymin, zmax); SbVec3f v3(xmin, ymin, zmax); vv.projectToScreen(v0, v0); vv.projectToScreen(v1, v1); vv.projectToScreen(v2, v2); vv.projectToScreen(v3, v3); float width, height; width = (v1[0] - v0[0]) * vpsize[0]; height = (v1[1] - v3[1]) * vpsize[1]; switch (this->justification.getValue()) { case SoText2::RIGHT: nilpoint[0] -= width; break; case SoText2::CENTER: nilpoint[0] -= 0.5f * width; break; default: break; } if (lines > 1) { nilpoint[1] -= (float(lines - 1) / (float)lines * height); } SbVec3f toppoint = nilpoint; toppoint[0] += width; toppoint[1] += height; // Set new state. glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0, vpsize[0], 0, vpsize[1], -1.0f, 1.0f); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); state->push(); // disable textures for all units SoGLTextureEnabledElement::set(state, this, false); SoMultiTextureEnabledElement::set(state, this, false); glPushAttrib(GL_ENABLE_BIT | GL_PIXEL_MODE_BIT | GL_COLOR_BUFFER_BIT); glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); // color and frame size SbColor color = this->backgroundColor.getValue(); float fs = this->frameSize.getValue(); // draw background glColor3f(color[0], color[1], color[2]); glBegin(GL_QUADS); glVertex3f(nilpoint[0] - fs, nilpoint[1] - fs, 0.0f); glVertex3f(toppoint[0] + fs, nilpoint[1] - fs, 0.0f); glVertex3f(toppoint[0] + fs, toppoint[1] + fs, 0.0f); glVertex3f(nilpoint[0] - fs, toppoint[1] + fs, 0.0f); glEnd(); // pop old state glPopClientAttrib(); glPopAttrib(); state->pop(); glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // Pop old GL matrix state. glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); } state->pop(); inherited::GLRender(action); } // ------------------------------------------------------ SO_NODE_SOURCE(SoColorBarLabel) void SoColorBarLabel::initClass() { SO_NODE_INIT_CLASS(SoColorBarLabel, SoText2, "Text2"); } SoColorBarLabel::SoColorBarLabel() { SO_NODE_CONSTRUCTOR(SoColorBarLabel); } void SoColorBarLabel::computeBBox(SoAction* action, SbBox3f& box, SbVec3f& center) { inherited::computeBBox(action, box, center); if (!box.hasVolume()) { SbViewVolume vv = SoViewVolumeElement::get(action->getState()); // workaround for https://github.com/coin3d/coin/issues/417: // extend by 2 percent vv.scaleWidth(1.02f); SoViewVolumeElement::set(action->getState(), this, vv); inherited::computeBBox(action, box, center); } } // ------------------------------------------------------ SO_NODE_SOURCE(SoStringLabel) void SoStringLabel::initClass() { SO_NODE_INIT_CLASS(SoStringLabel, SoNode, "Node"); } SoStringLabel::SoStringLabel() { SO_NODE_CONSTRUCTOR(SoStringLabel); SO_NODE_ADD_FIELD(string, ("")); SO_NODE_ADD_FIELD(textColor, (SbVec3f(1.0f, 1.0f, 1.0f))); SO_NODE_ADD_FIELD(name, ("Helvetica")); SO_NODE_ADD_FIELD(size, (12)); } /** * Renders the open edges only. */ void SoStringLabel::GLRender(SoGLRenderAction* action) { QOpenGLWidget* window; SoState* state = action->getState(); state->push(); SoLazyElement::setLightModel(state, SoLazyElement::BASE_COLOR); SoGLWidgetElement::get(state, window); if (!window) { state->pop(); return; } // Enter in 2D screen mode glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(-1, 1, -1, 1, -1, 1); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glPushAttrib(GL_ENABLE_BIT); glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); glDisable(GL_TEXTURE_2D); glEnable(GL_BLEND); QFont font; font.setStyleStrategy(QFont::NoAntialias); font.setFamily(QLatin1String(this->name.getValue())); font.setPixelSize(this->size.getValue()); glBlendFunc(GL_ONE, GL_SRC_ALPHA); // text color SbColor color = this->textColor.getValue(); glColor4f(color[0], color[1], color[2], 1); const SbMatrix& mat = SoModelMatrixElement::get(state); const SbMatrix& projmatrix = (mat * SoViewingMatrixElement::get(state) * SoProjectionMatrixElement::get(state)); SbVec3f nil(0.0f, 0.0f, 0.0f); projmatrix.multVecMatrix(nil, nil); QStringList list; for (int i = 0; i < this->string.getNum(); i++) { list << QLatin1String(this->string[i].getString()); } // Leave 2D screen mode glPopAttrib(); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); state->pop(); } // ------------------------------------------------------ SO_NODE_SOURCE(SoFrameLabel) void SoFrameLabel::initClass() { SO_NODE_INIT_CLASS(SoFrameLabel, SoImage, "Image"); } SoFrameLabel::SoFrameLabel() { SO_NODE_CONSTRUCTOR(SoFrameLabel); SO_NODE_ADD_FIELD(string, ("")); SO_NODE_ADD_FIELD(textColor, (SbVec3f(1.0f, 1.0f, 1.0f))); SO_NODE_ADD_FIELD(backgroundColor, (SbVec3f(0.0f, 0.333f, 1.0f))); SO_NODE_ADD_FIELD(justification, (LEFT)); SO_NODE_ADD_FIELD(name, ("Helvetica")); SO_NODE_ADD_FIELD(size, (12)); SO_NODE_ADD_FIELD(frame, (true)); SO_NODE_ADD_FIELD(border, (true)); SO_NODE_ADD_FIELD(backgroundUseBaseColor, (false)); SO_NODE_ADD_FIELD(textUseBaseColor, (false)); // SO_NODE_ADD_FIELD(image, (SbVec2s(0,0), 0, NULL)); } void SoFrameLabel::setIcon(const QPixmap& pixMap) { iconPixmap = pixMap; drawImage(); } void SoFrameLabel::notify(SoNotList* list) { SoField* f = list->getLastField(); if (f == &this->string || f == &this->textColor || f == &this->backgroundColor || f == &this->justification || f == &this->name || f == &this->size || f == &this->frame || f == &this->border) { drawImage(); } inherited::notify(list); } void SoFrameLabel::drawImage() { const SbString* s = string.getValues(0); int num = string.getNum(); if (num == 0) { this->image = SoSFImage(); return; } QFont font(QString::fromLatin1(QByteArray(name.getValue())), size.getValue()); QFontMetrics fm(font); int w = 0; int h = fm.height() * num; const SbColor& b = backgroundColor.getValue(); QColor backgroundBrush; backgroundBrush.setRgbF(b[0], b[1], b[2]); const SbColor& t = textColor.getValue(); QColor front; front.setRgbF(t[0], t[1], t[2]); const QPen borderPen(QColor(0, 0, 127), 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); QStringList lines; for (int i = 0; i < num; i++) { QString line = QString::fromUtf8(s[i].getString()); w = std::max(w, QtTools::horizontalAdvance(fm, line)); lines << line; } int padding = 5; bool drawIcon = false; QImage iconImg; int widthIcon = 0; int heightIcon = 0; if (!iconPixmap.isNull()) { drawIcon = true; iconImg = iconPixmap.toImage(); widthIcon = iconImg.width() + 2 * padding; heightIcon = iconImg.height() + 2 * padding; } int widthText = w + 2 * padding; int heightText = h + 2 * padding; int widthTotal = widthText + widthIcon; int heightTotal = heightText > heightIcon ? heightText : heightIcon; int paddingTextV = (heightTotal - h) / 2; int paddingIconV = (heightTotal - iconImg.height()) / 2; QImage image(widthTotal, heightTotal, QImage::Format_ARGB32_Premultiplied); image.fill(0x00000000); QPainter painter(&image); painter.setRenderHint(QPainter::Antialiasing); SbBool drawFrame = frame.getValue(); SbBool drawBorder = border.getValue(); if (drawFrame || drawBorder) { painter.setPen(drawBorder ? borderPen : QPen(Qt::transparent)); painter.setBrush(QBrush(drawFrame ? backgroundBrush : Qt::transparent)); QRectF rectangle(0.0, 0.0, widthTotal, heightTotal); painter.drawRoundedRect(rectangle, 5, 5); } if (drawIcon) { painter.drawImage(QPoint(padding, paddingIconV), iconImg); } painter.setPen(front); Qt::Alignment align = Qt::AlignVCenter; if (justification.getValue() == 0) { align = Qt::AlignVCenter | Qt::AlignLeft; } else if (justification.getValue() == 1) { align = Qt::AlignVCenter | Qt::AlignRight; } else { align = Qt::AlignVCenter | Qt::AlignHCenter; } QString text = lines.join(QLatin1String("\n")); painter.setFont(font); painter.drawText(widthIcon + padding, paddingTextV, w, h, align, text); painter.end(); SoSFImage sfimage; Gui::BitmapFactory().convert(image, sfimage); this->image = sfimage; } /** * Renders the open edges only. */ void SoFrameLabel::GLRender(SoGLRenderAction* action) { if (backgroundUseBaseColor.getValue()) { SoState* state = action->getState(); const SbColor& diffuse = SoLazyElement::getDiffuse(state, 0); if (diffuse != this->backgroundColor.getValue()) { this->backgroundColor.setValue(diffuse); } } if (textUseBaseColor.getValue()) { SoState* state = action->getState(); const SbColor& diffuse = SoLazyElement::getDiffuse(state, 0); if (diffuse != this->textColor.getValue()) { this->textColor.setValue(diffuse); } } inherited::GLRender(action); } // ------------------------------------------------------ SO_NODE_SOURCE(TranslateManip) void TranslateManip::initClass() { SO_NODE_INIT_CLASS(TranslateManip, SoTransformManip, "TransformManip"); } TranslateManip::TranslateManip() { SO_NODE_CONSTRUCTOR(TranslateManip); auto myDrag = new SoTranslate2Dragger; setDragger(myDrag); } TranslateManip::~TranslateManip() = default;