int PCBNEW_CONTROL::ZoomFitScreen( const TOOL_EVENT& aEvent ) { KIGFX::VIEW* view = getView(); EDA_DRAW_PANEL_GAL* galCanvas = m_frame->GetGalCanvas(); BOARD* board = getModel<BOARD>(); board->ComputeBoundingBox(); BOX2I boardBBox = board->ViewBBox(); VECTOR2D scrollbarSize = VECTOR2D( galCanvas->GetSize() - galCanvas->GetClientSize() ); VECTOR2D screenSize = view->ToWorld( galCanvas->GetClientSize(), false ); if( boardBBox.GetWidth() == 0 || boardBBox.GetHeight() == 0 ) { // Empty view view->SetScale( 17.0 ); // works fine for the standard worksheet frame view->SetCenter( screenSize / 2.0 ); } else { VECTOR2D vsize = boardBBox.GetSize(); double scale = view->GetScale() / std::max( fabs( vsize.x / screenSize.x ), fabs( vsize.y / screenSize.y ) ); view->SetScale( scale ); view->SetCenter( boardBBox.Centre() ); } // Take scrollbars into account VECTOR2D worldScrollbarSize = view->ToWorld( scrollbarSize, false ); view->SetCenter( view->GetCenter() + worldScrollbarSize / 2.0 ); return 0; }
bool SHAPE_LINE_CHAIN::PointInside( const VECTOR2I& aPt, int aAccuracy ) const { BOX2I bbox = BBox(); bbox.Inflate( aAccuracy ); if( !m_closed || PointCount() < 3 || !BBox().Contains( aPt ) ) return false; bool inside = false; /** * To check for interior points, we draw a line in the positive x direction from * the point. If it intersects an even number of segments, the point is outside the * line chain (it had to first enter and then exit). Otherwise, it is inside the chain. * * Note: slope might be denormal here in the case of a horizontal line but we require our * y to move from above to below the point (or vice versa) */ for( int i = 0; i < PointCount(); i++ ) { const auto p1 = CPoint( i ); const auto p2 = CPoint( i + 1 ); // CPoint wraps, so ignore counts const auto diff = p2 - p1; if( diff.y != 0 ) { const int d = rescale( diff.x, ( aPt.y - p1.y ), diff.y ); if( ( ( p1.y > aPt.y ) != ( p2.y > aPt.y ) ) && ( aPt.x - p1.x < d ) ) inside = !inside; } } return inside && !PointOnEdge( aPt, aAccuracy ); }
void DIALOG_PAD_PROPERTIES::redraw() { if( m_parent->IsGalCanvasActive() ) { m_dummyPad->ViewUpdate(); BOX2I bbox = m_dummyPad->ViewBBox(); if( bbox.GetSize().x > 0 && bbox.GetSize().y > 0 ) { // gives a size to the full drawable area BOX2I drawbox; drawbox.Move( m_dummyPad->GetPosition() ); drawbox.Inflate( bbox.GetSize().x*2, bbox.GetSize().y*2 ); m_panelShowPadGal->GetView()->SetBoundary( drawbox ); // Autozoom m_panelShowPadGal->GetView()->SetViewport( BOX2D( bbox.GetOrigin(), bbox.GetSize() ) ); // Add a margin m_panelShowPadGal->GetView()->SetScale( m_panelShowPadGal->GetView()->GetScale() * 0.7 ); m_panelShowPadGal->Refresh(); } } else { m_panelShowPad->Refresh(); } }
const BOX2I RATSNEST_VIEWITEM::ViewBBox() const { // Make it always visible BOX2I bbox; bbox.SetMaximum(); return bbox; }
const BOX2I DIMENSION::ViewBBox() const { BOX2I dimBBox = BOX2I( VECTOR2I( GetBoundingBox().GetPosition() ), VECTOR2I( GetBoundingBox().GetSize() ) ); dimBBox.Merge( m_Text.ViewBBox() ); return dimBBox; }
const BOX2I RULER_ITEM::ViewBBox() const { BOX2I tmp; tmp.SetOrigin( m_origin ); tmp.SetEnd( m_end ); tmp.Normalize(); return tmp; }
void VIEW::clearGroupCache() { BOX2I r; r.SetMaximum(); clearLayerCache visitor( this ); for( LAYER_MAP_ITER i = m_layers.begin(); i != m_layers.end(); ++i ) { VIEW_LAYER* l = &( ( *i ).second ); l->items->Query( r, visitor ); } }
const BOX2I VIEW::CalculateExtents() { extentsVisitor v; BOX2I fullScene; fullScene.SetMaximum(); for( VIEW_LAYER* l : m_orderedLayers ) { l->items->Query( fullScene, v ); } return v.extents; }
void VIEW::ChangeLayerDepth( int aLayer, int aDepth ) { // There is no point in updating non-cached layers if( !IsCached( aLayer ) ) return; BOX2I r; r.SetMaximum(); changeItemsDepth visitor( aLayer, aDepth, m_gal ); m_layers[aLayer].items->Query( r, visitor ); MarkTargetDirty( m_layers[aLayer].target ); }
void VIEW::UpdateLayerColor( int aLayer ) { // There is no point in updating non-cached layers if( !IsCached( aLayer ) ) return; BOX2I r; r.SetMaximum(); updateItemsColor visitor( aLayer, m_painter, m_gal ); m_layers[aLayer].items->Query( r, visitor ); MarkTargetDirty( m_layers[aLayer].target ); }
const BOX2I SHAPE_ARC::BBox( int aClearance ) const { BOX2I bbox; std::vector<VECTOR2I> points; points.push_back( m_pc ); points.push_back( m_p0 ); points.push_back( GetP1() ); bbox.Compute( points ); if( aClearance != 0 ) bbox.Inflate( aClearance ); return bbox; }
const BOX2I SHAPE_POLY_SET::BBox( int aClearance ) const { BOX2I bb; for( unsigned i = 0; i < m_polys.size(); i++ ) { if( i == 0 ) bb = m_polys[i][0].BBox(); else bb.Merge( m_polys[i][0].BBox() ); } bb.Inflate( aClearance ); return bb; }
void AddBox( BOX2I aB, int aColor ) { SHAPE_LINE_CHAIN l; VECTOR2I o = aB.GetOrigin(); VECTOR2I s = aB.GetSize(); l.Append( o ); l.Append( o.x + s.x, o.y ); l.Append( o.x + s.x, o.y + s.y ); l.Append( o.x, o.y + s.y ); l.Append( o ); AddLine( l, aColor, 10000 ); }
void DrawDebugBox( BOX2I aB, int aColor ) { SHAPE_LINE_CHAIN l; VECTOR2I o = aB.GetOrigin(); VECTOR2I s = aB.GetSize(); l.Append( o ); l.Append( o.x + s.x, o.y ); l.Append( o.x + s.x, o.y + s.y ); l.Append( o.x, o.y + s.y ); l.Append( o ); ROUTER::GetInstance()->DisplayDebugLine( l, aColor, 10000 ); }
bool GERBVIEW_SELECTION_TOOL::selectionContains( const VECTOR2I& aPoint ) const { const unsigned GRIP_MARGIN = 20; VECTOR2D margin = getView()->ToWorld( VECTOR2D( GRIP_MARGIN, GRIP_MARGIN ), false ); // Check if the point is located within any of the currently selected items bounding boxes for( auto item : m_selection ) { BOX2I itemBox = item->ViewBBox(); itemBox.Inflate( margin.x, margin.y ); // Give some margin for gripping an item if( itemBox.Contains( aPoint ) ) return true; } return false; }
void VIEW::RecacheAllItems() { BOX2I r; r.SetMaximum(); for( LAYER_MAP_ITER i = m_layers.begin(); i != m_layers.end(); ++i ) { VIEW_LAYER* l = &( ( *i ).second ); if( IsCached( l->id ) ) { recacheItem visitor( this, m_gal, l->id ); l->items->Query( r, visitor ); } } }
bool operator()( VIEW_ITEM* aItem ) { if( first ) extents = aItem->ViewBBox(); else extents.Merge ( aItem->ViewBBox() ); return false; }
const BOX2I VIEW_GROUP::ViewBBox() const { BOX2I bb; if( !m_groupItems.size() ) { bb.SetMaximum(); } else { bb = m_groupItems[0]->ViewBBox(); for( auto item : m_groupItems ) bb.Merge( item->ViewBBox() ); } return bb; }
bool SELECTION_TOOL::selectionContains( const VECTOR2I& aPoint ) const { const unsigned GRIP_MARGIN = 20; VECTOR2D margin = getView()->ToWorld( VECTOR2D( GRIP_MARGIN, GRIP_MARGIN ), false ); // Check if the point is located within any of the currently selected items bounding boxes for( unsigned int i = 0; i < m_selection.items.GetCount(); ++i ) { BOARD_ITEM* item = m_selection.Item<BOARD_ITEM>( i ); BOX2I itemBox = item->ViewBBox(); itemBox.Inflate( margin.x, margin.y ); // Give some margin for gripping an item if( itemBox.Contains( aPoint ) ) return true; } return false; }
static void extendBox( BOX2I& aBox, bool& aDefined, const VECTOR2I& aP ) { if( aDefined ) aBox.Merge ( aP ); else { aBox = BOX2I( aP, VECTOR2I( 0, 0 ) ); aDefined = true; } }
void VIEW::UpdateAllLayersColor() { BOX2I r; r.SetMaximum(); for( LAYER_MAP_ITER i = m_layers.begin(); i != m_layers.end(); ++i ) { VIEW_LAYER* l = &( ( *i ).second ); // There is no point in updating non-cached layers if( !IsCached( l->id ) ) continue; updateItemsColor visitor( l->id, m_painter, m_gal ); l->items->Query( r, visitor ); } MarkDirty(); }
const BOX2I ROUTER_PREVIEW_ITEM::ViewBBox() const { BOX2I bbox; switch( m_type ) { case PR_SHAPE: bbox = m_shape->BBox(); bbox.Inflate( m_width / 2 ); return bbox; case PR_POINT: bbox = BOX2I ( m_pos - VECTOR2I( 100000, 100000 ), VECTOR2I( 200000, 200000 ) ); return bbox; default: break; } return bbox; }
int PCBNEW_CONTROL::ZoomFitScreen( const TOOL_EVENT& aEvent ) { KIGFX::VIEW* view = m_frame->GetGalCanvas()->GetView(); KIGFX::GAL* gal = m_frame->GetGalCanvas()->GetGAL(); BOARD* board = getModel<BOARD>(); board->ComputeBoundingBox(); BOX2I boardBBox = board->ViewBBox(); VECTOR2I screenSize = gal->GetScreenPixelSize(); if( boardBBox.GetSize().x == 0 || boardBBox.GetSize().y == 0 ) { // Empty view view->SetCenter( view->ToWorld( VECTOR2D( screenSize.x / 2, screenSize.y / 2 ) ) ); view->SetScale( 17.0 ); } else { // Autozoom to board double iuPerX = screenSize.x ? boardBBox.GetWidth() / screenSize.x : 1.0; double iuPerY = screenSize.y ? boardBBox.GetHeight() / screenSize.y : 1.0; double bestZoom = std::max( iuPerX, iuPerY ); double zoomFactor = gal->GetWorldScale() / gal->GetZoomFactor(); double zoom = 1.0 / ( zoomFactor * bestZoom ); view->SetCenter( boardBBox.Centre() ); view->SetScale( zoom ); } return 0; }
void DIALOG_PAD_PROPERTIES::redraw() { if( m_parent->IsGalCanvasActive() ) { m_dummyPad->ViewUpdate(); BOX2I bbox = m_dummyPad->ViewBBox(); if( bbox.GetSize().x > 0 && bbox.GetSize().y > 0 ) { // Autozoom m_panelShowPadGal->GetView()->SetViewport( BOX2D( bbox.GetOrigin(), bbox.GetSize() ) ); // Add a margin m_panelShowPadGal->GetView()->SetScale( m_panelShowPadGal->GetView()->GetScale() * 0.7 ); m_panelShowPadGal->Refresh(); } } else { m_panelShowPad->Refresh(); } }
int PCBNEW_CONTROL::ZoomFitScreen( TOOL_EVENT& aEvent ) { KIGFX::VIEW* view = m_frame->GetGalCanvas()->GetView(); KIGFX::GAL* gal = m_frame->GetGalCanvas()->GetGAL(); BOX2I boardBBox = getModel<BOARD>( PCB_T )->ViewBBox(); VECTOR2I screenSize = gal->GetScreenPixelSize(); double iuPerX = screenSize.x ? boardBBox.GetWidth() / screenSize.x : 1.0; double iuPerY = screenSize.y ? boardBBox.GetHeight() / screenSize.y : 1.0; double bestZoom = std::max( iuPerX, iuPerY ); // This is needed to avoid "jumpy" zooms if first hot key was used and then mouse scroll // (or other way round). m_frame->GetScreen()->SetZoom( bestZoom ); double zoomFactor = gal->GetWorldScale() / gal->GetZoomFactor(); double zoom = 1.0 / ( zoomFactor * bestZoom ); view->SetScale( zoom ); view->SetCenter( boardBBox.Centre() ); setTransitions(); return 0; }
void VIEW::Clear() { BOX2I r; r.SetMaximum(); for( VIEW_ITEM* item : m_needsUpdate ) item->clearUpdateFlags(); m_needsUpdate.clear(); for( LAYER_MAP_ITER i = m_layers.begin(); i != m_layers.end(); ++i ) { VIEW_LAYER* l = &( ( *i ).second ); unlinkItem v; if( m_dynamic ) l->items->Query( r, v ); l->items->RemoveAll(); } m_gal->ClearCache(); }
bool GERBVIEW_SELECTION_TOOL::selectMultiple() { bool cancelled = false; // Was the tool cancelled while it was running? m_multiple = true; // Multiple selection mode is active KIGFX::VIEW* view = getView(); getViewControls()->SetAutoPan( true ); KIGFX::PREVIEW::SELECTION_AREA area; view->Add( &area ); while( OPT_TOOL_EVENT evt = Wait() ) { if( evt->IsCancel() ) { cancelled = true; break; } if( evt->IsDrag( BUT_LEFT ) ) { // Start drawing a selection box area.SetOrigin( evt->DragOrigin() ); area.SetEnd( evt->Position() ); area.SetAdditive( m_additive ); area.SetSubtractive( m_subtractive ); view->SetVisible( &area, true ); view->Update( &area ); } if( evt->IsMouseUp( BUT_LEFT ) ) { // End drawing the selection box view->SetVisible( &area, false ); // Mark items within the selection box as selected std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> selectedItems; // Filter the view items based on the selection box BOX2I selectionBox = area.ViewBBox(); view->Query( selectionBox, selectedItems ); // Get the list of selected items std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR>::iterator it, it_end; int width = area.GetEnd().x - area.GetOrigin().x; int height = area.GetEnd().y - area.GetOrigin().y; // Construct an EDA_RECT to determine EDA_ITEM selection EDA_RECT selectionRect( wxPoint( area.GetOrigin().x, area.GetOrigin().y ), wxSize( width, height ) ); selectionRect.Normalize(); for( it = selectedItems.begin(), it_end = selectedItems.end(); it != it_end; ++it ) { auto item = static_cast<GERBER_DRAW_ITEM*>( it->first ); if( !item || !selectable( item ) ) continue; /* Selection mode depends on direction of drag-selection: * Left > Right : Select objects that are fully enclosed by selection * Right > Left : Select objects that are crossed by selection */ if( width >= 0 ) { if( selectionBox.Contains( item->ViewBBox() ) ) { if( m_subtractive ) unselect( item ); else select( item ); } } else { if( item->HitTest( selectionRect ) ) { if( m_subtractive ) unselect( item ); else select( item ); } } } if( m_selection.Size() == 1 ) m_frame->SetCurItem( static_cast<GERBER_DRAW_ITEM*>( m_selection.Front() ) ); else m_frame->SetCurItem( NULL ); // Inform other potentially interested tools if( !m_selection.Empty() ) m_toolMgr->ProcessEvent( SelectedEvent ); break; // Stop waiting for events } } // Stop drawing the selection box view->Remove( &area ); m_multiple = false; // Multiple selection mode is inactive getViewControls()->SetAutoPan( false ); return cancelled; }
void ZONE_FILLER::buildUnconnectedThermalStubsPolygonList( SHAPE_POLY_SET& aCornerBuffer, const ZONE_CONTAINER* aZone, const SHAPE_POLY_SET& aRawFilledArea, double aArcCorrection, double aRoundPadThermalRotation ) const { SHAPE_LINE_CHAIN spokes; BOX2I itemBB; VECTOR2I ptTest[4]; auto zoneBB = aRawFilledArea.BBox(); int zone_clearance = aZone->GetZoneClearance(); int biggest_clearance = m_board->GetDesignSettings().GetBiggestClearanceValue(); biggest_clearance = std::max( biggest_clearance, zone_clearance ); zoneBB.Inflate( biggest_clearance ); // half size of the pen used to draw/plot zones outlines int pen_radius = aZone->GetMinThickness() / 2; for( auto module : m_board->Modules() ) { for( auto pad : module->Pads() ) { // Rejects non-standard pads with tht-only thermal reliefs if( aZone->GetPadConnection( pad ) == PAD_ZONE_CONN_THT_THERMAL && pad->GetAttribute() != PAD_ATTRIB_STANDARD ) continue; if( aZone->GetPadConnection( pad ) != PAD_ZONE_CONN_THERMAL && aZone->GetPadConnection( pad ) != PAD_ZONE_CONN_THT_THERMAL ) continue; if( !pad->IsOnLayer( aZone->GetLayer() ) ) continue; if( pad->GetNetCode() != aZone->GetNetCode() ) continue; // Calculate thermal bridge half width int thermalBridgeWidth = aZone->GetThermalReliefCopperBridge( pad ) - aZone->GetMinThickness(); if( thermalBridgeWidth <= 0 ) continue; // we need the thermal bridge half width // with a small extra size to be sure we create a stub // slightly larger than the actual stub thermalBridgeWidth = ( thermalBridgeWidth + 4 ) / 2; int thermalReliefGap = aZone->GetThermalReliefGap( pad ); itemBB = pad->GetBoundingBox(); itemBB.Inflate( thermalReliefGap ); if( !( itemBB.Intersects( zoneBB ) ) ) continue; // Thermal bridges are like a segment from a starting point inside the pad // to an ending point outside the pad // calculate the ending point of the thermal pad, outside the pad VECTOR2I endpoint; endpoint.x = ( pad->GetSize().x / 2 ) + thermalReliefGap; endpoint.y = ( pad->GetSize().y / 2 ) + thermalReliefGap; // Calculate the starting point of the thermal stub // inside the pad VECTOR2I startpoint; int copperThickness = aZone->GetThermalReliefCopperBridge( pad ) - aZone->GetMinThickness(); if( copperThickness < 0 ) copperThickness = 0; // Leave a small extra size to the copper area inside to pad copperThickness += KiROUND( IU_PER_MM * 0.04 ); startpoint.x = std::min( pad->GetSize().x, copperThickness ); startpoint.y = std::min( pad->GetSize().y, copperThickness ); startpoint.x /= 2; startpoint.y /= 2; // This is a CIRCLE pad tweak // for circle pads, the thermal stubs orientation is 45 deg double fAngle = pad->GetOrientation(); if( pad->GetShape() == PAD_SHAPE_CIRCLE ) { endpoint.x = KiROUND( endpoint.x * aArcCorrection ); endpoint.y = endpoint.x; fAngle = aRoundPadThermalRotation; } // contour line width has to be taken into calculation to avoid "thermal stub bleed" endpoint.x += pen_radius; endpoint.y += pen_radius; // compute north, south, west and east points for zone connection. ptTest[0] = VECTOR2I( 0, endpoint.y ); // lower point ptTest[1] = VECTOR2I( 0, -endpoint.y ); // upper point ptTest[2] = VECTOR2I( endpoint.x, 0 ); // right point ptTest[3] = VECTOR2I( -endpoint.x, 0 ); // left point // Test all sides for( int i = 0; i < 4; i++ ) { // rotate point RotatePoint( ptTest[i], fAngle ); // translate point ptTest[i] += pad->ShapePos(); if( aRawFilledArea.Contains( ptTest[i] ) ) continue; spokes.Clear(); // polygons are rectangles with width of copper bridge value switch( i ) { case 0: // lower stub spokes.Append( -thermalBridgeWidth, endpoint.y ); spokes.Append( +thermalBridgeWidth, endpoint.y ); spokes.Append( +thermalBridgeWidth, startpoint.y ); spokes.Append( -thermalBridgeWidth, startpoint.y ); break; case 1: // upper stub spokes.Append( -thermalBridgeWidth, -endpoint.y ); spokes.Append( +thermalBridgeWidth, -endpoint.y ); spokes.Append( +thermalBridgeWidth, -startpoint.y ); spokes.Append( -thermalBridgeWidth, -startpoint.y ); break; case 2: // right stub spokes.Append( endpoint.x, -thermalBridgeWidth ); spokes.Append( endpoint.x, thermalBridgeWidth ); spokes.Append( +startpoint.x, thermalBridgeWidth ); spokes.Append( +startpoint.x, -thermalBridgeWidth ); break; case 3: // left stub spokes.Append( -endpoint.x, -thermalBridgeWidth ); spokes.Append( -endpoint.x, thermalBridgeWidth ); spokes.Append( -startpoint.x, thermalBridgeWidth ); spokes.Append( -startpoint.x, -thermalBridgeWidth ); break; } aCornerBuffer.NewOutline(); // add computed polygon to list for( int ic = 0; ic < spokes.PointCount(); ic++ ) { auto cpos = spokes.CPoint( ic ); RotatePoint( cpos, fAngle ); // Rotate according to module orientation cpos += pad->ShapePos(); // Shift origin to position aCornerBuffer.Append( cpos ); } } } } }
const SHAPE_LINE_CHAIN ConvexHull( const SHAPE_CONVEX& aConvex, int aClearance ) { // this defines the horizontal and vertical lines in the hull octagon BOX2I box = aConvex.BBox( aClearance + HULL_MARGIN ); box.Normalize(); SEG topline = SEG( VECTOR2I( box.GetX(), box.GetY() + box.GetHeight() ), VECTOR2I( box.GetX() + box.GetWidth(), box.GetY() + box.GetHeight() ) ); SEG rightline = SEG( VECTOR2I( box.GetX() + box.GetWidth(), box.GetY() + box.GetHeight() ), VECTOR2I( box.GetX() + box.GetWidth(), box.GetY() ) ); SEG bottomline = SEG( VECTOR2I( box.GetX() + box.GetWidth(), box.GetY() ), box.GetOrigin() ); SEG leftline = SEG( box.GetOrigin(), VECTOR2I( box.GetX(), box.GetY() + box.GetHeight() ) ); const SHAPE_LINE_CHAIN& vertices = aConvex.Vertices(); // top right diagonal VECTOR2I corner = box.GetOrigin() + box.GetSize(); SEG toprightline = SEG( corner, corner + VECTOR2I( box.GetHeight(), -box.GetHeight() ) ); MoveDiagonal( toprightline, vertices, aClearance ); // bottom right diagonal corner = box.GetOrigin() + VECTOR2I( box.GetWidth(), 0 ); SEG bottomrightline = SEG( corner + VECTOR2I( box.GetHeight(), box.GetHeight() ), corner ); MoveDiagonal( bottomrightline, vertices, aClearance ); // bottom left diagonal corner = box.GetOrigin(); SEG bottomleftline = SEG( corner, corner + VECTOR2I( -box.GetHeight(), box.GetHeight() ) ); MoveDiagonal( bottomleftline, vertices, aClearance ); // top left diagonal corner = box.GetOrigin() + VECTOR2I( 0, box.GetHeight() ); SEG topleftline = SEG( corner + VECTOR2I( -box.GetHeight(), -box.GetHeight() ), corner ); MoveDiagonal( topleftline, vertices, aClearance ); SHAPE_LINE_CHAIN octagon; octagon.SetClosed( true ); octagon.Append( *leftline.IntersectLines( bottomleftline ) ); octagon.Append( *bottomline.IntersectLines( bottomleftline ) ); octagon.Append( *bottomline.IntersectLines( bottomrightline ) ); octagon.Append( *rightline.IntersectLines( bottomrightline ) ); octagon.Append( *rightline.IntersectLines( toprightline ) ); octagon.Append( *topline.IntersectLines( toprightline ) ); octagon.Append( *topline.IntersectLines( topleftline ) ); octagon.Append( *leftline.IntersectLines( topleftline ) ); return octagon; }
bool SELECTION_TOOL::selectMultiple() { bool cancelled = false; // Was the tool cancelled while it was running? m_multiple = true; // Multiple selection mode is active KIGFX::VIEW* view = getView(); getViewControls()->SetAutoPan( true ); SELECTION_AREA area; view->Add( &area ); while( OPT_TOOL_EVENT evt = Wait() ) { if( evt->IsCancel() ) { cancelled = true; break; } if( evt->IsDrag( BUT_LEFT ) ) { if( !m_additive ) clearSelection(); // Start drawing a selection box area.SetOrigin( evt->DragOrigin() ); area.SetEnd( evt->Position() ); area.ViewSetVisible( true ); area.ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY ); } if( evt->IsMouseUp( BUT_LEFT ) ) { // End drawing the selection box area.ViewSetVisible( false ); // Mark items within the selection box as selected std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> selectedItems; BOX2I selectionBox = area.ViewBBox(); view->Query( selectionBox, selectedItems ); // Get the list of selected items std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR>::iterator it, it_end; for( it = selectedItems.begin(), it_end = selectedItems.end(); it != it_end; ++it ) { BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it->first ); // Add only those items that are visible and fully within the selection box if( !item->IsSelected() && selectable( item ) && selectionBox.Contains( item->ViewBBox() ) ) { select( item ); } } // Do not display information about selected item,as there is more than one m_frame->SetCurItem( NULL ); if( !m_selection.Empty() ) { // Inform other potentially interested tools m_toolMgr->ProcessEvent( SelectedEvent ); } break; // Stop waiting for events } } // Stop drawing the selection box area.ViewSetVisible( false ); view->Remove( &area ); m_multiple = false; // Multiple selection mode is inactive getViewControls()->SetAutoPan( false ); return cancelled; }