def do(self): diff_text = '' num_patches = len(self.patches) orig_head = self.model.git.rev_parse('HEAD')[STDOUT] for idx, patch in enumerate(self.patches): status, out, err = self.model.git.am(patch) # Log the git-am command Interaction.log_status(status, out, err) if num_patches > 1: diff = self.model.git.diff('HEAD^!', stat=True)[STDOUT] diff_text += (N_('PATCH %(current)d/%(count)d') % dict(current=idx+1, count=num_patches)) diff_text += ' - %s:\n%s\n\n' % (os.path.basename(patch), diff) diff_text += N_('Summary:') + '\n' diff_text += self.model.git.diff(orig_head, stat=True)[STDOUT] # Display a diffstat self.model.set_diff_text(diff_text) self.model.update_file_status() basenames = '\n'.join([os.path.basename(p) for p in self.patches]) Interaction.information( N_('Patch(es) Applied'), (N_('%d patch(es) applied.') + '\n\n%s') % (len(self.patches), basenames))
def do(self): diff_text = '' num_patches = len(self.patches) orig_head = self.model.git.rev_parse('HEAD') for idx, patch in enumerate(self.patches): status, output = self.model.git.am(patch, with_status=True, with_stderr=True) # Log the git-am command Interaction.log_status(status, output, '') if num_patches > 1: diff = self.model.git.diff('HEAD^!', stat=True) diff_text += 'Patch %d/%d - ' % (idx+1, num_patches) diff_text += '%s:\n%s\n\n' % (os.path.basename(patch), diff) diff_text += 'Summary:\n' diff_text += self.model.git.diff(orig_head, stat=True) # Display a diffstat self.model.set_diff_text(diff_text) self.model.update_file_status() Interaction.information( 'Patch(es) Applied', '%d patch(es) applied:\n\n%s' % (len(self.patches), '\n'.join(map(os.path.basename, self.patches))))
def do(self): diff_text = "" num_patches = len(self.patches) orig_head = self.model.git.rev_parse("HEAD")[STDOUT] for idx, patch in enumerate(self.patches): status, out, err = self.model.git.am(patch) # Log the git-am command Interaction.log_status(status, out, err) if num_patches > 1: diff = self.model.git.diff("HEAD^!", stat=True)[STDOUT] diff_text += N_("PATCH %(current)d/%(count)d") % dict(current=idx + 1, count=num_patches) diff_text += " - %s:\n%s\n\n" % (os.path.basename(patch), diff) diff_text += N_("Summary:") + "\n" diff_text += self.model.git.diff(orig_head, stat=True)[STDOUT] # Display a diffstat self.model.set_diff_text(diff_text) self.model.update_file_status() basenames = "\n".join([os.path.basename(p) for p in self.patches]) Interaction.information( N_("Patch(es) Applied"), (N_("%d patch(es) applied.") + "\n\n%s") % (len(self.patches), basenames) )
def do(self): diff_text = "" num_patches = len(self.patches) orig_head = self.model.git.rev_parse("HEAD") for idx, patch in enumerate(self.patches): status, output = self.model.git.am(patch, with_status=True, with_stderr=True) # Log the git-am command Interaction.log_status(status, output, "") if num_patches > 1: diff = self.model.git.diff("HEAD^!", stat=True) diff_text += "Patch %d/%d - " % (idx + 1, num_patches) diff_text += "%s:\n%s\n\n" % (os.path.basename(patch), diff) diff_text += "Summary:\n" diff_text += self.model.git.diff(orig_head, stat=True) # Display a diffstat self.model.set_diff_text(diff_text) self.model.update_file_status() Interaction.information( "Patch(es) Applied", "%d patch(es) applied:\n\n%s" % (len(self.patches), "\n".join(map(os.path.basename, self.patches))), )
def clone_repo(spawn=True): """ Present GUI controls for cloning a repository A new cola session is invoked when 'spawn' is True. """ url, ok = qtutils.prompt(N_('Path or URL to clone (Env. $VARS okay)')) url = os.path.expandvars(core.encode(url)) if not ok or not url: return None try: # Pick a suitable basename by parsing the URL newurl = url.replace('\\', '/').rstrip('/') default = newurl.rsplit('/', 1)[-1] if default == '.git': # The end of the URL is /.git, so assume it's a file path default = os.path.basename(os.path.dirname(newurl)) if default.endswith('.git'): # The URL points to a bare repo default = default[:-4] if url == '.': # The URL is the current repo default = os.path.basename(os.getcwd()) if not default: raise except: Interaction.information( N_('Error Cloning'), N_('Could not parse Git URL: "%s"') % url) Interaction.log(N_('Could not parse Git URL: "%s"') % url) return None # Prompt the user for a directory to use as the parent directory msg = N_('Select a parent directory for the new clone') dirname = qtutils.opendir_dialog(msg, cola.model().getcwd()) if not dirname: return None count = 1 dirname = core.decode(dirname) destdir = os.path.join(dirname, core.decode(default)) olddestdir = destdir if os.path.exists(destdir): # An existing path can be specified msg = (N_('"%s" already exists, cola will create a new directory') % destdir) Interaction.information('Directory Exists', msg) # Make sure the new destdir doesn't exist while os.path.exists(destdir): destdir = olddestdir + str(count) count += 1 if cmds.do(cmds.Clone, core.decode(url), destdir, spawn=spawn): return destdir return None
def do(self): rescan = False for filename in self.filenames: if filename: try: os.remove(filename) rescan = True except: Interaction.information("Error", 'Deleting "%s" failed' % filename) if rescan: self.model.update_file_status()
def do(self): status, out = self.model.git.clone(self.url, self.new_directory, with_stderr=True, with_status=True) if status != 0: Interaction.information( "Error cloning: %s" % self.url, "git clone returned exit code %s%s" % (status, out and ("\n" + out) or ""), ) return False if self.spawn: utils.fork([sys.executable, sys.argv[0], "--repo", self.new_directory]) return True
def rebase_start(self): cfg = gitcfg.current() if not cfg.get("rebase.autostash", False): if self.model.staged or self.model.unmerged or self.model.modified: Interaction.information(N_("Unable to rebase"), N_("You cannot rebase with uncommitted changes.")) return upstream = guicmds.choose_ref(N_("Select New Upstream"), N_("Interactive Rebase"), default="@{upstream}") if not upstream: return self.model.is_rebasing = True self._update_callback() cmds.do(cmds.Rebase, upstream=upstream)
def do(self): status, out, err = self.model.git.clone(self.url, self.new_directory) if status != 0: Interaction.information( N_('Error: could not clone "%s"') % self.url, (N_('git clone returned exit code %s') % status) + ((out+err) and ('\n\n' + out + err) or '')) return False if self.spawn: core.fork([sys.executable, sys.argv[0], '--repo', self.new_directory]) return True
def prompt_for_clone(): """ Present a GUI for cloning a repository. Returns the target directory and URL """ url, ok = qtutils.prompt(N_('Path or URL to clone (Env. $VARS okay)')) url = utils.expandpath(url) if not ok or not url: return None try: # Pick a suitable basename by parsing the URL newurl = url.replace('\\', '/').rstrip('/') default = newurl.rsplit('/', 1)[-1] if default == '.git': # The end of the URL is /.git, so assume it's a file path default = os.path.basename(os.path.dirname(newurl)) if default.endswith('.git'): # The URL points to a bare repo default = default[:-4] if url == '.': # The URL is the current repo default = os.path.basename(core.getcwd()) if not default: raise except: Interaction.information( N_('Error Cloning'), N_('Could not parse Git URL: "%s"') % url) Interaction.log(N_('Could not parse Git URL: "%s"') % url) return None # Prompt the user for a directory to use as the parent directory msg = N_('Select a parent directory for the new clone') dirname = qtutils.opendir_dialog(msg, main.model().getcwd()) if not dirname: return None count = 1 destdir = os.path.join(dirname, default) olddestdir = destdir if core.exists(destdir): # An existing path can be specified msg = (N_('"%s" already exists, cola will create a new directory') % destdir) Interaction.information(N_('Directory Exists'), msg) # Make sure the new destdir doesn't exist while core.exists(destdir): destdir = olddestdir + str(count) count += 1 return url, destdir
def rebase_start(self): if self.model.staged or self.model.unmerged or self.model.modified: Interaction.information( N_('Unable to rebase'), N_('You cannot rebase with uncommitted changes.')) return branch = guicmds.choose_ref(N_('Select New Upstream'), N_('Interactive Rebase')) if not branch: return None self.model.is_rebasing = True self._update_callback() cmds.do(cmds.Rebase, branch)
def do(self): model = self.model cmd = ["git", "show", "%s:%s" % (model.ref, model.relpath)] with core.xopen(model.filename, "wb") as fp: proc = core.start_command(cmd, stdout=fp) out, err = proc.communicate() status = proc.returncode msg = N_('Saved "%(filename)s" from "%(ref)s" to "%(destination)s"') % dict( filename=model.relpath, ref=model.ref, destination=model.filename ) Interaction.log_status(status, msg, "") Interaction.information(N_("File Saved"), N_('File saved to "%s"') % model.filename)
def do(self): status, out, err = self.model.git.push(self.remote, self.branch, delete=True) Interaction.log_status(status, out, err) self.model.update_status() if status == 0: Interaction.information( N_("Remote Branch Deleted"), N_('"%(branch)s" has been deleted from "%(remote)s".') % dict(branch=self.branch, remote=self.remote), ) else: command = "git push" message = N_('"%(command)s" returned exit status %(status)d') % dict(command=command, status=status) Interaction.critical(N_("Error Deleting Remote Branch"), message, out + err)
def do(self): """Leave/enter amend mode.""" """Attempt to enter amend mode. Do not allow this when merging.""" if self.amending: if self.model.is_merging: self.skip = True self.model.set_mode(self.old_mode) Interaction.information( N_("Cannot Amend"), N_("You are in the middle of a merge.\n" "Cannot amend while merging.") ) return self.skip = False Command.do(self) self.model.set_commitmsg(self.new_commitmsg) self.model.update_file_status()
def rebase_start(self): cfg = gitcfg.current() if not cfg.get('rebase.autostash', False): if self.model.staged or self.model.unmerged or self.model.modified: Interaction.information( N_('Unable to rebase'), N_('You cannot rebase with uncommitted changes.')) return upstream = guicmds.choose_ref(N_('Select New Upstream'), N_('Interactive Rebase'), default='@{upstream}') if not upstream: return self.model.is_rebasing = True self._update_callback() cmds.do(cmds.Rebase, upstream=upstream)
def do(self): """Leave/enter amend mode.""" """Attempt to enter amend mode. Do not allow this when merging.""" if self.amending: if os.path.exists(self.model.git.git_path('MERGE_HEAD')): self.skip = True _notifier.broadcast(_notifier.AMEND, False) Interaction.information( 'Oops! Unmerged', 'You are in the middle of a merge.\n' 'You cannot amend while merging.') return self.skip = False _notifier.broadcast(_notifier.AMEND, self.amending) self.model.set_commitmsg(self.new_commitmsg) Command.do(self) self.model.update_file_status()
def do(self): """Leave/enter amend mode.""" """Attempt to enter amend mode. Do not allow this when merging.""" if self.amending: if self.model.is_merging: self.skip = True self.model.set_mode(self.old_mode) Interaction.information( N_('Cannot Amend'), N_('You are in the middle of a merge.\n' 'Cannot amend while merging.')) return self.skip = False Command.do(self) self.model.set_commitmsg(self.new_commitmsg) self.model.update_file_status()
def do(self): model = self.model cmd = ['git', 'show', '%s:%s' % (model.ref, model.relpath)] with core.xopen(model.filename, 'wb') as fp: proc = core.start_command(cmd, stdout=fp) out, err = proc.communicate() status = proc.returncode msg = (N_('Saved "%(filename)s" from "%(ref)s" to "%(destination)s"') % dict(filename=model.relpath, ref=model.ref, destination=model.filename)) Interaction.log_status(status, msg, '') Interaction.information(N_('File Saved'), N_('File saved to "%s"') % model.filename)
def do(self): status, out = self.model.git.clone(self.url, self.new_directory, with_stderr=True, with_status=True) if status != 0: Interaction.information( 'Error cloning: %s' % self.url, 'git clone returned exit code %s%s' % (status, out and ('\n' + out) or '')) return False if self.spawn: utils.fork([sys.executable, sys.argv[0], '--repo', self.new_directory]) return True
def do(self): """Leave/enter amend mode.""" """Attempt to enter amend mode. Do not allow this when merging.""" if self.amending: if os.path.exists(self.model.git.git_path("MERGE_HEAD")): self.skip = True _notifier.broadcast(_notifier.AMEND, False) Interaction.information( "Oops! Unmerged", "You are in the middle of a merge.\n" "You cannot amend while merging." ) return self.skip = False _notifier.broadcast(_notifier.AMEND, self.amending) self.model.set_commitmsg(self.new_commitmsg) Command.do(self) self.model.update_file_status()
def do(self): """Leave/enter amend mode.""" """Attempt to enter amend mode. Do not allow this when merging.""" if self.amending: if core.exists(self.model.git.git_path('MERGE_HEAD')): self.skip = True _notifier.broadcast(_notifier.AMEND, False) Interaction.information( N_('Cannot Amend'), N_('You are in the middle of a merge.\n' 'Cannot amend while merging.')) return self.skip = False _notifier.broadcast(_notifier.AMEND, self.amending) self.model.set_commitmsg(self.new_commitmsg) Command.do(self) self.model.update_file_status()
def do(self): status, out, err = self.model.git.push(self.remote, self.branch, delete=True) Interaction.log_status(status, out, err) self.model.update_status() if status == 0: Interaction.information( N_('Remote Branch Deleted'), N_('"%(branch)s" has been deleted from "%(remote)s".') % dict(branch=self.branch, remote=self.remote)) else: command = 'git push' message = (N_('"%(command)s" returned exit status %(status)d') % dict(command=command, status=status)) Interaction.critical(N_('Error Deleting Remote Branch'), message, out + err)
def do(self): model = self.model ref = core.encode(model.ref) relpath = core.encode(model.relpath) cmd = ['git', 'show', '%s:%s' % (ref, relpath)] fp = open(core.encode(model.filename), 'wb') proc = utils.start_command(cmd, stdout=fp) out, err = proc.communicate() fp.close() status = proc.returncode msg = ('Saved "%s" from %s to "%s"' % (model.relpath, model.ref, model.filename)) Interaction.log_status(status, msg, '') Interaction.information( 'File Saved', 'File saved to "%s"' % model.filename)
def do(self): status, output = self.model.git.push(self.remote, self.branch, delete=True, with_status=True, with_stderr=True) self.model.update_status() Interaction.log_status(status, output) if status == 0: Interaction.information( N_('Remote Branch Deleted'), N_('"%(branch)s" has been deleted from "%(remote)s".') % dict(branch=self.branch, remote=self.remote)) else: command = 'git push' message = (N_('"%(command)s" returned exit status %(status)d') % dict(command=command, status=status)) Interaction.critical(N_('Error Deleting Remote Branch'), message, output)
def do(self): files = self.filenames if not files: return rescan = False bad_filenames = [] remove = self.remover for filename in files: if filename: try: remove(filename) rescan = True except: bad_filenames.append(filename) if bad_filenames: Interaction.information(N_("Error"), N_('Deleting "%s" failed') % file_summary(files)) if rescan: self.model.update_file_status()
def do(self): files = self.filenames if not files: return rescan = False bad_filenames = [] remove = self.remover for filename in files: if filename: try: remove(filename) rescan=True except: bad_filenames.append(filename) if bad_filenames: Interaction.information( N_('Error'), N_('Deleting "%s" failed') % file_summary(files)) if rescan: self.model.update_file_status()
def action_completed(self, task, status, output): # Grab the results of the action and finish up self.progress_thread.stop() self.progress_thread.wait() if task in self.tasks: self.tasks.remove(task) already_up_to_date = N_('Already up-to-date.') if not output: # git fetch --tags --verbose doesn't print anything... output = already_up_to_date self.setEnabled(True) self.progress.close() QtGui.QApplication.restoreOverrideCursor() command = 'git %s' % self.action.lower() message = (N_('"%(command)s" returned exit status %(status)d') % dict(command=command, status=status)) if output: message += '\n\n' + output Interaction.log(message) if status == 0: Interaction.information( N_('Success'), N_('"git %s" succeeded.') % self.action.lower()) self.accept() return if self.action == PUSH: message += '\n\n' message += N_('Have you rebased/pulled lately?') Interaction.critical(self.windowTitle(), message=message, details=output)
def do(self): for env in ('FILENAME', 'REVISION', 'ARGS'): try: compat.unsetenv(env) except KeyError: pass rev = None args = None opts = _config.get_guitool_opts(self.action_name) cmd = opts.get('cmd') if 'title' not in opts: opts['title'] = cmd if 'prompt' not in opts or opts.get('prompt') is True: prompt = N_('Run "%s"?') % cmd opts['prompt'] = prompt if opts.get('needsfile'): filename = selection.filename() if not filename: Interaction.information( N_('Please select a file'), N_('"%s" requires a selected file.') % cmd) return False compat.setenv('FILENAME', filename) if opts.get('revprompt') or opts.get('argprompt'): while True: ok = Interaction.confirm_config_action(cmd, opts) if not ok: return False rev = opts.get('revision') args = opts.get('args') if opts.get('revprompt') and not rev: title = N_('Invalid Revision') msg = N_('The revision expression cannot be empty.') Interaction.critical(title, msg) continue break elif opts.get('confirm'): title = os.path.expandvars(opts.get('title')) prompt = os.path.expandvars(opts.get('prompt')) if Interaction.question(title, prompt): return if rev: compat.setenv('REVISION', rev) if args: compat.setenv('ARGS', args) title = os.path.expandvars(cmd) Interaction.log(N_('Running command: %s') % title) cmd = ['sh', '-c', cmd] if opts.get('noconsole'): status, out, err = core.run_command(cmd) else: status, out, err = Interaction.run_command(title, cmd) Interaction.log_status(status, out and (N_('Output: %s') % out) or '', err and (N_('Errors: %s') % err) or '') if not opts.get('norescan'): self.model.update_status() return status
def do(self): for env in ("FILENAME", "REVISION", "ARGS"): try: compat.unsetenv(env) except KeyError: pass rev = None args = None cfg = gitcfg.current() opts = cfg.get_guitool_opts(self.action_name) cmd = opts.get("cmd") if "title" not in opts: opts["title"] = cmd if "prompt" not in opts or opts.get("prompt") is True: prompt = N_('Run "%s"?') % cmd opts["prompt"] = prompt if opts.get("needsfile"): filename = selection.filename() if not filename: Interaction.information(N_("Please select a file"), N_('"%s" requires a selected file.') % cmd) return False compat.setenv("FILENAME", filename) if opts.get("revprompt") or opts.get("argprompt"): while True: ok = Interaction.confirm_config_action(cmd, opts) if not ok: return False rev = opts.get("revision") args = opts.get("args") if opts.get("revprompt") and not rev: title = N_("Invalid Revision") msg = N_("The revision expression cannot be empty.") Interaction.critical(title, msg) continue break elif opts.get("confirm"): title = os.path.expandvars(opts.get("title")) prompt = os.path.expandvars(opts.get("prompt")) if Interaction.question(title, prompt): return if rev: compat.setenv("REVISION", rev) if args: compat.setenv("ARGS", args) title = os.path.expandvars(cmd) Interaction.log(N_("Running command: %s") % title) cmd = ["sh", "-c", cmd] if opts.get("background"): core.fork(cmd) status, out, err = (0, "", "") elif opts.get("noconsole"): status, out, err = core.run_command(cmd) else: status, out, err = Interaction.run_command(title, cmd) Interaction.log_status(status, out and (N_("Output: %s") % out) or "", err and (N_("Errors: %s") % err) or "") if not opts.get("background") and not opts.get("norescan"): self.model.update_status() return status
def do(self): for env in ('FILENAME', 'REVISION', 'ARGS'): try: compat.unsetenv(env) except KeyError: pass rev = None args = None cfg = gitcfg.current() opts = cfg.get_guitool_opts(self.action_name) cmd = opts.get('cmd') if 'title' not in opts: opts['title'] = cmd if 'prompt' not in opts or opts.get('prompt') is True: prompt = N_('Run "%s"?') % cmd opts['prompt'] = prompt if opts.get('needsfile'): filename = selection.filename() if not filename: Interaction.information( N_('Please select a file'), N_('"%s" requires a selected file.') % cmd) return False compat.setenv('FILENAME', filename) if opts.get('revprompt') or opts.get('argprompt'): while True: ok = Interaction.confirm_config_action(cmd, opts) if not ok: return False rev = opts.get('revision') args = opts.get('args') if opts.get('revprompt') and not rev: title = N_('Invalid Revision') msg = N_('The revision expression cannot be empty.') Interaction.critical(title, msg) continue break elif opts.get('confirm'): title = os.path.expandvars(opts.get('title')) prompt = os.path.expandvars(opts.get('prompt')) if Interaction.question(title, prompt): return if rev: compat.setenv('REVISION', rev) if args: compat.setenv('ARGS', args) title = os.path.expandvars(cmd) Interaction.log(N_('Running command: %s') % title) cmd = ['sh', '-c', cmd] if opts.get('background'): core.fork(cmd) status, out, err = (0, '', '') elif opts.get('noconsole'): status, out, err = core.run_command(cmd) else: status, out, err = Interaction.run_command(title, cmd) Interaction.log_status(status, out and (N_('Output: %s') % out) or '', err and (N_('Errors: %s') % err) or '') if not opts.get('background') and not opts.get('norescan'): self.model.update_status() return status
def do(self): for env in ("FILENAME", "REVISION", "ARGS"): try: compat.unsetenv(env) except KeyError: pass rev = None args = None opts = _config.get_guitool_opts(self.name) cmd = opts.get("cmd") if "title" not in opts: opts["title"] = cmd if "prompt" not in opts or opts.get("prompt") is True: prompt = i18n.gettext("Are you sure you want to run %s?") % cmd opts["prompt"] = prompt if opts.get("needsfile"): filename = selection.filename() if not filename: Interaction.information("Please select a file", '"%s" requires a selected file' % cmd) return False compat.putenv("FILENAME", filename) if opts.get("revprompt") or opts.get("argprompt"): while True: ok = Interaction.confirm_config_action(cmd, opts) if not ok: return False rev = opts.get("revision") args = opts.get("args") if opts.get("revprompt") and not rev: title = "Invalid Revision" msg = "The revision expression cannot be empty." Interaction.critical(title, msg) continue break elif opts.get("confirm"): title = os.path.expandvars(opts.get("title")) prompt = os.path.expandvars(opts.get("prompt")) if Interaction.question(title, prompt): return if rev: compat.putenv("REVISION", rev) if args: compat.putenv("ARGS", args) title = os.path.expandvars(cmd) Interaction.log("running: " + title) cmd = ["sh", "-c", cmd] if opts.get("noconsole"): status, out, err = utils.run_command(cmd) else: status, out, err = Interaction.run_command(title, cmd) Interaction.log_status(status, out and "stdout: %s" % out, err and "stderr: %s" % err) if not opts.get("norescan"): self.model.update_status() return status
def do(self): for env in ('FILENAME', 'REVISION', 'ARGS'): try: compat.unsetenv(env) except KeyError: pass rev = None args = None opts = _config.get_guitool_opts(self.name) cmd = opts.get('cmd') if 'title' not in opts: opts['title'] = cmd if 'prompt' not in opts or opts.get('prompt') is True: prompt = i18n.gettext('Are you sure you want to run %s?') % cmd opts['prompt'] = prompt if opts.get('needsfile'): filename = selection.filename() if not filename: Interaction.information('Please select a file', '"%s" requires a selected file' % cmd) return False compat.putenv('FILENAME', filename) if opts.get('revprompt') or opts.get('argprompt'): while True: ok = Interaction.confirm_config_action(cmd, opts) if not ok: return False rev = opts.get('revision') args = opts.get('args') if opts.get('revprompt') and not rev: title = 'Invalid Revision' msg = 'The revision expression cannot be empty.' Interaction.critical(title, msg) continue break elif opts.get('confirm'): title = os.path.expandvars(opts.get('title')) prompt = os.path.expandvars(opts.get('prompt')) if Interaction.question(title, prompt): return if rev: compat.putenv('REVISION', rev) if args: compat.putenv('ARGS', args) title = os.path.expandvars(cmd) Interaction.log('running: ' + title) cmd = ['sh', '-c', cmd] if opts.get('noconsole'): status, out, err = utils.run_command(cmd) else: status, out, err = Interaction.run_command(title, cmd) Interaction.log_status(status, out and 'stdout: %s' % out, err and 'stderr: %s' % err) if not opts.get('norescan'): self.model.update_status() return status
def save(self): """Saves the bookmarks settings and exits""" self.model.save() Interaction.information(N_('Bookmarks Saved'), N_('Successfully saved bookmarks')) self.save_button.setEnabled(False)