1 /* 2 * Copyright (C) 2014-2018 Christopho, Solarus - http://www.solarus-games.org 3 * 4 * Solarus Quest Editor is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * Solarus Quest Editor is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License along 15 * with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 #ifndef SOLARUSEDITOR_SPRITE_MODEL_H 18 #define SOLARUSEDITOR_SPRITE_MODEL_H 19 20 #include "natural_comparator.h" 21 22 #include <solarus/graphics/SpriteData.h> 23 #include <QAbstractItemModel> 24 #include <QImage> 25 #include <QItemSelectionModel> 26 #include <QPixmap> 27 #include <map> 28 #include <memory> 29 30 namespace SolarusEditor { 31 32 class Quest; 33 34 /** 35 * @brief Model that wraps a sprite. 36 * 37 * It makes the link between the editor and the sprite data of the 38 * Solarus library. 39 * Signals are sent when something changes in the wrapped sprite. 40 * This model can be used as a model for a tree view of animations 41 * and directions. 42 * It also stores the selection information. 43 */ 44 class SpriteModel : public QAbstractItemModel { 45 Q_OBJECT 46 47 public: 48 49 /** 50 * @brief Index identifying an animation and direction of a sprite. 51 */ 52 struct Index { 53 54 public: 55 56 /** 57 * @brief Creates a sprite direction index. 58 * @param animation_name Animation name of this index. 59 * @param direction_nb Direction number of this index. 60 */ 61 Index(const QString& animation_name = "", int direction_nb = -1) : animation_nameIndex62 animation_name(animation_name), 63 direction_nb(direction_nb) { 64 } 65 66 /** 67 * @brief Returns whether this index is valid. 68 * @return \c true if this index is valid. 69 */ is_validIndex70 bool is_valid() const { 71 return !animation_name.isEmpty(); 72 } 73 74 /** 75 * @brief Returns whether this index represents an animation. 76 * @return \c true if this index represents an animation. 77 */ is_animation_indexIndex78 bool is_animation_index() const { 79 return !animation_name.isEmpty() && direction_nb < 0; 80 } 81 82 /** 83 * @brief Returns whether this index represents a direction. 84 * @return \c true if this index represents a direction. 85 */ is_direction_indexIndex86 bool is_direction_index() const { 87 return !animation_name.isEmpty() && direction_nb >= 0; 88 } 89 90 QString animation_name; /**< Animation name of this index. */ 91 int direction_nb; /**< Direction number of this index. */ 92 }; 93 94 // Creation. 95 SpriteModel(const Quest& quest, const QString& sprite_id, QObject* parent = nullptr); 96 97 const Quest& get_quest() const; 98 QString get_sprite_id() const; 99 QString get_default_animation_name() const; 100 void set_default_animation_name(const QString& default_animation_name); 101 bool exists(const Index& index) const; 102 QModelIndex get_model_index(const Index& index) const; 103 QString get_tileset_id() const; 104 void set_tileset_id(const QString& tileset_id); 105 106 // QAbstractItemModel interface. 107 int columnCount(const QModelIndex& parent = QModelIndex()) const override; 108 int rowCount(const QModelIndex& parent = QModelIndex()) const override; 109 QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; 110 QModelIndex parent(const QModelIndex& model_index) const override; 111 bool hasChildren(const QModelIndex& parent = QModelIndex()) const override; 112 QVariant data(const QModelIndex& model_index, int role) const override; 113 114 // Animation. 115 int get_animation_nb(const Index& index) const; 116 Index get_animation_index(int animation_nb) const; 117 118 bool animation_exists(const Index& index) const; 119 void create_animation(const QString& animation_name); 120 void insert_animation( 121 const Index& index, const Solarus::SpriteAnimationData& data); 122 void delete_animation(const Index& index); 123 void set_animation_name(const Index& index, const QString& new_name); 124 125 QStringList get_animation_names() const; 126 Solarus::SpriteAnimationData get_animation_data(const Index& index) const; 127 QString get_animation_source_image(const Index& index) const; 128 bool is_animation_image_tileset(const Index& index) const; 129 void set_animation_source_image(const Index& index, const QString &src_image); 130 uint32_t get_animation_frame_delay(const Index& index) const; 131 void set_animation_frame_delay(const Index& index, uint32_t frame_delay); 132 int get_animation_loop_on_frame(const Index& index) const; 133 void set_animation_loop_on_frame(const Index& index, int loop_on_frame); 134 int get_animation_num_directions(const Index& index) const; 135 int get_animation_max_num_frames(const Index& index) const; 136 137 // Direction. 138 bool direction_exists(const Index& index) const; 139 int add_direction( 140 const Index& index, const QRect &frame, 141 int num_frames = 1, int num_columns = 1); 142 int insert_direction( 143 const Index& index, const Solarus::SpriteAnimationDirectionData &data); 144 void delete_direction(const Index& index); 145 void move_direction(const Index& index, int new_direction_nb); 146 147 Solarus::SpriteAnimationDirectionData 148 get_direction_data(const Index& index) const; 149 QRect get_direction_first_frame_rect(const Index& index) const; 150 QRect get_direction_all_frames_rect(const Index& index) const; 151 QList<QRect> get_direction_frames(const Index& index) const; 152 QPoint get_direction_position(const Index& index) const; 153 void set_direction_position(const Index& index, const QPoint& position); 154 QSize get_direction_size(const Index& index) const; 155 void set_direction_size(const Index& index, const QSize& size); 156 QPoint get_direction_origin(const Index& index) const; 157 void set_direction_origin(const Index& index, const QPoint& origin); 158 static QPoint get_direction_default_origin(const QSize& frame_size); 159 bool is_direction_multi_frame(const Index& index) const; 160 int get_direction_num_frames(const Index& index) const; 161 void set_direction_num_frames(const Index& index, int num_frames); 162 int get_direction_num_columns(const Index& index) const; 163 void set_direction_num_columns(const Index& index, int num_columns); 164 165 // Images. 166 QImage get_animation_image(const Index& index) const; 167 QList<QPixmap> get_direction_all_frames(const Index& index) const; 168 QPixmap get_direction_first_frame(const Index& index) const; 169 QPixmap get_direction_frame(const Index& index, int frame) const; 170 QPixmap get_direction_icon(const Index& index) const; 171 QPixmap get_icon() const; 172 173 // Selection. 174 QItemSelectionModel& get_selection_model(); 175 bool is_selection_empty() const; 176 Index get_selected_index() const; 177 void set_selected_index(const Index& index); 178 void set_selected_animation(const Index& index); 179 void clear_selection(); 180 181 signals: 182 183 void default_animation_changed( 184 const QString& old_default_animation, const QString& new_default_animation); 185 void animation_created(const Index& index); 186 void animation_deleted(const Index& index); 187 void animation_name_changed(const Index& old_index, const Index& new_index); 188 void animation_image_changed(const Index& index, const QString& src_image); 189 void animation_frame_delay_changed(const Index& index, uint32_t frame_delay); 190 void animation_loop_on_frame_changed(const Index& index, int loop_on_frame); 191 192 void direction_added(const Index& index); 193 void direction_deleted(const Index& index); 194 void direction_moved(const Index& index, int new_direction_nb); 195 void direction_position_changed(const Index& index, const QPoint& position); 196 void direction_size_changed(const Index& index, const QSize& size); 197 void direction_origin_changed(const Index& index, const QPoint& origin); 198 void direction_num_frames_changed(const Index& index, int num_frames); 199 void direction_num_columns_changed(const Index& index, int num_columns); 200 201 public slots: 202 203 void save() const; 204 205 private: 206 207 /** 208 * @brief Data of a specific sprite animation direction. 209 */ 210 struct DirectionModel { 211 212 public: 213 214 /** 215 * @brief Creates a sprite animation direction model. 216 */ DirectionModelDirectionModel217 DirectionModel(const QString& animation_name, int direction_nb) : 218 index(new Index(animation_name, direction_nb)) { 219 } 220 221 /** 222 * @brief Clears the image cache of this direction. 223 */ set_image_dirtyDirectionModel224 void set_image_dirty() const { 225 frames.clear(); 226 icon = QPixmap(); 227 } 228 229 std::shared_ptr<Index> index; /**< Index of the direction. */ 230 mutable QList<QPixmap> frames; /**< All frames of the direction. */ 231 mutable QPixmap icon; /**< 32x32 icon of the direction. */ 232 }; 233 234 /** 235 * @brief Data of a specific sprite animation. 236 */ 237 struct AnimationModel { 238 239 public: 240 241 /** 242 * @brief Creates a sprite animation model. 243 * @param name Name of the animation to represent. 244 */ AnimationModelAnimationModel245 AnimationModel(const QString& animation_name) : 246 index(new Index(animation_name)) { 247 } 248 249 /** 250 * @brief Returns the name of this animation. 251 * @return The animation name. 252 */ get_animation_nameAnimationModel253 QString get_animation_name() const { 254 return index->animation_name; 255 } 256 257 /** 258 * @brief Changes the name of this animation. 259 * @param animation_name The animation name. 260 */ set_animation_nameAnimationModel261 void set_animation_name(const QString& animation_name) { 262 index->animation_name = animation_name; 263 for (auto& direction: directions) { 264 direction.index->animation_name = animation_name; 265 } 266 } 267 268 /** 269 * @brief Clears the image cache of this animation. 270 */ set_image_dirtyAnimationModel271 void set_image_dirty() const { 272 image = QImage(); 273 for (const auto& direction: directions) { 274 direction.set_image_dirty(); 275 } 276 } 277 278 std::shared_ptr<Index> index; /**< Index of the animation. */ 279 QList<DirectionModel> directions; /**< Directions of the animation. */ 280 mutable QImage image; /**< Image of the animation. */ 281 }; 282 283 void build_index_map(); 284 285 void set_animation_image_dirty(const Index& index); 286 void set_direction_image_dirty(const Index& index); 287 288 const Solarus::SpriteAnimationData& get_animation(const Index& index) const; 289 Solarus::SpriteAnimationData& get_animation(const Index& index); 290 291 const Solarus::SpriteAnimationDirectionData& get_direction(const Index& index) const; 292 Solarus::SpriteAnimationDirectionData& get_direction(const Index& index); 293 294 const Quest& quest; /**< The quest the sprite belongs to. */ 295 const QString sprite_id; /**< Id of the sprite. */ 296 Solarus::SpriteData sprite; /**< Sprite data wrapped by this model. */ 297 QString tileset_id; /**< Tileset id used for animations images. */ 298 299 std::map<QString, int, NaturalComparator> 300 names_to_indexes; /**< Index in the list of each animation. 301 * The order is determined here. */ 302 303 QList<AnimationModel> 304 animations; /**< All animations. */ 305 306 QItemSelectionModel 307 selection_model; /**< Animations currently selected. */ 308 }; 309 310 } 311 312 #endif 313