def on_men_tiles_ani_settings_activate(self): dialog: Gtk.Dialog = self.parent.builder.get_object( 'dialog_tiles_animated_settings') dialog.set_attached_to(MainController.window()) dialog.set_transient_for(MainController.window()) bpas_frame_info_entries = [] for i, bpa in enumerate(self.parent.bpas): gui_i = i + 1 enabled = self.parent.builder.get_object(f'bpa_enable{gui_i}') # If there's no second layer: Disable activating the BPA # TODO: Currently when removing layers, BPAs are not removed. Is that a problem? if self.parent.bma.number_of_layers <= 1 and i > 3: enabled.set_sensitive(False) enabled.set_active(bpa is not None) dialog.resize(470, 450) this_frame_info_entries = [] bpas_frame_info_entries.append(this_frame_info_entries) bpa_duration_box: Gtk.Box = self.parent.builder.get_object( f'bpa_box{gui_i}') for child in bpa_duration_box: bpa_duration_box.remove(child) if bpa is None or len(bpa.frame_info) < 1: l = Gtk.Label.new( "This BPA has no frames.\n" "Enable the BPA and import images for a BPA to add frames." ) l.show() bpa_duration_box.add(l) else: # Fill value for existing BPAs for frame_info in bpa.frame_info: entry: Gtk.Entry = Gtk.Entry.new() entry.set_input_purpose(Gtk.InputPurpose.NUMBER) entry.set_width_chars(4) entry.set_max_length(4) entry.set_text(str(frame_info.duration_per_frame)) entry.set_halign(Gtk.Align.START) entry.show() this_frame_info_entries.append(entry) bpa_duration_box.pack_start(entry, False, True, 5) resp = dialog.run() dialog.hide() if resp == ResponseType.OK: had_errors = False for i, (bpa, bpa_entries) in enumerate( zip(self.parent.bpas, bpas_frame_info_entries)): gui_i = i + 1 if bpa is None and self.parent.builder.get_object( f'bpa_enable{gui_i}').get_active(): # HAS TO BE ADDED map_bg_entry = self.parent.module.get_level_entry( self.parent.item_id) # Add file new_bpa_filename = f"{map_bg_entry.bpl_name}{gui_i}" new_bpa_filename_with_ext = new_bpa_filename.lower( ) + BPA_EXT try: self.parent.module.project.create_new_file( f"{DIR}/{new_bpa_filename_with_ext}", FileType.BPA.new(), FileType.BPA) except FileExistsError: # Hm, okay, then we just re-use this file. pass # Add to MapBG list map_bg_entry.bpa_names[i] = new_bpa_filename # Refresh controller state self.parent.bpas = self.parent.module.get_bpas( self.parent.item_id) new_bpa = self.parent.bpas[i] # Add to BPC self.parent.bpc.process_bpa_change(i, new_bpa.number_of_tiles) self.parent.module.mark_level_list_as_modified() if bpa is not None and not self.parent.builder.get_object( f'bpa_enable{gui_i}').get_active(): # HAS TO BE DELETED map_bg_entry = self.parent.module.get_level_entry( self.parent.item_id) # Delete from BPC self.parent.bpc.process_bpa_change(i, 0) # Delete from MapBG list map_bg_entry.bpa_names[i] = None # Refresh controller state self.parent.bpas = self.parent.module.get_bpas( self.parent.item_id) self.parent.module.mark_level_list_as_modified() if bpa is not None: new_frame_info = [] for entry_i, entry in enumerate(bpa_entries): try: number_of_frames = int(entry.get_text()) except ValueError: number_of_frames = 0 had_errors = True new_frame_info.append(BpaFrameInfo( number_of_frames, 0)) bpa.frame_info = new_frame_info if had_errors: md = SkyTempleMessageDialog( MainController.window(), Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.WARNING, Gtk.ButtonsType.OK, "Some values were invalid (not a number). " "They were replaced with 0.", title="Warning!") md.set_position(Gtk.WindowPosition.CENTER) md.run() md.destroy() self.parent.reload_all() self.parent.mark_as_modified()
try: gi.require_foreign("cairo") except ImportError: from gi.repository import Gtk md = SkyTempleMessageDialog( None, Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.ERROR, Gtk.ButtonsType.OK, _("PyGObject compiled without Cairo support. Can't start!"), title=_("SkyTemple - Error!")) md.set_position(Gtk.WindowPosition.CENTER) md.run() md.destroy() exit(1) from gi.repository import Gtk, Gdk, GLib from gi.repository.Gtk import Window from skytemple.controller.main import MainController SKYTEMPLE_LOGLEVEL = logging.INFO def main(): # TODO: Gtk.Application: https://python-gtk-3-tutorial.readthedocs.io/en/latest/application.html path = os.path.abspath(os.path.dirname(__file__)) # Load settings settings = SkyTempleSettingsStore()
def on_men_map_width_height_activate(self): dialog: Gtk.Dialog = self.parent.builder.get_object( 'dialog_width_height') dialog.set_attached_to(MainController.window()) dialog.set_transient_for(MainController.window()) # Set dialog settings to map settings map_width_chunks: Gtk.Entry = self.parent.builder.get_object( 'map_width_chunks') map_height_chunks: Gtk.Entry = self.parent.builder.get_object( 'map_height_chunks') map_width_tiles: Gtk.Entry = self.parent.builder.get_object( 'map_width_tiles') map_height_tiles: Gtk.Entry = self.parent.builder.get_object( 'map_height_tiles') map_wh_link: Gtk.Switch = self.parent.builder.get_object('map_wh_link') map_wh_link_target: Gtk.Switch = self.parent.builder.get_object( 'map_wh_link_target') map_width_chunks.set_text(str(self.parent.bma.map_width_chunks)) map_height_chunks.set_text(str(self.parent.bma.map_height_chunks)) map_width_tiles.set_text(str(self.parent.bma.map_width_camera)) map_height_tiles.set_text(str(self.parent.bma.map_height_camera)) if self.parent.bma.map_width_camera == self.parent.bma.map_width_chunks * self.parent.bma.tiling_width and \ self.parent.bma.map_height_camera == self.parent.bma.map_height_chunks * self.parent.bma.tiling_height: map_wh_link.set_active(True) map_wh_link_target.set_sensitive(False) else: map_wh_link.set_active(False) map_wh_link_target.set_sensitive(True) map_width_chunks.connect( 'changed', partial(on_map_width_chunks_changed, self.parent.builder)) map_height_chunks.connect( 'changed', partial(on_map_height_chunks_changed, self.parent.builder)) map_wh_link.connect( 'state-set', partial(on_map_wh_link_state_set, self.parent.builder)) resp = dialog.run() if resp == ResponseType.OK: try: params = ( int(map_width_chunks.get_text()), int(map_height_chunks.get_text()), int(map_width_tiles.get_text()), int(map_height_tiles.get_text()), ) except ValueError: md = SkyTempleMessageDialog( MainController.window(), Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.ERROR, Gtk.ButtonsType.OK, "Please only enter numbers for the map size.", title="Error!") md.set_position(Gtk.WindowPosition.CENTER) md.run() md.destroy() else: self.parent.bma.resize(*params) self.parent.reload_all() self.parent.mark_as_modified() dialog.hide()
def on_import_clicked(self, *args): md = SkyTempleMessageDialog( MainController.window(), Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, f"To import, select a directory to import from. Files with the pattern 'XX.png'\n" f"will be imported, where XX is a number between 0 and 99. The image dimensions must be a multiple of 8.", title="Import Images") md.run() md.destroy() dialog = Gtk.FileChooserNative.new("Import images from PNGs...", MainController.window(), Gtk.FileChooserAction.SELECT_FOLDER, None, None) response = dialog.run() fn = dialog.get_filename() dialog.destroy() if response == Gtk.ResponseType.ACCEPT: r = re.compile(rf"(\d+)\.png", re.IGNORECASE) imgs_dict = { int(match[1]): name for match, name in self._try_match_import(r, os.listdir(fn)) if match is not None } imgs = [] # Convert imgs to list for i in range(0, len(imgs_dict)): if i not in imgs_dict: display_error( None, f'Failed importing image "{i}":\nImage for number {i} missing.', f"Image for number {i} missing.") return imgs.append(imgs_dict[i]) if len(imgs) == 0: display_error(None, f'No images found.', f"No images found.") return for index, image_fn in enumerate(imgs): try: with open(os.path.join(fn, image_fn), 'rb') as f: image = Image.open(f) if len(self.w16) > index: # Existing image, update self.w16[index].set(image) else: self.w16.append( W16AtImage.new(W16TocEntry(0, 0, 0, 0), image)) except Exception as err: logger.error(f"Failed importing image '{index}'.", exc_info=err) display_error(sys.exc_info(), f'Failed importing image "{index}":\n{err}', f"Error for '{index}'.") # Re-render self._reset() for draw in self._draws: draw.queue_draw() # Mark as modified self.module.mark_w16_as_modified(self.item_id)
def on_fix_dungeons_clicked(self, *args): dialog: Gtk.Dialog = self.builder.get_object( 'dialog_fix_dungeon_errors') dialog.set_attached_to(SkyTempleMainController.window()) dialog.set_transient_for(SkyTempleMainController.window()) dialog.resize(900, 520) dungeon_list = self.module.get_dungeon_list() validator = self.module.get_validator() validator.validate(dungeon_list) store: Gtk.Store = self.builder.get_object('store_dungeon_errors') store.clear() validator.errors.sort(key=lambda e: e.dungeon_id) for e in validator.errors: if not isinstance(e, DungeonTotalFloorCountInvalidError): if isinstance(e, FloorReusedError): i = e.reused_of_dungeon_with_id e.reused_of_dungeon_name = f'{"dungeon"} {i} ({self.module.project.get_string_provider().get_value(StringType.DUNGEON_NAMES_MAIN, i)})' dungeon_name = f'{e.dungeon_id}: {self.module.project.get_string_provider().get_value(StringType.DUNGEON_NAMES_MAIN, e.dungeon_id)}' store.append([ True, # selected dungeon_name, # dungeon_name e.dungeon_id, # dungeon_id textwrap.fill(e.name, 40), # error_name textwrap.fill(str(e), 40), # error_description textwrap.fill(self._get_solution_text(dungeon_list, e), 40), # solution e, # error ]) resp = dialog.run() dialog.hide() if resp == Gtk.ResponseType.APPLY: # Step 1, fix all selected errors for row in store: if row[0]: # selected self._fix_error(dungeon_list, row[6]) # Step 2, fix all open DungeonTotalFloorCountInvalidError validator.validate(dungeon_list) for error in validator.errors: if isinstance(error, DungeonTotalFloorCountInvalidError): self._fix_error(dungeon_list, error) # Step 3 report status if not validator.validate(dungeon_list): md = SkyTempleMessageDialog( MainSkyTempleController.window(), Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.WARNING, Gtk.ButtonsType.OK, _("Dungeon Errors were fixed.\nHowever there are still errors left. " "Re-open the dialog to fix the rest. If they are still not fixed, please report a bug!" ), title=_("Fix Dungeon Errors")) else: md = SkyTempleMessageDialog( MainSkyTempleController.window(), Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, _("Dungeon Errors were successfully fixed."), title=_("Fix Dungeon Errors"), is_success=True) md.run() md.destroy() self.module.save_dungeon_list(dungeon_list) self.module.save_mappa() self.module.mark_root_as_modified() self.module.rebuild_dungeon_tree()
def on_btn_export_clicked(self, *args): dialog: Gtk.Dialog = self.builder.get_object('export_dialog') dialog.resize(640, 560) dialog.set_attached_to(SkyTempleMainController.window()) dialog.set_transient_for(SkyTempleMainController.window()) # Fill Pokémon tree store: Gtk.TreeStore = self.builder.get_object('export_dialog_store') store.clear() monster_entries_by_base_id: Dict[int, List[MdEntry]] = {} for entry in self.module.monster_md.entries: if entry.md_index_base not in monster_entries_by_base_id: monster_entries_by_base_id[entry.md_index_base] = [] monster_entries_by_base_id[entry.md_index_base].append(entry) for baseid, entry_list in monster_entries_by_base_id.items(): name = self.module.project.get_string_provider().get_value( StringType.POKEMON_NAMES, baseid) entry_main_tree = self.module.generate_entry__entity_root( baseid, name) ent_root = store.append(None, [ -1, -1, False, entry_main_tree[0], entry_main_tree[1], False, False ]) for entry in entry_list: entry_main_tree = self.module.generate_entry__entry( entry.md_index, entry.gender) store.append(ent_root, [ entry_main_tree[4], -1, True, entry_main_tree[0], entry_main_tree[1], False, False ]) names, md_gender1, md_gender2, moveset, moveset2, stats, portraits, portraits2 = self.module.get_export_data( self.entry) we_are_gender1 = md_gender1 == self.entry if md_gender2 is None: sw: Gtk.Switch = self.builder.get_object( 'export_type_other_gender') sw.set_active(False) sw.set_sensitive(False) if portraits is None: sw: Gtk.Switch = self.builder.get_object( 'export_type_portraits_current_gender') sw.set_active(False) sw.set_sensitive(False) if portraits2 is None: sw: Gtk.Switch = self.builder.get_object( 'export_type_portraits_other_gender') sw.set_active(False) sw.set_sensitive(False) if stats is None: sw: Gtk.Switch = self.builder.get_object('export_type_stats') sw.set_active(False) sw.set_sensitive(False) if moveset is None: sw: Gtk.Switch = self.builder.get_object('export_type_moveset1') sw.set_active(False) sw.set_sensitive(False) if moveset2 is None: sw: Gtk.Switch = self.builder.get_object('export_type_moveset2') sw.set_active(False) sw.set_sensitive(False) resp = dialog.run() dialog.hide() if resp == Gtk.ResponseType.APPLY: # Create output XML if not self.builder.get_object( 'export_type_current_gender').get_active(): if we_are_gender1: md_gender1 = None else: md_gender2 = None if not self.builder.get_object( 'export_type_other_gender').get_active(): if not we_are_gender1: md_gender1 = None else: md_gender2 = None if not self.builder.get_object('export_type_names').get_active(): names = None if not self.builder.get_object('export_type_stats').get_active(): stats = None if not self.builder.get_object( 'export_type_moveset1').get_active(): moveset = None if not self.builder.get_object( 'export_type_moveset2').get_active(): moveset2 = None if not self.builder.get_object( 'export_type_portraits_current_gender').get_active(): if we_are_gender1: portraits = None else: portraits2 = None if not self.builder.get_object( 'export_type_portraits_other_gender').get_active(): if not we_are_gender1: portraits = None else: portraits2 = None xml = monster_xml_export( self.module.project.get_rom_module().get_static_data( ).game_version, md_gender1, md_gender2, names, moveset, moveset2, stats, portraits, portraits2) # 1. Export to file if self.builder.get_object('export_file_switch').get_active(): save_diag = Gtk.FileChooserNative.new( "Export Pokémon as...", SkyTempleMainController.window(), Gtk.FileChooserAction.SAVE, None, None) add_dialog_xml_filter(save_diag) response = save_diag.run() fn = save_diag.get_filename() if '.' not in fn: fn += '.xml' save_diag.destroy() if response == Gtk.ResponseType.ACCEPT: with open(fn, 'w') as f: f.write(prettify(xml)) else: md = SkyTempleMessageDialog( SkyTempleMainController.window(), Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.WARNING, Gtk.ButtonsType.OK, "Export was canceled.") md.set_position(Gtk.WindowPosition.CENTER) md.run() md.destroy() return # 2. Import to selected Pokémon selected_monsters: List[int] = [] def collect_monsters_recurse(titer: Optional[Gtk.TreeIter]): for i in range(store.iter_n_children(titer)): child = store.iter_nth_child(titer, i) if store[child][2] and store[child][ 5]: # is floor and is selected selected_monsters.append(store[child][0]) collect_monsters_recurse(child) collect_monsters_recurse(None) self.module.import_from_xml(selected_monsters, xml)
def run(self): """ Shows the settings dialog and processes settings changes. Doesn't return anything. """ gtk_settings = Gtk.Settings.get_default() # Discord enabled state discord_enabled_previous = self.settings.get_integration_discord_enabled( ) settings_discord_enable = self.builder.get_object( 'setting_discord_enable') settings_discord_enable.set_active(discord_enabled_previous) # Gtk Theme if not sys.platform.startswith('linux'): store: Gtk.ListStore = Gtk.ListStore.new([str]) cb: Gtk.ComboBox = self.builder.get_object('setting_gtk_theme') active = None for id, theme in enumerate(self._list_gtk_themes()): store.append([theme]) if theme == gtk_settings.get_property("gtk-theme-name"): active = id cb.set_model(store) if active is not None: cb.set_active(active) else: self.builder.get_object('frame_setting_gtk_theme').hide() # Languages cb: Gtk.ComboBox = self.builder.get_object('setting_language') store: Gtk.ListStore = self.builder.get_object('lang_store') store.clear() active = None for id, (code, name) in enumerate(LANGS): store.append([code, name]) if code == self.settings.get_locale(): active = id if active is not None: cb.set_active(active) response = self.window.run() have_to_restart = False if response == Gtk.ResponseType.ACCEPT: # Discord enabled state discord_enabled = settings_discord_enable.get_active() if discord_enabled != discord_enabled_previous: self.settings.set_integration_discord_enabled(discord_enabled) have_to_restart = True # Gtk Theme if not sys.platform.startswith('linux'): cb: Gtk.ComboBox = self.builder.get_object('setting_gtk_theme') theme_name = cb.get_model()[cb.get_active_iter()][0] gtk_settings.set_property("gtk-theme-name", theme_name) self.settings.set_gtk_theme(theme_name) # Languages cb: Gtk.ComboBox = self.builder.get_object('setting_language') lang_name = cb.get_model()[cb.get_active_iter()][0] before = self.settings.get_locale() if before != lang_name: self.settings.set_locale(lang_name) have_to_restart = True self.window.hide() if have_to_restart: md = SkyTempleMessageDialog(self.parent_window, Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, _("You need to restart SkyTemple to " "apply some of the settings."), title="SkyTemple") md.run() md.destroy()
def convert(self, *args): mode_cb: Gtk.ComboBox = self.builder.get_object('tq_mode') mode = ImageConversionMode( mode_cb.get_model()[mode_cb.get_active_iter()][0]) dither_level = self.builder.get_object('tq_dither_level').get_value() has_first_image = self.builder.get_object( 'tq_input_file').get_filename() is not None has_second_image = self.builder.get_object( 'tq_second_file').get_filename() is not None if not has_first_image: self.error(_("Please select an input image.")) return if has_second_image: md = SkyTempleMessageDialog( self.window, Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, _("Since you selected two images to convert, you will be " "asked for both images where to save them to.")) md.run() md.destroy() dialog = Gtk.FileChooserNative.new(_("Save first image as (PNG)..."), self.window, Gtk.FileChooserAction.SAVE, None, None) if self._previous_output_image is not None: dialog.set_filename(self._previous_output_image) add_dialog_png_filter(dialog) response = dialog.run() output_image = dialog.get_filename() if output_image and '.' not in output_image: output_image += '.png' self._previous_output_image = output_image dialog.destroy() if response != Gtk.ResponseType.ACCEPT: return if has_second_image: dialog = Gtk.FileChooserNative.new( _("Save second image as (PNG)..."), self.window, Gtk.FileChooserAction.SAVE, None, None) if self._previous_second_output_image is not None: dialog.set_filename(self._previous_second_output_image) else: dialog.set_filename(output_image) add_dialog_png_filter(dialog) response = dialog.run() second_output_image = dialog.get_filename() if '.' not in second_output_image: second_output_image += '.png' self._previous_second_output_image = second_output_image dialog.destroy() if response != Gtk.ResponseType.ACCEPT: return try: num_pals = int( self.builder.get_object('tq_number_palettes').get_text()) input_image = self.builder.get_object( 'tq_input_file').get_filename() second_input_file = self.builder.get_object( 'tq_second_file').get_filename() transparent_color = self.builder.get_object( 'tq_transparent_color').get_color() transparent_color = (int(transparent_color.red_float * 255), int(transparent_color.green_float * 255), int(transparent_color.blue_float * 255)) except ValueError: self.error(_("You entered invalid numbers.")) else: if not os.path.exists(input_image): self.error(_("The input image does not exist.")) return if has_second_image and not os.path.exists(second_input_file): self.error(_("The second input image does not exist.")) return with open(input_image, 'rb') as input_file: try: if not has_second_image: # Only one image image = Image.open(input_file) else: # Two images: Merge them. image1 = Image.open(input_file) image2 = Image.open(second_input_file) image = Image.new('RGBA', (max(image1.width, image2.width), image1.height + image2.height), transparent_color) image.paste(image1, (0, 0)) image.paste(image2, (0, image1.height)) except OSError: self.error(_("The input image is not a supported format.")) return try: img = self._convert(image, transparent_color, mode, num_pals, dither_level) if not has_second_image: # Only one image img.save(output_image) else: # Two images: Un-merge them. img.crop((0, 0, image1.width, image1.height)).save(output_image) img.crop( (0, image1.height, image2.width, image1.height + image2.height)).save(second_output_image) except BaseException as err: logger.error("Tilequant error.", exc_info=err) self.error(str(err)) else: md = SkyTempleMessageDialog( self.window, Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, _("Image was converted."), is_success=True) md.run() md.destroy()
def run(self): """ Shows the settings dialog and processes settings changes. Doesn't return anything. """ gtk_settings = Gtk.Settings.get_default() # Discord enabled state discord_enabled_previous = self.settings.get_integration_discord_enabled( ) settings_discord_enable = self.builder.get_object( 'setting_discord_enable') settings_discord_enable.set_active(discord_enabled_previous) # Gtk Theme if not sys.platform.startswith('linux'): store: Gtk.ListStore = Gtk.ListStore.new([str]) cb: Gtk.ComboBox = self.builder.get_object('setting_gtk_theme') active = None for id, theme in enumerate(self._list_gtk_themes()): store.append([theme]) if theme == gtk_settings.get_property("gtk-theme-name"): active = id cb.set_model(store) if active is not None: cb.set_active(active) else: gbox: Gtk.Box = self.builder.get_object('setting_gtk_theme_box') for child in gbox: gbox.remove(child) label = Gtk.Label() label.set_markup( _("<i>Use System Settings to set this under Linux.</i>")) gbox.pack_start(label, False, False, 0) label.show() # Languages cb: Gtk.ComboBox = self.builder.get_object('setting_language') store: Gtk.ListStore = self.builder.get_object('lang_store') store.clear() active = None for id, (code, name) in enumerate(LANGS): store.append([code, name]) if code == self.settings.get_locale(): active = id if active is not None: cb.set_active(active) # Native file handler native_impl_enabled_previous = self.settings.get_implementation_type( ) == ImplementationType.NATIVE settings_native_enable = self.builder.get_object( 'setting_native_enable') settings_native_enable.set_active(native_impl_enabled_previous) # Async modes cb = self.builder.get_object('setting_async') store = self.builder.get_object('async_store') store.clear() active = None for mode in AsyncConfiguration: if mode.available(): store.append([mode.value, mode.name_localized]) if mode == self.settings.get_async_configuration(): active = mode.value if active is not None: cb.set_active_id(active) # Sentry allow_sentry_previous = self.settings.get_allow_sentry() settings_allow_sentry_enable = self.builder.get_object( 'setting_allow_sentry') settings_allow_sentry_enable.set_active(allow_sentry_previous) # CSD csd_before = self.settings.csd_enabled() settings_csd_enable = self.builder.get_object('setting_csd_enable') settings_csd_enable.set_active(csd_before) response = self.window.run() have_to_restart = False if response == Gtk.ResponseType.ACCEPT: # Discord enabled state discord_enabled = settings_discord_enable.get_active() if discord_enabled != discord_enabled_previous: self.settings.set_integration_discord_enabled(discord_enabled) have_to_restart = True # Gtk Theme if not sys.platform.startswith('linux'): cb = self.builder.get_object('setting_gtk_theme') iter = cb.get_active_iter() if iter is not None: theme_name = cb.get_model()[iter][0] gtk_settings.set_property("gtk-theme-name", theme_name) self.settings.set_gtk_theme(theme_name) # Languages cb = self.builder.get_object('setting_language') lang_name = cb.get_model()[cb.get_active_iter()][0] before = self.settings.get_locale() if before != lang_name: self.settings.set_locale(lang_name) have_to_restart = True # Native file handler enabled state native_impl_enabled = settings_native_enable.get_active() if native_impl_enabled != native_impl_enabled_previous: self.settings.set_implementation_type( ImplementationType.NATIVE if native_impl_enabled else ImplementationType.PYTHON) have_to_restart = True # Async modes cb = self.builder.get_object('setting_async') async_mode = AsyncConfiguration( cb.get_model()[cb.get_active_iter()][0]) before_async = self.settings.get_async_configuration() if before_async != async_mode: self.settings.set_async_configuration(async_mode) have_to_restart = True # Sentry allow_sentry_enabled = settings_allow_sentry_enable.get_active() if allow_sentry_enabled != allow_sentry_previous: self.settings.set_allow_sentry(allow_sentry_enabled) if not allow_sentry_enabled: have_to_restart = True else: from skytemple.core import sentry sentry.init() # CSD csd_new = settings_csd_enable.get_active() before_csd = self.settings.csd_enabled() if before_csd != csd_new: self.settings.set_csd_enabled(csd_new) have_to_restart = True self.window.hide() if have_to_restart: md = SkyTempleMessageDialog(self.parent_window, Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, _("You need to restart SkyTemple to " "apply some of the settings."), title="SkyTemple") md.run() md.destroy()
def on_btn_apply_clicked(self, *args): tree: Gtk.TreeView = self.builder.get_object('patch_tree') model, treeiter = tree.get_selection().get_selected() if model is not None and treeiter is not None: name = model[treeiter][0] try: if self._patcher.is_applied(name): md = SkyTempleMessageDialog(MainAppController.window(), Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.WARNING, Gtk.ButtonsType.OK_CANCEL, _("This patch is already applied. " "Some patches support applying them again, " "but you might also run into problems with some. " "Proceed with care.")) md.set_position(Gtk.WindowPosition.CENTER) response = md.run() md.destroy() if response != Gtk.ResponseType.OK: return except NotImplementedError: self._error(_("The current ROM is not supported by this patch.")) return if self.module.project.has_modifications(): self._error(_("Please save the ROM before applying the patch.")) return if name == 'ExpandPokeList': md = SkyTempleMessageDialog(MainAppController.window(), Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.WARNING, Gtk.ButtonsType.YES_NO, _("This patch extends the Pokémon list. It is very experimental and " "WILL break a few things. Once applied you can not remove it again. " "Proceed?")) md.set_position(Gtk.WindowPosition.CENTER) response = md.run() md.destroy() if response != Gtk.ResponseType.YES: return some_skipped = False try: dependencies = self._get_dependencies(name) if len(dependencies) > 0: md = SkyTempleMessageDialog(MainAppController.window(), Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.INFO, Gtk.ButtonsType.YES_NO, _("This patch requires some other patches to be applied first:\n") + '\n'.join(dependencies) + _("\nDo you want to apply these first?")) md.set_position(Gtk.WindowPosition.CENTER) response = md.run() md.destroy() if response != Gtk.ResponseType.YES: return for patch in dependencies + [name]: try: self._apply(patch) except PatchCanceledError: some_skipped = True break except PatchNotConfiguredError as ex: err = str(ex) if ex.config_parameter != "*": err += _('\nConfiguration field with errors: "{}"\nError: {}').format(ex.config_parameter, ex.error) self._error(f(_("Error applying the patch:\n{err}")), exc_info=sys.exc_info(), should_report=True) except RuntimeError as err: self._error(f(_("Error applying the patch:\n{err}")), exc_info=sys.exc_info(), should_report=True) else: if not some_skipped: self._error(_("Patch was successfully applied. The ROM will now be reloaded."), Gtk.MessageType.INFO, is_success=True) else: self._error(_("Not all patches were applied successfully. The ROM will now be reloaded."), Gtk.MessageType.INFO) finally: self.module.mark_asm_patches_as_modified() self.refresh(self._current_tab) MainSkyTempleController.save(lambda: MainSkyTempleController.reload_project())