/** * @brief Creates a quest files model. * @param parent Path of the quest to represent. */ QuestFilesModel::QuestFilesModel(Quest& quest): QSortFilterProxyModel(nullptr), quest(quest), source_model(new QFileSystemModel) { // Watch changes on the filesystem. source_model->setRootPath(quest.get_data_path()); // Only watch changes in the data directory. source_model->setReadOnly(false); setSourceModel(source_model); // Watch changes in resources. connect(&quest.get_resources(), SIGNAL(element_added(ResourceType, QString, QString)), this, SLOT(resource_element_added(ResourceType, QString, QString))); connect(&quest.get_resources(), SIGNAL(element_removed(ResourceType, QString)), this, SLOT(resource_element_removed(ResourceType, QString))); connect(&quest.get_resources(), SIGNAL(element_renamed(ResourceType, QString, QString)), this, SLOT(resource_element_renamed(ResourceType, QString, QString))); connect(&quest.get_resources(), SIGNAL(element_description_changed(ResourceType, QString, QString)), this, SLOT(resource_element_description_changed(ResourceType, QString, QString))); // This model adds extra items for files missing on the filesystem. // To ensure we have an extra item if and only if the file is missing, // we need to watch files creations and destructions. connect(source_model, SIGNAL(rowsInserted(QModelIndex, int, int)), SLOT(source_model_rows_inserted(QModelIndex, int, int))); connect(source_model, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)), SLOT(source_model_rows_about_to_be_removed(QModelIndex, int, int))); }
/** * @brief Creates a resource model. * @param quest The quest. * @param resource_type Type of resources to show. * @param parent The parent object or nullptr. */ ResourceModel::ResourceModel(const Quest& quest, ResourceType resource_type, QObject* parent) : QStandardItemModel(parent), quest(quest), resource_type(resource_type), items(), icons(), directory_icon(":/images/icon_folder_open.png"), tileset_id() { QStringList ids = get_resources().get_elements(this->resource_type); for (const QString& id : ids) { add_element(id); } const QuestResources& resources = get_resources(); connect(&resources, SIGNAL(element_added(ResourceType, QString, QString)), this, SLOT(element_added(ResourceType, QString, QString))); connect(&resources, SIGNAL(element_removed(ResourceType, QString)), this, SLOT(element_removed(ResourceType, QString))); connect(&resources, SIGNAL(element_renamed(ResourceType, QString, QString)), this, SLOT(element_renamed(ResourceType, QString, QString))); connect(&resources, SIGNAL(element_description_changed(ResourceType, QString, QString)), this, SLOT(element_description_changed(ResourceType, QString, QString))); }
/** * @brief Changes the description of a resource element. * @param type A type of resource. * @param id Id of the element to change. * @param description The new description to set. * @return @c true in case of success, @c false if such an element does not * exist or if the description is invalid. */ bool QuestResources::set_description( ResourceType type, const QString& id, const QString& description) { if (description.isEmpty() || description.contains("\"") || description.contains("\'") || description.contains("\n") || description.contains("\r") || description.contains("\\") ) { return false; } if (!resources.set_description(type, id.toStdString(), description.toStdString())) { return false; } emit element_description_changed(type, id, description); return true; }
/** * @brief Creates a map editor. * @param quest The quest containing the file. * @param path Path of the map data file to open. * @param parent The parent object or nullptr. * @throws EditorException If the file could not be opened. */ MapEditor::MapEditor(Quest& quest, const QString& path, QWidget* parent) : Editor(quest, path, parent), map_id(), map(nullptr), entity_creation_toolbar(nullptr), status_bar(nullptr), ignore_tileset_selection_changes(false) { ui.setupUi(this); build_entity_creation_toolbar(); build_status_bar(); // Get the map. ResourceType resource_type; QString map_id; quest.check_exists(path); if (!quest.is_resource_element(path, resource_type, map_id) || resource_type != ResourceType::MAP) { throw EditorException(tr("File '%1' is not a map").arg(path)); } this->map_id = map_id; // Editor properties. set_title(tr("Map %1").arg(get_file_name_without_extension())); set_icon(QIcon(":/images/icon_resource_map.png")); set_close_confirm_message( tr("Map '%1' has been modified. Save changes?").arg(map_id)); set_select_all_supported(true); set_zoom_supported(true); get_view_settings().set_zoom(2.0); set_grid_supported(true); set_entity_type_visibility_supported(true); // Shortcuts. QAction* open_script_action = new QAction(this); open_script_action->setShortcut(tr("F4")); open_script_action->setShortcutContext(Qt::WindowShortcut); connect(open_script_action, SIGNAL(triggered(bool)), this, SLOT(open_script_requested())); addAction(open_script_action); // Open the file. map = new MapModel(quest, map_id, this); get_undo_stack().setClean(); // Prepare the gui. const int side_width = 350; ui.splitter->setSizes({ side_width, width() - side_width }); ui.map_side_splitter->setStretchFactor(0, 0); // Don't expand the map properties view ui.map_side_splitter->setStretchFactor(1, 1); // but only the tileset view. ui.tileset_field->set_resource_type(ResourceType::TILESET); ui.tileset_field->set_quest(quest); ui.music_field->set_resource_type(ResourceType::MUSIC); ui.music_field->set_quest(quest); ui.music_field->add_special_value("none", tr("<No music>"), 0); ui.music_field->add_special_value("same", tr("<Same as before>"), 1); ui.tileset_view->set_read_only(true); ui.map_view->set_map(map); ui.map_view->set_view_settings(get_view_settings()); ui.map_view->set_common_actions(&get_common_actions()); ui.size_field->config("x", 0, 99999, 8); ui.size_field->set_tooltips( tr("Width of the map in pixels"), tr("Height of the map in pixels")); ui.location_field->config(",", 0, 99999, 8); ui.location_field->set_tooltips( tr("Coordinates of the map in its world (useful to make adjacent scrolling maps)"), tr("Coordinates of the map in its world (useful to make adjacent scrolling maps)")); set_num_layers_visibility_supported(map->get_num_layers()); load_settings(); update(); // Make connections. connect(&get_resources(), SIGNAL(element_description_changed(ResourceType, QString, QString)), this, SLOT(update_description_to_gui())); connect(ui.description_field, SIGNAL(editingFinished()), this, SLOT(set_description_from_gui())); connect(ui.size_field, SIGNAL(editing_finished()), this, SLOT(change_size_requested())); connect(map, SIGNAL(size_changed(QSize)), this, SLOT(update_size_field())); connect(ui.world_check_box, SIGNAL(stateChanged(int)), this, SLOT(world_check_box_changed())); connect(ui.world_field, SIGNAL(editingFinished()), this, SLOT(change_world_requested())); connect(map, SIGNAL(world_changed(QString)), this, SLOT(update_world_field())); connect(ui.floor_check_box, SIGNAL(stateChanged(int)), this, SLOT(floor_check_box_changed())); connect(ui.floor_field, SIGNAL(editingFinished()), this, SLOT(change_floor_requested())); connect(map, SIGNAL(floor_changed(int)), this, SLOT(update_floor_field())); connect(ui.location_field, SIGNAL(editing_finished()), this, SLOT(change_location_requested())); connect(map, SIGNAL(location_changed(QPoint)), this, SLOT(update_location_field())); connect(ui.tileset_field, SIGNAL(activated(QString)), this, SLOT(tileset_selector_activated())); connect(map, SIGNAL(tileset_id_changed(QString)), this, SLOT(tileset_id_changed(QString))); connect(ui.tileset_refresh_button, SIGNAL(clicked()), map, SLOT(tileset_modified())); connect(ui.tileset_edit_button, SIGNAL(clicked()), this, SLOT(open_tileset_requested())); connect(ui.music_field, SIGNAL(activated(QString)), this, SLOT(music_selector_activated())); connect(map, SIGNAL(music_id_changed(QString)), this, SLOT(update_music_field())); connect(ui.open_script_button, SIGNAL(clicked()), this, SLOT(open_script_requested())); connect(ui.map_view, SIGNAL(edit_entity_requested(EntityIndex, EntityModelPtr&)), this, SLOT(edit_entity_requested(EntityIndex, EntityModelPtr&))); connect(ui.map_view, SIGNAL(move_entities_requested(EntityIndexes, QPoint, bool)), this, SLOT(move_entities_requested(EntityIndexes, QPoint, bool))); connect(ui.map_view, SIGNAL(resize_entities_requested(QMap<EntityIndex, QRect>, bool)), this, SLOT(resize_entities_requested(QMap<EntityIndex, QRect>, bool))); connect(ui.map_view, SIGNAL(convert_tiles_requested(EntityIndexes)), this, SLOT(convert_tiles_requested(EntityIndexes))); connect(ui.map_view, SIGNAL(set_entities_direction_requested(EntityIndexes, int)), this, SLOT(set_entities_direction_requested(EntityIndexes, int))); connect(ui.map_view, SIGNAL(set_entities_layer_requested(EntityIndexes, int)), this, SLOT(set_entities_layer_requested(EntityIndexes, int))); connect(ui.map_view, SIGNAL(bring_entities_to_front_requested(EntityIndexes)), this, SLOT(bring_entities_to_front_requested(EntityIndexes))); connect(ui.map_view, SIGNAL(bring_entities_to_back_requested(EntityIndexes)), this, SLOT(bring_entities_to_back_requested(EntityIndexes))); connect(ui.map_view, SIGNAL(add_entities_requested(AddableEntities&)), this, SLOT(add_entities_requested(AddableEntities&))); connect(ui.map_view, SIGNAL(remove_entities_requested(EntityIndexes)), this, SLOT(remove_entities_requested(EntityIndexes))); connect(ui.map_view->get_scene(), SIGNAL(selectionChanged()), this, SLOT(map_selection_changed())); }