def warn_update(self, plugins): def _open_update(is_checked): if is_checked: self.config['auto_update'] = True get_action('plugins').activate() page = self.notebook.page_num(self._ui.available_plugins_box) self.notebook.set_current_page(page) if plugins: plugins_str = '\n' + '\n'.join(plugins) NewConfirmationCheckDialog( _('Plugin Updates'), _('Plugin Updates Available'), _('There are updates for your plugins:\n' '<b>%s</b>') % plugins_str, _('Update plugins automatically next time'), [ DialogButton.make('Cancel'), DialogButton.make('Accept', text=_('_Update'), is_default=True, callback=_open_update) ]).show() else: log.info('No updates found') if hasattr(self, 'thread'): del self.thread
def _on_enable_switch(self, switch, state, account): def _disable(): app.connections[account].change_status('offline', 'offline') app.connections[account].disconnect(reconnect=False) app.interface.disable_account(account) switch.set_state(state) old_state = app.config.get_per('accounts', account, 'active') if old_state == state: return Gdk.EVENT_PROPAGATE if (account in app.connections and app.connections[account].connected > 0): # Connecting or connected NewConfirmationDialog( _('Disable Account'), _('Account %s is still connected') % account, _('All chat and group chat windows will be closed.'), [ DialogButton.make( 'Cancel', callback=lambda: switch.set_active(True)), DialogButton.make('Remove', text=_('_Disable Account'), callback=_disable) ], transient_for=self.get_toplevel()).show() return Gdk.EVENT_STOP if state: app.interface.enable_account(account) else: app.interface.disable_account(account) return Gdk.EVENT_PROPAGATE
def on_error(self, reason): if reason == 'CERTIFICATE_VERIFY_FAILED': NewConfirmationDialog( _('Security Error'), _('Security error while trying to download'), _('A security error occurred while trying to download. The ' 'certificate of the plugin archive could not be verified. ' 'This might be a security attack. \n\nYou can continue at ' 'your own risk (not recommended).'), [ DialogButton.make('Cancel'), DialogButton.make('Remove', text=_('_Continue'), callback=lambda dlg: self.start_download( secure=False, upgrading=True)) ]).show() else: if self.available_plugins_model: for i in range(len(self.available_plugins_model)): self.available_plugins_model[i][Column.UPGRADE] = False self._ui.spinner.hide() text = GLib.markup_escape_text(reason) WarningDialog( _('Error While Downloading'), _('An error occurred while downloading\n\n' '<tt>[%s]</tt>' % (str(text))), self.window)
def _relog(self, account): if app.connections[account].connected == 0: return if account == app.ZEROCONF_ACC_NAME: app.connections[app.ZEROCONF_ACC_NAME].update_details() return def login(account, show_before, status_before): """ Login with previous status """ # first make sure connection is really closed, # 0.5 may not be enough app.connections[account].disconnect(True) app.interface.roster.send_status(account, show_before, status_before) def relog(account): show_before = app.SHOW_LIST[app.connections[account].connected] status_before = app.connections[account].status app.interface.roster.send_status(account, 'offline', _('Be right back.')) GLib.timeout_add(500, login, account, show_before, status_before) NewConfirmationDialog( _('Re-Login'), _('Re-Login now?'), _('To apply all changes instantly, you have to re-login.'), [ DialogButton.make('Cancel', text=_('_Later')), DialogButton.make('Accept', text=_('_Re-Login'), callback=lambda *args: relog(account)) ], transient_for=self).show()
def _on_plugin_exists(zip_filename): def _on_yes(): plugin = app.plugin_manager.install_from_zip( zip_filename, True) if not plugin: show_warn_dialog() return model = self.installed_plugins_model for _index, row in enumerate(model): if plugin == row[Column.PLUGIN]: model.remove(row.iter) break iter_ = model.append([ plugin, plugin.name, False, plugin.activatable, self.get_plugin_icon(plugin) ]) sel = self.installed_plugins_treeview.get_selection() sel.select_iter(iter_) NewConfirmationDialog( _('Overwrite Plugin?'), _('Plugin already exists'), _('Do you want to overwrite the currently installed version?'), [ DialogButton.make('Cancel'), DialogButton.make( 'Remove', text=_('_Overwrite'), callback=_on_yes) ], transient_for=self.window).show()
def on_history_manager_window_delete_event(self, widget, event): if not self.AT_LEAST_ONE_DELETION_DONE: if is_standalone(): Gtk.main_quit() return def _on_yes(): self.cur.execute('VACUUM') self.con.commit() if is_standalone(): Gtk.main_quit() def _on_no(): if is_standalone(): Gtk.main_quit() NewConfirmationDialog( _('Database Cleanup'), _('Do you want to clean up the database? ' '(STRONGLY NOT RECOMMENDED IF GAJIM IS RUNNING)'), _('Normally, the allocated database size will not be freed, it ' 'will just become reusable. This operation may take a while.'), [ DialogButton.make('Cancel', callback=_on_no), DialogButton.make( 'Remove', text=_('_Cleanup'), callback=_on_yes) ]).show()
def show_request_dialog(self, obj, session): def _on_accept(): session.invited(obj.stanza) def _on_decline(): session.decline_invitation() account = obj.conn.name contact = app.contacts.get_first_contact_from_jid(account, obj.jid) if contact: name = contact.get_shown_name() else: name = obj.jid NewConfirmationDialog( _('Incoming Tictactoe'), _('Incoming Tictactoe Invitation'), _('%(name)s (%(jid)s) wants to play tictactoe with you.') % { 'name': name, 'jid': obj.jid }, [ DialogButton.make( 'Cancel', text=_('_Decline'), callback=_on_decline), DialogButton.make('OK', text=_('_Accept'), callback=_on_accept) ], modal=False, transient_for=app.app.get_active_window()).show()
def show_file_request(self, account, contact, file_props): """ Show dialog asking for comfirmation and store location of new file requested by a contact """ if not file_props or not file_props.name: return sectext = _('File: %s') % GLib.markup_escape_text( file_props.name) if file_props.size: sectext += '\n' + _('Size: %s') % GLib.format_size_full( file_props.size, self.units) if file_props.mime_type: sectext += '\n' + _('Type: %s') % file_props.mime_type if file_props.desc: sectext += '\n' + _('Description: %s') % file_props.desc def _on_ok(): self.on_file_request_accepted(account, contact, file_props) def _on_cancel(): app.connections[account].get_module( 'Bytestream').send_file_rejection(file_props) NewConfirmationDialog( _('File Transfer Request'), _('%s wants to send you a file') % contact.jid, sectext, [DialogButton.make('Cancel', callback=_on_cancel), DialogButton.make('Accept', callback=_on_ok)]).show()
def _on_remove_theme(self, *args): store, iter_ = self._theme_treeview.get_selection().get_selected() if iter_ is None: return theme = store[iter_][Column.THEME] if theme == app.config.get('roster_theme'): ErrorDialog(_('Active Theme'), _('You tried to delete the currently active theme. ' 'Please switch to a different theme first.'), transient_for=self) return def _remove_theme(): app.css_config.remove_theme(theme) store.remove(iter_) first = store.get_iter_first() if first is None: self._remove_theme_button.set_sensitive(False) self._add_option_button.set_sensitive(False) self._clear_options() NewConfirmationDialog( _('Delete'), _('Delete Theme'), _('Do you want to permanently delete this theme?'), [ DialogButton.make('Cancel'), DialogButton.make('Delete', callback=_remove_theme) ], transient_for=self).show()
def _on_accepted(account, contact, file_props, file_path): if os.path.exists(file_path): app.config.set('last_save_dir', os.path.dirname(file_path)) # Check if we have write permissions if not os.access(file_path, os.W_OK): file_name = GLib.markup_escape_text( os.path.basename(file_path)) ErrorDialog( _('Cannot overwrite existing file \'%s\'' % file_name), _('A file with this name already exists and you do ' 'not have permission to overwrite it.')) return stat = os.stat(file_path) dl_size = stat.st_size file_size = file_props.size dl_finished = dl_size >= file_size def _on_resume(): if not dl_finished: file_props.offset = dl_size self._start_receive( file_path, account, contact, file_props) def _on_replace(): self._start_receive( file_path, account, contact, file_props) def _on_cancel(): con.get_module('Bytestream').send_file_rejection( file_props) NewConfirmationDialog( _('File Transfer Conflict'), _('File already exists'), _('Resume download or replace file?'), [DialogButton.make('Cancel', callback=_on_cancel), DialogButton.make('OK', text=_('Resume _Download'), callback=_on_resume), DialogButton.make('Accept', text=_('Replace _File'), callback=_on_replace)]).show() # File does not exist yet dirname = os.path.dirname(file_path) if not os.access(dirname, os.W_OK) and os.name != 'nt': # read-only bit is used to mark special folder under # windows, not to mark that a folder is read-only. # See ticket #3587 ErrorDialog( _('Directory \'%s\' is not writable') % dirname, _('You do not have permissions to create files ' 'in this directory.')) return self._start_receive(file_path, account, contact, file_props)
def _on_not_trusted(event): NewConfirmationCheckDialog( _('Untrusted PGP key'), _('Untrusted PGP key'), _('The PGP key used to encrypt this chat is not ' 'trusted. Do you really want to encrypt this ' 'message?'), _('_Do not ask me again'), [ DialogButton.make( 'Cancel', text=_('_No'), callback=event.on_no), DialogButton.make( 'OK', text=_('_Encrypt Anyway'), callback=event.on_yes) ]).show()
def on_remove_button_clicked(self, _widget): def remove(): if self.account in app.connections and \ app.connections[self.account].connected and \ not self._ui.remove_and_unregister_radiobutton.get_active(): # change status to offline only if we will not # remove this JID from server app.connections[self.account].change_status( 'offline', 'offline') if self._ui.remove_and_unregister_radiobutton.get_active(): if not self.account in app.connections: ErrorDialog( _('Account is disabled'), _('To unregister from a server, the account must be ' 'enabled.'), transient_for=self._ui.remove_account_window) return if not app.connections[self.account].password: def on_ok(passphrase, _checked): if passphrase == -1: # We don't remove account cause we # canceled pw window return app.connections[self.account].password = passphrase app.connections[self.account].unregister_account( self._on_remove_success) PassphraseDialog( _('Password required'), _('Enter your password for account %s') % self.account, _('Save password'), ok_handler=on_ok, transient_for=self._ui.remove_account_window) return app.connections[self.account].unregister_account( self._on_remove_success) else: self._on_remove_success(True) if self.account in app.connections and \ app.connections[self.account].connected: NewConfirmationDialog( _('Remove Account'), _('Account \'%s\' is still connected to the ' 'server') % self.account, _('If you remove it, the connection will be lost.'), [ DialogButton.make('Cancel'), DialogButton.make('Remove', callback=remove) ], transient_for=self._ui.remove_account_window).show() else: remove()
def _on_save_avatar(file_path): if os.path.exists(file_path): # Check if we have write permissions if not os.access(file_path, os.W_OK): file_name = os.path.basename(file_path) ErrorDialog( _('Cannot overwrite existing file \'%s\'' % file_name), _('A file with this name already exists and you do ' 'not have permission to overwrite it.')) return app.config.set('last_save_dir', os.path.dirname(file_path)) if isinstance(avatar, str): # We got a SHA pixbuf = app.interface.avatar_storage.pixbuf_from_filename(avatar) else: # We got a pixbuf pixbuf = avatar extension = os.path.splitext(file_path)[1] if not extension: # Silently save as Jpeg image image_format = 'png' file_path += '.png' else: image_format = extension[1:] # remove leading dot # Save image try: pixbuf.savev(file_path, image_format, [], []) except Exception as error: log.error('Error saving avatar: %s', error) if os.path.exists(file_path): os.remove(file_path) new_file_path = '.'.join(file_path.split('.')[:-1]) + '.png' def _on_ok(file_path, pixbuf): pixbuf.savev(file_path, 'png', [], []) NewConfirmationDialog( _('Error While Saving'), _('Extension not supported'), _('Image cannot be saved in %(type)s format.\n' 'Save as %(new_filename)s?') % { 'type': image_format, 'new_filename': new_file_path }, [ DialogButton.make('Cancel'), DialogButton.make('OK', text=_('_Save'), callback=_on_ok, args=[new_file_path, pixbuf]) ]).show()
def _delete_jid_logs(self, liststore, list_of_paths): paths_len = len(list_of_paths) if paths_len == 0: # nothing is selected return def on_ok(): # delete all rows from db that match jid_id list_of_rowrefs = [] for path in list_of_paths: # make them treerowrefs (it's needed) list_of_rowrefs.append( Gtk.TreeRowReference.new(liststore, path)) for rowref in list_of_rowrefs: path = rowref.get_path() if path is None: continue jid_id = liststore[path][1] del liststore[path] # remove from UI # remove from db self.cur.execute( ''' DELETE FROM logs WHERE jid_id = ? ''', (jid_id, )) # now delete "jid, jid_id" row from jids table self.cur.execute( ''' DELETE FROM jids WHERE jid_id = ? ''', (jid_id, )) self.con.commit() self.AT_LEAST_ONE_DELETION_DONE = True NewConfirmationDialog( _('Delete'), ngettext('Delete Conversation', 'Delete Conversations', paths_len), ngettext( 'Do you want to permanently delete this ' 'conversation with <b>%s</b>?', 'Do you want to permanently delete these conversations?', paths_len, liststore[list_of_paths[0]][0]), [ DialogButton.make('Cancel'), DialogButton.make('Delete', callback=on_ok) ], transient_for=self._ui.history_manager_window).show()
def show_hash_error(self, jid, file_props, account): def on_yes(dummy, fjid, file_props, account): # Delete old file os.remove(file_props.file_name) jid, resource = app.get_room_and_nick_from_fjid(fjid) if resource: contact = app.contacts.get_contact(account, jid, resource) else: contact = app.contacts.get_contact_with_highest_priority( account, jid) fjid = contact.get_full_jid() # Request the file to the sender sid = helpers.get_random_string_16() new_file_props = FilesProp.getNewFileProp(account, sid) new_file_props.file_name = file_props.file_name new_file_props.name = file_props.name new_file_props.desc = file_props.desc new_file_props.size = file_props.size new_file_props.date = file_props.date new_file_props.hash_ = file_props.hash_ new_file_props.type_ = 'r' tsid = app.connections[account].get_module( 'Jingle').start_file_transfer(fjid, new_file_props, True) new_file_props.transport_sid = tsid self.add_transfer(account, contact, new_file_props) if file_props.type_ == 'r': file_name = os.path.basename(file_props.file_name) else: file_name = file_props.name NewConfirmationDialog( _('File Transfer Error'), _('File Transfer Error'), _('The file %s has been received, but it seems to have ' 'been damaged along the way.\n' 'Do you want to download it again?') % file_name, [DialogButton.make('Cancel', text=_('_No')), DialogButton.make('Accept', text=_('_Download Again'), callback=on_yes, args=[jid, file_props, account])]).show()
def on_plugin_downloaded(self, plugin_dirs, auto_update): need_restart = False for _dir in plugin_dirs: updated = app.plugin_manager.update_plugins(replace=False, activate=True, plugin_name=_dir) if updated: if not auto_update: plugin = self._get_plugin(updated[0]) if plugin is None: log.error('Plugin %s not found', updated[0]) continue for row in range(len(self.available_plugins_model)): model_row = self.available_plugins_model[row] if plugin.name == model_row[Column.NAME]: model_row[Column.LOCAL_VERSION] = plugin.version model_row[Column.UPGRADE] = False break # Get plugin icon icon_file = os.path.join(plugin.__path__, os.path.split( plugin.__path__)[1]) + '.png' icon = FALLBACK_ICON if os.path.isfile(icon_file): icon = GdkPixbuf.Pixbuf.new_from_file_at_size( icon_file, 16, 16) row = [ plugin, plugin.name, plugin.active, plugin.activatable, icon ] self.installed_plugins_model.append(row) else: need_restart = True if not auto_update: if need_restart: sectext = _('Updates will be installed next time Gajim is ' 'started.') else: sectext = _('All selected plugins downloaded and activated.') InformationDialog(_('Plugin Updates Downloaded'), sectext) if auto_update and self.config['auto_update_feedback']: def _on_ok(is_checked): if is_checked: self.config['auto_update_feedback'] = False NewConfirmationCheckDialog( _('Plugins Updated'), _('Plugins Updated'), _('Plugin updates have successfully been downloaded.\n' 'Updates will be installed next time Gajim is started.'), _('Do not show this message again'), [DialogButton.make('OK', callback=_on_ok)]).show() if auto_update and not self.config['auto_update_feedback']: log.info('Updates downloaded, will install on next restart')
def on_ok(msg_name): msg_text = status_message_to_save_as_preset msg_text_1l = helpers.to_one_line(msg_text) if not msg_name: # msg_name was '' msg_name = msg_text_1l def _on_ok2(): self.preset_messages_dict[msg_name] = [ msg_text, self.pep_dict.get('activity'), self.pep_dict.get('subactivity'), self.pep_dict.get('activity_text'), self.pep_dict.get('mood'), self.pep_dict.get('mood_text') ] app.config.set_per('statusmsg', msg_name, 'message', msg_text_1l) app.config.set_per('statusmsg', msg_name, 'activity', self.pep_dict.get('activity')) app.config.set_per('statusmsg', msg_name, 'subactivity', self.pep_dict.get('subactivity')) app.config.set_per('statusmsg', msg_name, 'activity_text', self.pep_dict.get('activity_text')) app.config.set_per('statusmsg', msg_name, 'mood', self.pep_dict.get('mood')) app.config.set_per('statusmsg', msg_name, 'mood_text', self.pep_dict.get('mood_text')) if msg_name in self.preset_messages_dict: NewConfirmationDialog( _('Overwrite'), _('Overwrite Status Message?'), _('Preset name is already in use. ' 'Do you want to overwrite this preset?'), [ DialogButton.make('Cancel'), DialogButton.make( 'Remove', text=_('_Overwrite'), callback=_on_ok2) ], transient_for=self.dialog).show() return app.config.add_per('statusmsg', msg_name) _on_ok2() iter_ = self.message_liststore.append((msg_name, )) # select in combobox the one we just saved self.message_combobox.set_active_iter(iter_)
def _on_plugin_exists(zip_filename): def _on_yes(): plugin = app.plugin_manager.install_from_zip( zip_filename, True) if not plugin: show_warn_dialog() return NewConfirmationDialog( _('Overwrite Plugin?'), _('Plugin already exists'), _('Do you want to overwrite the currently installed version?'), [ DialogButton.make('Cancel'), DialogButton.make( 'Remove', text=_('_Overwrite'), callback=_on_yes) ], transient_for=self.window).show()
def on_remove_account(self, account): if app.events.get_events(account): app.interface.raise_dialog('unread-events-on-remove-account') return if app.config.get_per('accounts', account, 'is_zeroconf'): # Should never happen as button is insensitive return win_opened = False if app.interface.msg_win_mgr.get_controls(acct=account): win_opened = True elif account in app.interface.instances: for key in app.interface.instances[account]: if (app.interface.instances[account][key] and key != 'remove_account'): win_opened = True break # Detect if we have opened windows for this account def remove(): if (account in app.interface.instances and 'remove_account' in app.interface.instances[account]): dialog = app.interface.instances[account]['remove_account'] dialog.window.present() else: if account not in app.interface.instances: app.interface.instances[account] = {} app.interface.instances[account]['remove_account'] = \ RemoveAccountWindow(account) if win_opened: NewConfirmationDialog( _('Remove Account'), _('You still have open chats in your account %s') % account, _('All chat and group chat windows will be closed.\n' 'Do you want to continue?'), [ DialogButton.make('Cancel'), DialogButton.make('Remove', callback=remove) ], transient_for=self).show() else: remove()
def delete_fingerprint(self, *args): def _remove(): backend = self.get_toplevel()._omemo.backend backend.remove_device(self.jid, self.device_id) backend.storage.deleteSession(self.jid, self.device_id) backend.storage.deleteIdentity(self.jid, self._identity_key) self.get_parent().remove(self) self.destroy() NewConfirmationDialog( _('Delete'), _('Delete Fingerprint'), _('Doing so will permanently delete this Fingerprint'), [ DialogButton.make('Cancel'), DialogButton.make('Remove', text=_('Delete'), callback=_remove) ], transient_for=self.get_toplevel()).show()
def show_request_dialog(self, account, fjid, jid, sid, content_types): def _on_accept(): session = app.connections[account].get_module( 'Jingle').get_jingle_session(fjid, sid) self.sid = session.sid if not session.accepted: session.approve_session() for content in content_types: session.approve_content('xhtml') for _jid in (fjid, jid): ctrl = app.interface.msg_win_mgr.get_control(_jid, account) if ctrl: break if not ctrl: # Create it app.interface.new_chat_from_jid(account, jid) ctrl = app.interface.msg_win_mgr.get_control(jid, account) session = session.contents[('initiator', 'xhtml')] ctrl.draw_whiteboard(session) def _on_decline(): session = app.connections[account].get_module( 'Jingle').get_jingle_session(fjid, sid) session.decline_session() contact = app.contacts.get_first_contact_from_jid(account, jid) if contact: name = contact.get_shown_name() else: name = jid NewConfirmationDialog( _('Incoming Whiteboard'), _('Incoming Whiteboard Request'), _('%(name)s (%(jid)s) wants to start a whiteboard with ' 'you.') % {'name': name, 'jid': jid}, [DialogButton.make('Cancel', text=_('_Decline'), callback=_on_decline), DialogButton.make('OK', text=_('_Accept'), callback=_on_accept)], transient_for=app.app.get_active_window()).show()
def finished(self, file): def _open_file(): helpers.open_file(file.filepath) def _open_folder(): directory = os.path.dirname(file.filepath) helpers.open_file(directory) NewConfirmationDialog( _('Open File'), _('Open File?'), _('Do you want to open %s?') % file.filename, [ DialogButton.make('Cancel', text=_('_No')), DialogButton.make( 'OK', text=_('Open _Folder'), callback=_open_folder), DialogButton.make( 'Accept', text=_('_Open'), callback=_open_file) ], transient_for=self.window).show() return False
def _delete_logs(self, liststore, list_of_paths): paths_len = len(list_of_paths) if paths_len == 0: # nothing is selected return def on_ok(): # delete rows from db that match log_line_id list_of_rowrefs = [] for path in list_of_paths: # make them treerowrefs (it's needed) list_of_rowrefs.append( Gtk.TreeRowReference.new(liststore, path)) for rowref in list_of_rowrefs: path = rowref.get_path() if path is None: continue log_line_id = liststore[path][0] del liststore[path] # remove from UI # remove from db self.cur.execute( ''' DELETE FROM logs WHERE log_line_id = ? ''', (log_line_id, )) self.con.commit() self.AT_LEAST_ONE_DELETION_DONE = True NewConfirmationDialog( _('Delete'), ngettext('Delete Message', 'Delete Messages', paths_len), ngettext('Do you want to permanently delete this message', 'Do you want to permanently delete these messages', paths_len), [ DialogButton.make('Cancel'), DialogButton.make('Delete', callback=on_ok) ], transient_for=self._ui.history_manager_window).show()
def _mam_prefs_saved(self, obj): if obj.conn.name != self.account: return self._disable_spinner() def _on_ok(): self.destroy() NewConfirmationDialog( _('Archiving Preferences'), _('Archiving Preferences Saved'), _('Your archiving preferences have successfully been saved.'), [DialogButton.make('OK', callback=_on_ok)]).show()
def _on_window_delete(self, win, event): if self.dont_warn_on_delete: # Destroy the window return False # Number of controls that will be closed and for which we'll loose data: # chat, pm, gc that won't go in roster number_of_closed_control = 0 for ctrl in self.controls(): if not ctrl.safe_shutdown(): number_of_closed_control += 1 if number_of_closed_control > 1: def _on_yes1(checked): if checked: app.config.set('confirm_close_multiple_tabs', False) self.dont_warn_on_delete = True for ctrl in self.controls(): if ctrl.minimizable(): ctrl.minimize() win.destroy() if not app.config.get('confirm_close_multiple_tabs'): for ctrl in self.controls(): if ctrl.minimizable(): ctrl.minimize() # destroy window return False NewConfirmationCheckDialog( _('Close Tabs'), _('You are about to close several tabs'), _('Do you really want to close all of them?'), _('_Do not ask me again'), [ DialogButton.make('Cancel'), DialogButton.make( 'Accept', text=_('_Close'), callback=_on_yes1) ], transient_for=self.window).show() return True def on_yes(ctrl): if self.on_delete_ok == 1: self.dont_warn_on_delete = True win.destroy() self.on_delete_ok -= 1 def on_no(ctrl): return def on_minimize(ctrl): ctrl.minimize() if self.on_delete_ok == 1: self.dont_warn_on_delete = True win.destroy() self.on_delete_ok -= 1 # Make sure all controls are okay with being deleted self.on_delete_ok = self.get_nb_controls() for ctrl in self.controls(): ctrl.allow_shutdown(self.CLOSE_CLOSE_BUTTON, on_yes, on_no, on_minimize) return True # halt the delete for the moment