item_state.h 7.56 KB
Newer Older
Maciej Suminski's avatar
Maciej Suminski committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
/*
 * This program source code file is part of KiCad, a free EDA CAD application.
 *
 * Copyright (C) 2013 CERN
 * @author Maciej Suminski <maciej.suminski@cern.ch>
 *
 * This program 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 2
 * of the License, or (at your option) any later version.
 *
 * This program 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 this program; if not, you may find one here:
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * or you may search the http://www.gnu.org website for the version 2 license,
 * or you may write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 */

25 26 27 28 29 30
#ifndef ITEM_STATE_H_
#define ITEM_STATE_H_

#include <deque>
#include <class_board_item.h>

Maciej Suminski's avatar
Maciej Suminski committed
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
/**
 * Class ITEM_STATE
 *
 * Provides means for modifying properties of groups of items and gives possibility of rolling back
 * the introduced changes. Does not take ownership of modified items, neither takes care of
 * refreshing.
 */
class ITEM_STATE
{
public:
    ITEM_STATE() :
        m_movement( 0.0, 0.0 ), m_flips( 0 ), m_rotation( 0.0 )
    {
#ifdef __WXDEBUG__
        m_canSave = true;
#endif
    }

    /**
     * Function Save()
     *
     * Adds an item and saves it's state.
     * @param aItem is the item to be added.
     */
    void Save( BOARD_ITEM* aItem )
    {
#ifdef __WXDEBUG__
58 59
        wxASSERT_MSG( m_canSave, wxT( "You cannot save items after issuing commands. You have "
                                      "either RestoreAll() or Apply() before adding items!" ) );
Maciej Suminski's avatar
Maciej Suminski committed
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
#endif
        m_items.push_back( aItem );
    }

    /**
     * Function RestoreAll()
     *
     * Rollbacks all the changes to the initial state.
     */
    void RestoreAll()
    {
        // Check if there is a not saved movement command
        saveMovement();

        std::deque<BOARD_ITEM*>::iterator it, it_end;
        std::deque<COMMAND>::iterator cmd, cmd_end;
76

Maciej Suminski's avatar
Maciej Suminski committed
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
        for( it = m_items.begin(), it_end = m_items.end(); it != it_end; ++it )
        {
            for( cmd = m_commands.begin(), cmd_end = m_commands.end(); cmd != cmd_end; ++cmd )
                cmd->Revert( *it );
        }

        reset();
    }

    /**
     * Function Apply()
     *
     * Resets the state, clears the list of items & changes, so the object can be reused for
     * other items.
     */
    void Apply()
    {
        reset();
    }

    /**
     * Function Move()
     *
     * Moves stored items by a given vector.
     * @param aMovement is the movement vector.
     */
    void Move( const VECTOR2D& aMovement )
    {
#ifdef __WXDEBUG__
        m_canSave = false;
#endif
        std::deque<BOARD_ITEM*>::iterator it, it_end;
109

Maciej Suminski's avatar
Maciej Suminski committed
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
        for( it = m_items.begin(), it_end = m_items.end(); it != it_end; ++it )
            (*it)->Move( wxPoint( aMovement.x, aMovement.y ) );

        m_movement += aMovement;
    }

    /**
     * Function Rotate()
     *
     * Rotates stored items by a given angle.
     * @param aAngle is the angle (in decidegrees).
     */
    void Rotate( const VECTOR2D& aPoint, double aAngle )
    {
#ifdef __WXDEBUG__
        m_canSave = false;
#endif
        saveMovement();
        m_commands.push_front( COMMAND( COMMAND::ROTATE, aPoint, aAngle ) );

        std::deque<BOARD_ITEM*>::iterator it, it_end;
131

Maciej Suminski's avatar
Maciej Suminski committed
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
        for( it = m_items.begin(), it_end = m_items.end(); it != it_end; ++it )
            (*it)->Rotate( wxPoint( aPoint.x, aPoint.y ), aAngle );

        m_rotation += aAngle;
    }

    /**
     * Function Flip()
     *
     * Changes the board side for stored items.
     * @param aPoint is the rotation point.
     */
    void Flip( const VECTOR2D& aPoint )
    {
#ifdef __WXDEBUG__
        m_canSave = false;
#endif
        saveMovement();
        m_commands.push_front( COMMAND( COMMAND::FLIP, aPoint ) );

        std::deque<BOARD_ITEM*>::iterator it, it_end;
153

Maciej Suminski's avatar
Maciej Suminski committed
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
        for( it = m_items.begin(), it_end = m_items.end(); it != it_end; ++it )
            (*it)->Flip( wxPoint( aPoint.x, aPoint.y ) );

        m_flips++;
    }

    /**
     * Function ToggleVisibility()
     *
     * Switches the visibility property of stored items.
     */
    void ToggleVisibility()
    {
#ifdef __WXDEBUG__
        m_canSave = false;
#endif
        m_commands.push_front( COMMAND( COMMAND::VISIBILITY ) );

        std::deque<BOARD_ITEM*>::iterator it, it_end;
173

Maciej Suminski's avatar
Maciej Suminski committed
174 175 176 177 178 179 180 181 182 183 184
        for( it = m_items.begin(), it_end = m_items.end(); it != it_end; ++it )
            (*it)->ViewSetVisible( !(*it)->ViewIsVisible() );
    }

    /**
     * Function GetUpdateFlag()
     *
     * Returns information on what kind of update should be applied to items in order to display
     * them properly.
     * @return Flag required to refresh items.
     */
Maciej Suminski's avatar
Maciej Suminski committed
185
    KIGFX::VIEW_ITEM::VIEW_UPDATE_FLAGS GetUpdateFlag() const
Maciej Suminski's avatar
Maciej Suminski committed
186
    {
187 188
        if( m_flips % 2 == 1 ) // If number of flips is odd, then we need to change layers
            return KIGFX::VIEW_ITEM::LAYERS;
Maciej Suminski's avatar
Maciej Suminski committed
189
        else if( m_movement.x != 0.0 || m_movement.y != 0.0 || m_rotation != 0.0 )
190
            return KIGFX::VIEW_ITEM::GEOMETRY;
Maciej Suminski's avatar
Maciej Suminski committed
191

192
        return KIGFX::VIEW_ITEM::APPEARANCE;
Maciej Suminski's avatar
Maciej Suminski committed
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
    }

private:
    /// COMMAND stores modifications that were done to items
    struct COMMAND
    {
        /// Type of command
        enum TYPE { MOVE, ROTATE, FLIP, VISIBILITY };
        TYPE m_type;

        /// Point where flip/rotation occurred or movement vector
        VECTOR2D m_point;

        /// Used only for rotation
        double m_angle;

        COMMAND( TYPE aType, VECTOR2D aPoint = VECTOR2D( 0.0, 0.0 ), double aAngle = 0.0 ) :
            m_type( aType ), m_point( aPoint ), m_angle( aAngle ) {};

        void Revert( BOARD_ITEM* aItem )
        {
            switch( m_type )
            {
            case MOVE:
                aItem->Move( wxPoint( -m_point.x, -m_point.y ) );
                break;

            case ROTATE:
                aItem->Rotate( wxPoint( m_point.x, m_point.y ), -m_angle );
                break;

            case FLIP:
                aItem->Flip( wxPoint( m_point.x, m_point.y ) );
                break;

            case VISIBILITY:
                aItem->ViewSetVisible( !aItem->ViewIsVisible() );
                break;
            }
        }
    };

    /// Adds a MOVEMENT command basing on the current movement vector
    void saveMovement()
    {
        if( m_movement.x != 0.0 || m_movement.y != 0.0 )
        {
            m_commands.push_front( COMMAND( COMMAND::MOVE, m_movement ) );

            m_movement.x = 0.0;
            m_movement.y = 0.0;
        }
    }

    /// Restores the initial state
    void reset()
    {
        m_movement.x = 0.0;
        m_movement.y = 0.0;
        m_flips = 0;
        m_rotation = 0.0;

        m_items.clear();
        m_commands.clear();

#ifdef __WXDEBUG__
        m_canSave = true;
#endif
    }

    /// List of issued commands
    std::deque<BOARD_ITEM*> m_items;

    /// List of items that are affected by commands
    std::deque<COMMAND> m_commands;

    /// Current movement vector (updated by Move() command)
    VECTOR2D m_movement;

    /// Number of flips applied to items
    unsigned int m_flips;

    /// Total rotation applied to items
    double m_rotation;

#ifdef __WXDEBUG__
    /// Debug flag assuring that functions are called in proper order
    bool m_canSave;
#endif
};

#endif /* ITEM_STATE_H_ */