/* View.cpp * * Copyright (C) 2013 Jim Evins * * This file is part of gLabels-qt. * * gLabels-qt is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * gLabels-qt 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with gLabels-qt. If not, see . */ #include "View.h" #include #include #include #include #include #include #include #include "LabelModel.h" #include "LabelModelObject.h" #include "libglabels/Markup.h" #include "libglabels/FrameRect.h" #include "libglabels/FrameRound.h" #include "libglabels/FrameEllipse.h" #include "libglabels/FrameCd.h" // // Private Configuration Data // namespace { const int nZoomLevels = 14; const double zoomLevels[nZoomLevels] = { 8, 6, 4, 3, 2, 1.5, 1, 0.75, 0.67, 0.50, 0.33, 0.25, 0.15, 0.10 }; const double ZOOM_TO_FIT_PAD = 16.0; const QColor shadowColor( 64, 64, 64 ); const double shadowOffsetPixels = 3; const double shadowRadiusPixels = 12; const QColor labelColor( 255, 255, 255 ); const QColor labelOutlineColor( 0, 0, 0 ); const double labelOutlineWidthPixels = 1; const QColor gridLineColor( 192, 192, 192 ); const double gridLineWidthPixels = 1; const double gridSpacing = 9; // TODO: determine from locale. const QColor markupLineColor( 240, 99, 99 ); const double markupLineWidthPixels = 1; } namespace glabels { /// /// Constructor /// View::View( QWidget *parent ) : QGraphicsView(parent) { setZoomReal( 1, false ); mModel = 0; setMouseTracking( true ); setAttribute( Qt::WA_TranslucentBackground ); viewport()->setAutoFillBackground( false ); mScene = new QGraphicsScene(); setScene( mScene ); mScene->addItem( mLabelLayer = new QGraphicsItemGroup() ); mScene->addItem( mGridLayer = new QGraphicsItemGroup() ); mScene->addItem( mMarkupLayer = new QGraphicsItemGroup() ); mScene->addItem( mObjectLayer = new QGraphicsItemGroup() ); mScene->addItem( mForegroundLayer = new QGraphicsItemGroup() ); } /// /// Model Parameter Setter /// void View::setModel( LabelModel* model ) { mModel = model; createLabelLayer(); createGridLayer(); createMarkupLayer(); foreach (LabelModelObject* object, model->objectList() ) { addObjectToObjectLayer( object ); } createForegroundLayer(); } /// /// Grid Visibility Parameter Setter /// void View::setGridVisible( bool visibleFlag ) { mGridLayer->setVisible( visibleFlag ); } /// /// Markup Visibility Parameter Setter /// void View::setMarkupVisible( bool visibleFlag ) { mMarkupLayer->setVisible( visibleFlag ); } /// /// Zoom In "One Notch" /// void View::zoomIn() { // Find closest standard zoom level to our current zoom // Start with 2nd largest scale int i_min = 1; double dist_min = fabs( zoomLevels[1] - mZoom ); for ( int i = 2; i < nZoomLevels; i++ ) { double dist = fabs( zoomLevels[i] - mZoom ); if ( dist < dist_min ) { i_min = i; dist_min = dist; } } // Zoom in one notch setZoomReal( zoomLevels[i_min-1], false ); } /// /// Zoom Out "One Notch" /// void View::zoomOut() { // Find closest standard zoom level to our current zoom // Start with largest scale, end on 2nd smallest int i_min = 0; double dist_min = fabs( zoomLevels[0] - mZoom ); for ( int i = 1; i < (nZoomLevels-1); i++ ) { double dist = fabs( zoomLevels[i] - mZoom ); if ( dist < dist_min ) { i_min = i; dist_min = dist; } } // Zoom out one notch setZoomReal( zoomLevels[i_min+1], false ); } /// /// Zoom To 1:1 Scale /// void View::zoom1To1() { setZoomReal( 1.0, false ); } /// /// Zoom To Fit /// void View::zoomToFit() { double x_scale = (72.0/physicalDpiX()) * ( width() - ZOOM_TO_FIT_PAD ) / mModel->w(); double y_scale = (72.0/physicalDpiY()) * ( height() - ZOOM_TO_FIT_PAD ) / mModel->h(); double newZoom = std::min( x_scale, y_scale ); // Limits newZoom = std::min( newZoom, zoomLevels[0] ); newZoom = std::max( newZoom, zoomLevels[nZoomLevels-1] ); setZoomReal( newZoom, true ); } /// /// Is Zoom at Maximum? /// bool View::isZoomMax() const { return ( mZoom >= zoomLevels[0] ); } /// /// Is Zoom at Minimum? /// bool View::isZoomMin() const { return ( mZoom <= zoomLevels[nZoomLevels-1] ); } /// /// Set Zoom to Value /// void View::setZoomReal( double zoom, bool zoomToFitFlag ) { mZoom = zoom; mZoomToFitFlag = zoomToFitFlag; resetTransform(); scale( mZoom*physicalDpiX()/72.0, mZoom*physicalDpiY()/72.0 ); emit zoomChanged(); } /// /// Resize Event Handler /// void View::resizeEvent( QResizeEvent *event ) { if ( mZoomToFitFlag ) { zoomToFit(); } else { // Refresh to keep view location relative to window resetTransform(); scale( mZoom*physicalDpiX()/72.0, mZoom*physicalDpiY()/72.0 ); } } /// /// Mouse Movement Event Handler /// void View::mouseMoveEvent( QMouseEvent* event ) { QPointF pointer = mapToScene( event->x(), event->y() ); emit pointerMoved( pointer.x(), pointer.y() ); } /// /// Leave Event Handler /// void View::leaveEvent( QEvent* event ) { emit pointerExited(); } /// /// Clear Layer (Item Group) of All Child Items /// void View::clearLayer( QGraphicsItemGroup* layer ) { foreach( QGraphicsItem* item, layer->childItems() ) { layer->removeFromGroup( item ); } } /// /// Create Label Layer /// void View::createLabelLayer() { clearLayer( mLabelLayer ); QGraphicsPathItem *labelItem = new QGraphicsPathItem( mModel->frame()->path() ); QBrush brush( labelColor ); labelItem->setBrush( brush ); QGraphicsDropShadowEffect *shadowEffect = new QGraphicsDropShadowEffect(); shadowEffect->setColor( shadowColor ); shadowEffect->setOffset( shadowOffsetPixels ); shadowEffect->setBlurRadius( shadowRadiusPixels ); labelItem->setGraphicsEffect( shadowEffect ); mLabelLayer->addToGroup( labelItem ); } /// /// Create Grid Layer /// void View::createGridLayer() { clearLayer( mGridLayer ); QGraphicsPathItem *clipItem = new QGraphicsPathItem( mModel->frame()->path() ); clipItem->setFlag( QGraphicsItem::ItemClipsChildrenToShape ); QPen pen( gridLineColor ); pen.setCosmetic( true ); pen.setWidthF( gridLineWidthPixels ); double w = mModel->w(); double h = mModel->h(); double x0; double y0; if ( dynamic_cast( mModel->frame() ) ) { x0 = gridSpacing; y0 = gridSpacing; } else { /* round labels, align grid with center of label. */ x0 = fmod( w/2, gridSpacing ); y0 = fmod( h/2, gridSpacing ); } for ( double x = x0; x < w; x += gridSpacing ) { QGraphicsLineItem* lineItem = new QGraphicsLineItem( x, 0, x, h, clipItem ); lineItem->setPen( pen ); } for ( double y = y0; y < h; y += gridSpacing ) { QGraphicsLineItem* lineItem = new QGraphicsLineItem( 0, y, w, y, clipItem ); lineItem->setPen( pen ); } mGridLayer->addToGroup( clipItem ); } /// /// Create Markup Layer /// void View::createMarkupLayer() { clearLayer( mMarkupLayer ); QPen pen( markupLineColor ); pen.setCosmetic( true ); pen.setWidthF( markupLineWidthPixels ); const libglabels::Frame* frame = mModel->frame(); foreach (libglabels::Markup* markup, frame->markups() ) { QGraphicsItem* markupItem = markup->createGraphicsItem( frame, pen ); mMarkupLayer->addToGroup( markupItem ); } } /// /// Add Object to Object Layer /// void View::addObjectToObjectLayer( LabelModelObject* object ) { QGraphicsItem* item = object->createGraphicsItem(); mObjectLayer->addToGroup( item ); } /// /// Create Foreground Layer /// void View::createForegroundLayer() { clearLayer( mForegroundLayer ); QGraphicsPathItem *outlineItem = new QGraphicsPathItem( mModel->frame()->path() ); QPen pen( labelOutlineColor ); pen.setJoinStyle( Qt::MiterJoin ); pen.setCosmetic( true ); pen.setWidthF( labelOutlineWidthPixels ); outlineItem->setPen( pen ); mForegroundLayer->addToGroup( outlineItem ); } }