Exemplo n.º 1
0
 def _load_dtef_rendering(self):
     self.dtef = ExplorersDtef(self.dma, self.dpc, self.dpci, self.dpl, self.dpla)
     box: Gtk.Box = self.builder.get_object('dungeon_image_placeholder')
     for child in box:
         box.remove(child)
     image: Gtk.Image = Gtk.Image.new_from_surface(pil_to_cairo_surface(self.dtef.get_tiles()[0].convert('RGBA')))
     box.pack_start(image, True, True, 0)
     box.show_all()
Exemplo n.º 2
0
    def _reinit_image(self):
        cb_store: Gtk.ListStore = self.builder.get_object('cb_weather_store')
        cb: Gtk.ComboBoxText = self.builder.get_object('cb_weather')
        v : int = cb_store[cb.get_active_iter()][0]
        surface = self.colvec.to_pil(v)
        surface = surface.resize((surface.width*16, surface.height*16), resample=Image.NEAREST)
        self.colormap = pil_to_cairo_surface(surface.convert('RGBA'))

        self.surface = None
        if self.dma:
            self.dtef = ExplorersDtef(self.dma, self.dpc, self.dpci, self.dpl, self.dpla)
            surface = self.dtef.get_tiles()[0]
            
            #Apply colormap
            surface.putpalette(self.colvec.apply_colormap(v, list(surface.palette.palette)))
            self.surface = pil_to_cairo_surface(surface.convert('RGBA'))
        
        self.builder.get_object('draw_tileset').queue_draw()
        self.builder.get_object('draw_colormap').queue_draw()
Exemplo n.º 3
0
class TilesetController(AbstractController):
    _last_open_tab_id = 0
    def __init__(self, module: 'DungeonGraphicsModule', item_id: int):
        self.module = module
        self.item_id = item_id

        self.builder = None

        self.dma: Dma = module.get_dma(item_id)
        self.dpl: Dpl = module.get_dpl(item_id)
        self.dpla: Dpla = module.get_dpla(item_id)
        self.dpc: Dpc = module.get_dpc(item_id)
        self.dpci: Dpci = module.get_dpci(item_id)

        self.rules = [
            [0, 0, 0],
            [0, 0, 0],
            [0, 0, 0]
        ]
        self.pal_ani_durations = 0

        self.current_icon_view_renderers: List[DungeonChunkCellDrawer] = []

        self.chunks_surfaces: Iterable[Iterable[List[cairo.Surface]]] = []

        self._init_chunk_imgs()
        self.dtef = None

        self.menu_controller = BgMenuController(self)

    def get_view(self) -> Gtk.Widget:
        self.builder = self._get_builder(__file__, 'tileset.glade')
        self._init_rules()
        self._init_rule_icon_views()
        self._init_chunk_picker_icon_view()
        self._init_secondary_terrain()
        self.builder.connect_signals(self)
        editor = self.builder.get_object('editor')
        root = self.builder.get_object('editor_root')
        root.set_current_page(self.__class__._last_open_tab_id)
        self.on_editor_root_switch_page(None, None, self.__class__._last_open_tab_id)
        self.builder.get_object('label_tileset_name').set_text(f(_('Dungeon Tileset {self.item_id} Rules')))
        self.builder.get_object('label_tileset_name2').set_text(f(_('Dungeon Tileset {self.item_id}')))
        return editor

    def on_editor_root_switch_page(self, w, p, pnum, *args):
        self.__class__._last_open_tab_id = pnum
        if pnum == 1:
            for renderer in self.current_icon_view_renderers:
                renderer.start()
        else:
            self._load_dtef_rendering()
            for renderer in self.current_icon_view_renderers:
                renderer.stop()

    # SIMPLE MODE

    def on_btn_import_clicked(self, *args):
        md = SkyTempleMessageDialog(MainController.window(),
                                    Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.INFO,
                                    Gtk.ButtonsType.OK, _("To import select the XML file in the DTEF tileset package. If it "
                                                          "is still zipped, unzip it first."),
                                    title="SkyTemple")
        md.run()
        md.destroy()

        dialog = Gtk.FileChooserNative.new(
            _("Import dungeon tileset..."),
            MainController.window(),
            Gtk.FileChooserAction.OPEN,
            None, None
        )

        filter = Gtk.FileFilter()
        filter.set_name(_("DTEF XML document (*.dtef.xml)"))
        filter.add_pattern("*.dtef.xml")
        dialog.add_filter(filter)
        add_dialog_xml_filter(dialog)

        response = dialog.run()
        fn = dialog.get_filename()
        dialog.destroy()

        if response == Gtk.ResponseType.ACCEPT:
            try:
                dirname = os.path.dirname(fn)
                fn_xml = fn
                fn_var0 = os.path.join(dirname, VAR0_FN)
                fn_var1 = os.path.join(dirname, VAR1_FN)
                fn_var2 = os.path.join(dirname, VAR2_FN)
                dtef_importer = ExplorersDtefImporter(self.dma, self.dpc, self.dpci, self.dpl, self.dpla)
                dtef_importer.do_import(
                    dirname, fn_xml, fn_var0, fn_var1, fn_var2
                )

                md = SkyTempleMessageDialog(MainController.window(),
                                            Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.INFO,
                                            Gtk.ButtonsType.OK, _("The tileset was successfully imported."),
                                            title=_("Success!"), is_success=True)
                md.run()
                md.destroy()
                self.mark_as_modified()
                MainController.reload_view()
            except BaseException as e:
                display_error(
                    sys.exc_info(),
                    str(e),
                    _("Error importing the tileset.")
                )

    def on_btn_export_clicked(self, *args):
        dialog = Gtk.FileChooserNative.new(
            _("Export dungeon tileset..."),
            MainController.window(),
            Gtk.FileChooserAction.SELECT_FOLDER,
            None, None
        )

        response = dialog.run()
        fn = dialog.get_filename()
        dialog.destroy()

        if response == Gtk.ResponseType.ACCEPT:
            try:
                # Write XML
                with open(os.path.join(fn, 'tileset.dtef.xml'), 'w') as f:
                    f.write(prettify(self.dtef.get_xml()))
                # Write Tiles
                var0, var1, var2, rest = self.dtef.get_tiles()
                var0fn, var1fn, var2fn, restfn = self.dtef.get_filenames()
                var0.save(os.path.join(fn, var0fn))
                var1.save(os.path.join(fn, var1fn))
                var2.save(os.path.join(fn, var2fn))
                rest.save(os.path.join(fn, restfn))
                shutil.copy(get_template_file(), os.path.join(fn, 'template.png'))

                md = SkyTempleMessageDialog(MainController.window(),
                                            Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.INFO,
                                            Gtk.ButtonsType.OK, _("The tileset was successfully exported."),
                                            title=_("Success!"), is_success=True)
                md.run()
                md.destroy()
            except BaseException as e:
                display_error(
                    sys.exc_info(),
                    str(e),
                    _("Error exporting the tileset.")
                )

    def on_btn_help_clicked(self, *args):
        webbrowser.open_new_tab(URL_HELP)

    # END SIMPLE MODE

    def on_men_chunks_edit_activate(self, *args):
        self.menu_controller.on_men_chunks_layer1_edit_activate()

    def on_men_chunks_export_activate(self, *args):
        self.menu_controller.on_men_chunks_layer1_export_activate()

    def on_men_chunks_import_activate(self, *args):
        self.menu_controller.on_men_chunks_layer1_import_activate()

    def on_men_tiles_export_activate(self, *args):
        self.menu_controller.on_men_tiles_layer1_export_activate()

    def on_men_tiles_import_activate(self, *args):
        self.menu_controller.on_men_tiles_layer1_import_activate()

    def on_men_palettes_edit_activate(self, *args):
        self.menu_controller.on_men_palettes_edit_activate()

    def on_men_palettes_ani_settings_activate(self, *args):
        self.menu_controller.on_men_palettes_ani_settings_activate()

    def on_men_palettes_ani_edit_11_activate(self, *args):
        self.menu_controller.edit_palette_ani(0)

    def on_men_palettes_ani_edit_12_activate(self, *args):
        self.menu_controller.edit_palette_ani(1)

    def on_men_tools_tilequant_activate(self, *args):
        MainController.show_tilequant_dialog(12, 16)

    def on_rules_active_type_changed(self, widget, *area):
        self.update_chunks_from_current_rules()

    def on_rules_a0_toggled(self, widget: Gtk.CheckButton, *area):
        self._rules_pos_toggle(0, widget.get_active())

    def on_rules_a1_toggled(self, widget: Gtk.CheckButton, *area):
        self._rules_pos_toggle(1, widget.get_active())

    def on_rules_a2_toggled(self, widget: Gtk.CheckButton, *area):
        self._rules_pos_toggle(2, widget.get_active())

    def on_rules_a3_toggled(self, widget: Gtk.CheckButton, *area):
        self._rules_pos_toggle(3, widget.get_active())

    def on_rules_a4_toggled(self, widget: Gtk.CheckButton, *area):
        self._rules_pos_toggle(4, widget.get_active())

    def on_rules_a5_toggled(self, widget: Gtk.CheckButton, *area):
        self._rules_pos_toggle(5, widget.get_active())

    def on_rules_a6_toggled(self, widget: Gtk.CheckButton, *area):
        self._rules_pos_toggle(6, widget.get_active())

    def on_rules_a7_toggled(self, widget: Gtk.CheckButton, *area):
        self._rules_pos_toggle(7, widget.get_active())

    def on_rules_a8_toggled(self, widget: Gtk.CheckButton, *area):
        self._rules_pos_toggle(8, widget.get_active())

    def on_rules_chunk_picker_selection_changed(self, icon_view: Gtk.IconView):
        pass

    def on_secondary_terrain_type_changed(self, w: Gtk.ComboBox):
        idx = w.get_active()
        static = self.module.project.get_rom_module().get_static_data()
        secondary_terrains = HardcodedDungeons.get_secondary_terrains(
            self.module.project.get_binary(BinaryName.ARM9), static
        )
        secondary_terrains[self.item_id] = SecondaryTerrainTableEntry(idx)

        def update(arm9):
            HardcodedDungeons.set_secondary_terrains(secondary_terrains, arm9, static)
        self.module.project.modify_binary(BinaryName.ARM9, update)

        self.mark_as_modified()

    def on_rules_main_1_selection_changed(self, icon_view):
        model, treeiter = icon_view.get_model(), icon_view.get_selected_items()
        if model is not None and treeiter is not None and treeiter != []:
            self._rule_updated(model[treeiter][0], 0)

    def on_rules_main_2_selection_changed(self, icon_view):
        model, treeiter = icon_view.get_model(), icon_view.get_selected_items()
        if model is not None and treeiter is not None and treeiter != []:
            self._rule_updated(model[treeiter][0], 1)

    def on_rules_main_3_selection_changed(self, icon_view):
        model, treeiter = icon_view.get_model(), icon_view.get_selected_items()
        if model is not None and treeiter is not None and treeiter != []:
            self._rule_updated(model[treeiter][0], 2)

    def on_rules_extra_selection_changed(self, icon_view_extra):
        model, treeiter = icon_view_extra.get_model(), icon_view_extra.get_selected_items()
        if model is not None and treeiter is not None and treeiter != []:
            i = model[treeiter][0]
            icon_view_picker = self.builder.get_object('rules_chunk_picker')
            model, treeiter = icon_view_picker.get_model(), icon_view_picker.get_selected_items()
            if model is not None and treeiter is not None and treeiter != []:
                edited_value = model[treeiter][1]
                extra_type = DmaExtraType.FLOOR1
                if i > 15:
                    extra_type = DmaExtraType.WALL_OR_VOID
                if i > 31:
                    extra_type = DmaExtraType.FLOOR2
                self.dma.set_extra(extra_type, i % 16, edited_value)
                self.update_chunks_extra()
                self.mark_as_modified()

    def mark_as_modified(self):
        self.module.mark_as_modified(self.item_id, False)

    def reload_all(self):
        """Reload all image related things"""
        for renderer in self.current_icon_view_renderers:
            renderer.stop()
        self._init_chunk_imgs()
        for renderer in self.current_icon_view_renderers:
            renderer.reset(self.pal_ani_durations, self.chunks_surfaces)
        self._init_rule_icon_views()
        self._init_chunk_picker_icon_view()

    def _init_chunk_imgs(self):
        """(Re)-draw the chunk images"""
        self.chunks_surfaces = []

        # For each chunk...
        for chunk_idx in range(0, len(self.dpc.chunks)):
            # For each frame of palette animation... ( applicable for this chunk )
            pal_ani_frames = []
            self.chunks_surfaces.append(pal_ani_frames)

            chunk_data = self.dpc.chunks[chunk_idx]
            chunk_image = self.dpc.single_chunk_to_pil(chunk_idx, self.dpci, self.dpl.palettes)
            has_pal_ani = any(chunk.pal_idx >= 10 and self.dpla.has_for_palette(chunk.pal_idx - 10) for chunk in chunk_data)
            if not has_pal_ani:
                len_pal_ani = 1
            else:
                ani_pal_lengths = [self.dpla.get_frame_count_for_palette(x) for x in (0, 1) if self.dpla.has_for_palette(x)]
                if len(ani_pal_lengths) < 2:
                    len_pal_ani = ani_pal_lengths[0]
                else:
                    len_pal_ani = lcm(*ani_pal_lengths)

            for pal_ani in range(0, len_pal_ani):
                # We don't have animated tiles, so ani_frames just has one entry.
                ani_frames = []
                pal_ani_frames.append(ani_frames)
                # Switch out the palette with that from the palette animation
                if has_pal_ani:
                    pal_for_frame = itertools.chain.from_iterable(self.dpla.apply_palette_animations(self.dpl.palettes, pal_ani))
                    chunk_image.putpalette(pal_for_frame)
                ani_frames.append(pil_to_cairo_surface(chunk_image.convert('RGBA')))

        # TODO: No DPLA animations at different speeds supported at the moment
        ani_pal11 = 9999
        ani_pal12 = 9999
        if self.dpla.has_for_palette(0):
            ani_pal11 = self.dpla.get_duration_for_palette(0)
        if self.dpla.has_for_palette(1):
            ani_pal12 = self.dpla.get_duration_for_palette(1)

        self.pal_ani_durations = min(ani_pal11, ani_pal12)

    def _init_rule_icon_views(self):
        """Fill the icon views for the three variations and the extra rules."""
        def init_main_rules_store(store):
            for idx in range(0, 9):
                store.append([idx, 0])

        def init_extra_rules_store(store):
            for extra_type in (DmaExtraType.FLOOR1, DmaExtraType.WALL_OR_VOID, DmaExtraType.FLOOR2):
                for idx, val in enumerate(self.dma.get_extra(extra_type)):
                    store.append([idx + 16 * extra_type.value, val])

        for i, v_icon_view_name in enumerate(("rules_main_1", "rules_main_2", "rules_main_3")):
            self._init_an_icon_view(v_icon_view_name, init_main_rules_store, False)

        self._init_an_icon_view("rules_extra", init_extra_rules_store, False)

        self.update_chunks_from_current_rules()

    def _init_chunk_picker_icon_view(self):
        """Fill the icon view for the chunk picker"""
        def init_store(store):
            for idx in range(0, len(self.dpc.chunks)):
                store.append([idx, idx])

        self._init_an_icon_view("rules_chunk_picker", init_store, True, True)

    def _init_an_icon_view(self, name: str, init_store: Callable[[Gtk.ListStore], None], selection_draw_solid, select_first=False):
        icon_view: Gtk.IconView = self.builder.get_object(name)
        if icon_view.get_model() == None:
            #                     id, val
            store = Gtk.ListStore(int, int)
            icon_view.set_model(store)
            renderer = DungeonChunkCellDrawer(icon_view, self.pal_ani_durations, self.chunks_surfaces, selection_draw_solid)
            self.current_icon_view_renderers.append(renderer)
            icon_view.pack_start(renderer, True)
            icon_view.add_attribute(renderer, 'chunkidx', 1)
        else:
            store = icon_view.get_model()
            store.clear()
            self.renderer = None
            for child in icon_view.get_cells():
                if isinstance(child, DungeonChunkCellDrawer):
                    self.renderer = child
                    break

        init_store(store)
        if select_first:
            icon_view.select_path(store.get_path(store.get_iter_first()))

    def _rules_pos_toggle(self, i, state):
        y = i % 3
        x = math.floor(i / 3)
        self.rules[x][y] = state
        self.update_chunks_from_current_rules()

    def _init_rules(self):
        def ia(i):
            return self.builder.get_object(f'rules_a{i}').get_active()
        self.rules = list(chunks([ia(i) for i in range(0, 9)], 3))

    def update_chunks_from_current_rules(self):
        solid_type = self._get_current_solid_type()
        all_chunk_mapping_vars = []
        for y, row in enumerate(self.rules):
            for x, solid in enumerate(row):
                chunk_type = solid_type if solid else DmaType.FLOOR
                solid_neighbors = Dma.get_tile_neighbors(self.rules, x, y, bool(solid))
                all_chunk_mapping_vars.append(self.dma.get(chunk_type, solid_neighbors))

        for i, v_icon_view_name in enumerate(("rules_main_1", "rules_main_2", "rules_main_3")):
            icon_view_model: Gtk.ListStore = self.builder.get_object(v_icon_view_name).get_model()
            icon_view_model.clear()
            for j, idxs in enumerate(all_chunk_mapping_vars):
                icon_view_model.append([j, idxs[i]])

    def update_chunks_extra(self):
        store = self.builder.get_object('rules_extra').get_model()
        store.clear()
        for extra_type in (DmaExtraType.FLOOR1, DmaExtraType.WALL_OR_VOID, DmaExtraType.FLOOR2):
            for idx, val in enumerate(self.dma.get_extra(extra_type)):
                store.append([idx + 16 * extra_type.value, val])

    def _get_current_solid_type(self):
        combo_box: Gtk.ComboBoxText = self.builder.get_object("rules_active_type")
        return DmaType.WALL if combo_box.get_active_text() == 'Wall' else DmaType.WATER

    def _rule_updated(self, i, variation):
        icon_view = self.builder.get_object('rules_chunk_picker')
        model, treeiter = icon_view.get_model(), icon_view.get_selected_items()
        if model is not None and treeiter is not None and treeiter != []:

            x = i % 3
            y = math.floor(i / 3)
            edited_value = model[treeiter][1]
            solid = bool(self.rules[x][y])
            self.dma.set(
                self._get_current_solid_type() if solid else DmaType.FLOOR,
                Dma.get_tile_neighbors(self.rules, x, y, solid),
                variation, edited_value
            )
            self.update_chunks_from_current_rules()
            self.mark_as_modified()

    def _load_dtef_rendering(self):
        self.dtef = ExplorersDtef(self.dma, self.dpc, self.dpci, self.dpl, self.dpla)
        box: Gtk.Box = self.builder.get_object('dungeon_image_placeholder')
        for child in box:
            box.remove(child)
        image: Gtk.Image = Gtk.Image.new_from_surface(pil_to_cairo_surface(self.dtef.get_tiles()[0].convert('RGBA')))
        box.pack_start(image, True, True, 0)
        box.show_all()

    def _init_secondary_terrain(self):
        w: Gtk.ComboBox = self.builder.get_object('secondary_terrain_type')
        s: Gtk.ListStore = w.get_model()
        for v in SecondaryTerrainTableEntry:
            s.append([v.value, v.name.capitalize()])

        secondary_terrain = HardcodedDungeons.get_secondary_terrains(
            self.module.project.get_binary(BinaryName.ARM9), self.module.project.get_rom_module().get_static_data()
        )[self.item_id]
        w.set_active(secondary_terrain.value)
Exemplo n.º 4
0
class ColvecController(AbstractController):
    def __init__(self, module: 'DungeonGraphicsModule', item: str):
        self.module = module
        self._string_provider = module.project.get_string_provider()
        self.filename = item
        self.colvec: Colvec = self.module.get_colvec()

        self.dma = None
        self.dpl = None
        self.dpla = None
        self.dpc = None
        self.dpci = None

        self.builder = None

    def get_view(self) -> Gtk.Widget:
        self.builder = self._get_builder(__file__, 'colvec.glade')
        self._init_colvec()
        self._reinit_image()
        self.builder.connect_signals(self)
        self.builder.get_object('draw_tileset').connect(
            'draw', self.draw_tileset)
        self.builder.get_object('draw_colormap').connect(
            'draw', self.draw_colormap)
        return self.builder.get_object('editor')

    def on_export_clicked(self, *args):
        cb_store: Gtk.ListStore = self.builder.get_object('cb_weather_store')
        cb: Gtk.ComboBoxText = self.builder.get_object('cb_weather')
        v: int = cb_store[cb.get_active_iter()][0]

        dialog = Gtk.FileChooserNative.new("Export current colormap as PNG...",
                                           MainController.window(),
                                           Gtk.FileChooserAction.SAVE, "_Save",
                                           None)

        add_dialog_png_filter(dialog)

        response = dialog.run()
        fn = dialog.get_filename()
        dialog.destroy()

        if response == Gtk.ResponseType.ACCEPT:
            if '.' not in fn:
                fn += '.png'
            self.colvec.to_pil(v).save(fn)

    def on_import_clicked(self, *args):
        cb_store: Gtk.ListStore = self.builder.get_object('cb_weather_store')
        cb: Gtk.ComboBoxText = self.builder.get_object('cb_weather')
        v: int = cb_store[cb.get_active_iter()][0]

        dialog = Gtk.FileChooserNative.new(
            "Import current colormap from PNG...", MainController.window(),
            Gtk.FileChooserAction.OPEN, None, None)

        response = dialog.run()
        fn = dialog.get_filename()
        dialog.destroy()

        if response == Gtk.ResponseType.ACCEPT:
            try:
                img = Image.open(fn, 'r')
                self.colvec.from_pil(v, img)
                self.module.mark_colvec_as_modified()
            except Exception as err:
                display_error(sys.exc_info(), str(err),
                              "Error importing colormap.")
            self._reinit_image()

    def _load_tileset(self, v):
        self.dma: Dma = self.module.get_dma(v)
        self.dpl: Dpl = self.module.get_dpl(v)
        self.dpla: Dpla = self.module.get_dpla(v)
        self.dpc: Dpc = self.module.get_dpc(v)
        self.dpci: Dpci = self.module.get_dpci(v)

    def on_colvec_info_clicked(self, *args):
        md = SkyTempleMessageDialog(
            MainController.window(), Gtk.DialogFlags.DESTROY_WITH_PARENT,
            Gtk.MessageType.INFO, Gtk.ButtonsType.OK,
            f"This colormap defines color palette transformations used by the game during dungeons (for weather effects).\n"
            f"Each color color component (RGB) is changed to the corresponding color component of the n-th color map entry (where n is the old color component value).\n"
            f"This transformation also applies to monsters and items sprites.")
        md.run()
        md.destroy()

    def on_tileset_changed(self, widget):
        cb_store: Gtk.ListStore = self.builder.get_object('cb_tileset_store')
        cb: Gtk.ComboBoxText = self.builder.get_object('cb_tileset')
        v: int = cb_store[cb.get_active_iter()][0]
        self._load_tileset(v)
        self._reinit_image()

    def on_weather_changed(self, widget):
        self._reinit_image()

    def _init_colvec(self):
        # Init available weathers
        cb_store: Gtk.ListStore = self.builder.get_object('cb_weather_store')
        cb: Gtk.ComboBoxText = self.builder.get_object('cb_weather')
        self._fill_available_weathers_into_store(cb_store)
        cb.set_active(0)

        # Init available tilesets
        cb_store: Gtk.ListStore = self.builder.get_object('cb_tileset_store')
        cb: Gtk.ComboBoxText = self.builder.get_object('cb_tileset')
        self._fill_available_tilesets_into_store(cb_store)
        self._load_tileset(0)
        cb.set_active(0)

    def _reinit_image(self):
        cb_store: Gtk.ListStore = self.builder.get_object('cb_weather_store')
        cb: Gtk.ComboBoxText = self.builder.get_object('cb_weather')
        v: int = cb_store[cb.get_active_iter()][0]
        surface = self.colvec.to_pil(v)
        surface = surface.resize((surface.width * 16, surface.height * 16),
                                 resample=Image.NEAREST)
        self.colormap = pil_to_cairo_surface(surface.convert('RGBA'))

        self.surface = None
        if self.dma:
            self.dtef = ExplorersDtef(self.dma, self.dpc, self.dpci, self.dpl,
                                      self.dpla)
            surface = self.dtef.get_tiles()[0]

            #Apply colormap
            surface.putpalette(
                self.colvec.apply_colormap(v, list(surface.palette.palette)))
            self.surface = pil_to_cairo_surface(surface.convert('RGBA'))

        self.builder.get_object('draw_tileset').queue_draw()
        self.builder.get_object('draw_colormap').queue_draw()

    def _fill_available_tilesets_into_store(self, cb_store):
        # Init combobox
        cb_store.clear()
        for v in range(self.module.nb_tilesets()):
            cb_store.append([v, f"Tileset {v}"])

    def _fill_available_weathers_into_store(self, cb_store):
        # Init combobox
        cb_store.clear()
        for v in range(self.colvec.nb_colormaps()):
            cb_store.append([
                v,
                self._string_provider.get_value(StringType.WEATHER_NAMES, v)
            ])

    def draw_tileset(self, wdg, ctx: cairo.Context, *args):
        if self.surface:
            wdg.set_size_request(self.surface.get_width(),
                                 self.surface.get_height())
            ctx.fill()
            ctx.set_source_surface(self.surface, 0, 0)
            ctx.get_source().set_filter(cairo.Filter.NEAREST)
            ctx.paint()
        return True

    def draw_colormap(self, wdg, ctx: cairo.Context, *args):
        if self.colormap:
            wdg.set_size_request(self.colormap.get_width(),
                                 self.colormap.get_height())
            ctx.fill()
            ctx.set_source_surface(self.colormap, 0, 0)
            ctx.get_source().set_filter(cairo.Filter.NEAREST)
            ctx.paint()
        return True