예제 #1
0
 def __call__(self):
     fileName = as_human_readable(self.get_chosen_files()[0])
     gpg = GPG()
     myPass, ok = show_prompt('Passphrase')
     # https://stackoverflow.com/questions/4444923/get-filename-without-extension-in-python
     fileNameOut, ok = show_prompt('Target',
                                   default=os.path.splitext(fileName)[0])
     with open(fileName, 'rb') as f:
         status = gpg.decrypt_file(f, passphrase=myPass, output=fileNameOut)
     show_alert('Status: ' + status.status)
예제 #2
0
 def setSymbolHiddenF(self, value_default):
     value_cfg = " ".join(self.cfgCurrent['SymbolHiddenF'])
     prompt_msg      = "Please enter two symbols, separated by space, to indicate whether hidden files are Shown/Hidden" +'\n'\
         + "or leave the field empty to restore the default ("+str(value_default)+"):"
     selection_start = 0
     selection_end = 0
     value_new = ''
     value_new_list = []
     _len = len(value_new_list)
     len_def = len(value_default)
     while _len != len_def:
         value_new, ok = show_prompt(prompt_msg, value_cfg, selection_start,
                                     selection_end)
         value_cfg = value_new  # preserve user input on multiple edits
         if not ok:
             show_status_message("StatusBarExtended: setup canceled")
             return
         if value_new.strip(' ') == '':
             self.cfgCurrent['SymbolHiddenF'] = value_default
             return
         value_new_nosp = ' '.join(
             value_new.split())  # replace multiple spaces with 1
         value_new_list = value_new_nosp.split(' ')  # split by space
         _len = len(value_new_list)
         if _len != 2:
             show_alert("You entered\n" + value_new +'\n'\
                 + "I parsed it as " + str(value_new_list) + " with " + str(_len) + " element" + ("" if _len==1 else "s") +'\n'\
                 + "but was expecting " + str(len_def) + " elements")
     self.cfgCurrent['SymbolHiddenF'] = value_new_list
예제 #3
0
 def setMaxGlob(self, value_default):
     value_cfg = str(self.cfgCurrent['MaxGlob'])
     prompt_msg      = "Please enter a natural number to set the threshold of the number of folders+files in a pane," +'\n'\
         + "above which the status bar for such a pane will not be updated to improve performance" +'\n'\
         + "or enter '0' to disable" +'\n'\
         + "or leave the field empty to restore the default ("+str(value_default)+"):"
     selection_start = 0
     value_new = ''
     while not isNat0(value_new):
         value_new, ok = show_prompt(prompt_msg, value_cfg, selection_start)
         value_cfg = value_new  # preserve user input on multiple edits
         if not ok:
             show_status_message("StatusBarExtended: setup canceled")
             return
         if value_new.strip(' ') == '':
             self.cfgCurrent['MaxGlob'] = value_default
             return
         if value_new.strip(' ') == '0':
             self.cfgCurrent['MaxGlob'] = 0
             return
         if not isInt(value_new):
             show_alert("You entered\n" + value_new +'\n'\
                 + "but I couldn't parse it as an integer")
         elif not isNat0(value_new):
             show_alert("You entered\n" + value_new +'\n'\
                 + "but I was expecting a non-negative integer 0,1,2,3–∞")
     self.cfgCurrent['MaxGlob'] = int(value_new)
예제 #4
0
    def __call__(self):
        show_status_message('Creating a Script...')
        scriptVars = _GetScriptVars()
        script, flags = show_prompt("New Script Name?")
        newScript = scriptVars['directory'] + os.sep + script
        if os.path.isdir(newScript):
            show_alert("This is a directory.")
        else:
            if os.path.isfile(newScript):
                show_alert("Script already exists.")
            else:
                #
                # Create the script file.
                #
                fp = open(newScript,"w+")
                fp.write("#!/bin/sh\n\n")
                fp.write("#\n# The following variable are usable:\n#\n")
                fp.write("# $FILES_SELECTED - The currently selected file\n")
                fp.write("# $LEFT_PANE - The directory of the left pane\n")
                fp.write("# $RIGHT_PANE - The directory of the right pane\n")
                fp.write("# $CURRENT_DIRECTORY - The currently selected directory\n")
                fp.write("# $LEFT_PANE_SELECTED_FILE - The currently selected file in the left pane\n")
                fp.write("# $RIGHT_PANE_SELECTED_FILE - The currently selected file in the right pane\n")
                fp.close()
                os.chmod(newScript,0o755)

                #
                # Edit the script file.
                #
                if self.pane.is_command_visible('open_with_editor'):
                    self.pane.run_command('open_with_editor',{'url': as_url(newScript)})
        clear_status_message()
예제 #5
0
 def setEnabled(self, value_default):
     _t = ('1', 't', 'true')
     _f = ('0', 'f', 'false')
     _tsep = "'" + "' or '".join(_t) + "'"
     _fsep = "'" + "' or '".join(_f) + "'"
     _accept = (_t + _f)
     value_cfg = str(self.cfgCurrent['Enabled'])
     prompt_msg      = "Please enter " +_tsep+ " to enable this plugin" +'\n'\
         + "or " +_fsep+ " to disable it" +'\n'\
         + "or leave the field empty to restore the default ("+str(value_default) +'):'
     selection_start = 0
     value_new = ''
     value_new_fmt = value_new.casefold()
     while value_new_fmt not in _accept:
         value_new, ok = show_prompt(prompt_msg, value_cfg, selection_start)
         value_cfg = value_new  # preserve user input on multiple edits
         if not ok:
             show_status_message("StatusBarExtended: setup canceled")
             return
         if value_new.strip(' ') == '':
             self.cfgCurrent['Enabled'] = value_default
             return
         value_new_fmt = value_new.casefold()
         if value_new_fmt not in _accept:
             show_alert("You entered\n" + value_new +'\n'\
                 + "I parsed it as " + value_new_fmt +'\n'\
                 + "but the only acceptable values are:\n" +_tsep+ "\n" +_fsep)
     self.cfgCurrent['Enabled'] = True if value_new_fmt in _t else False
예제 #6
0
 def __call__(self):
     text, ok = show_prompt(
         'Please enter the URL',
         default='ftp[s]://[user[:password]@]ftp.host[:port][/path/to/dir]')
     if text and ok:
         self.pane.set_path(text)
         return
예제 #7
0
 def setHideDotfile(self, value_default):
     _t = ('1', 't', 'true')
     _f = ('0', 'f', 'false')
     _tsep = "'" + "' or '".join(_t) + "'"
     _fsep = "'" + "' or '".join(_f) + "'"
     _accept = (_t + _f)
     value_cfg = str(self.cfgCurrent['HideDotfile'])
     prompt_msg      = "Please enter " +_tsep+ " to treat all .dotfiles on Windows as hidden files\n    even if they don't have a 'hidden' attribute" +'\n'\
         + "or " +_fsep+ " to treat them as regular files" +'\n'\
         + "or leave the field empty to restore the default ("+str(value_default) +'):'
     selection_start = 0
     value_new = ''
     value_new_fmt = value_new.casefold()
     while value_new_fmt not in _accept:
         value_new, ok = show_prompt(prompt_msg, value_cfg, selection_start)
         value_cfg = value_new  # preserve user input on multiple edits
         if not ok:
             show_status_message("StatusBarExtended: setup canceled")
             return
         if value_new.strip(' ') == '':
             self.cfgCurrent['HideDotfile'] = value_default
             return
         value_new_fmt = value_new.casefold()
         if value_new_fmt not in _accept:
             show_alert("You entered\n" + value_new +'\n'\
                 + "I parsed it as " + value_new_fmt +'\n'\
                 + "but the only acceptable values are:\n" +_tsep+ "\n" +_fsep)
     self.cfgCurrent['HideDotfile'] = True if value_new_fmt in _t else False
예제 #8
0
 def __call__(self):
     #
     # Get the directory path.
     #
     selected_files = self.pane.get_selected_files()
     if len(selected_files) >= 1 or (len(selected_files) == 0 and self.get_chosen_files()):
         if len(selected_files) == 0 and self.get_chosen_files():
             selected_files.append(self.get_chosen_files()[0])
         dirName = as_human_readable(selected_files[0])
         if os.path.isfile(dirName):
             #
             # It's a file, not a directory. Get the directory
             # name for this file's parent directory.
             #
             dirName = os.path.dirname(dirName)
         #
         # Add to the list of projects. Get a name
         # from the user.
         #
         shortener, checked = show_prompt("Name this Directory Shortener:")
         shortEntry = shortener + "|" + dirName
         writeappend = 'w'
         if os.path.isfile(SHORTENERLIST):
             writeappend = 'a'
         with open(SHORTENERLIST, writeappend) as f:
             f.write(shortEntry+"\n")
예제 #9
0
 def __call__(self):
     scriptVars = _GetScriptVars()
     shellFile, status = show_prompt("What is your shell script?")
     shellFile = os.path.expanduser(shellFile)
     if not os.path.isfile(shellFile):
         show_alert("Not a real file.")
     else:
         scriptVars['local_shell'] = shellFile
         _SaveScriptVars(scriptVars)
예제 #10
0
    def __call__(self):
        valid_methods = "\n".join(f" - {hm}" for hm in hash_map.keys())
        chosen_files = self.get_chosen_files()
        results = []

        # perform some checks on the selected files
        # as of now, only individual files are allowed, not directories
        dirs_in_selection = any([is_dir(fl) for fl in chosen_files])
        if dirs_in_selection:
            show_alert("Please specify individual files, not directories.")
            return
        else:
            hmeth, okay = show_prompt(
                f"Enter one of the following hashing methods:\n{valid_methods}"
            )
            hmeth = hmeth.strip()
            if hmeth not in hash_map.keys():
                response = show_alert(
                    "Invalid hashing method. No action taken.")
                return
        s = None
        if hmeth in ["shake_128", "shake_256"]:
            s, okay = show_prompt("Enter the size of the hash:")
            if not s.isnumeric():
                response = show_alert("Invalid integer. Goodbye!")
                return
            s = int(s)

        # perform the actual hash calculations
        num_selected_files = len(chosen_files)
        for fl in chosen_files:
            fl = as_human_readable(fl)
            fhash = file_hash(fl, hmeth, s)
            hstr = f"{fl}: {fhash}"
            results.append(hstr)
            all_results = "\n".join(results)
            clipboard.set_text(all_results)
        if num_selected_files == 1:
            msg = (
                f"The path and {hmeth} hash of the specified file has been copied to your clipboard as well as being attached here:\n\n{all_results}"
            )
        else:
            msg = f"The paths and {hmeth} hashes of the {num_selected_files} selected files have been copied to your clipboard as well as being attached here:\n\n{all_results}"
        response = show_alert(msg)
예제 #11
0
    def __call__(self):
        url = self.pane.get_path()
        if not is_ftp(url):
            url = 'ftp[s]://user[:password]@other.host[:port]/some_dir'

        url, ok = show_prompt('New FTP bookmark, please enter the URL',
                              default=url)

        if not (url and ok):
            return
        if not is_ftp(url):
            show_alert('URL must include any of the following schemes: '
                       'ftp://, ftps://')
            return

        bookmarks = \
            load_json('FTP Bookmarks.json', default={}, save_on_quit=True)

        # XXX URL is split in `(base, path)` to allow setting a default path
        u = urlparse(url)
        base = alias = u._replace(path='').geturl()
        path = u.path

        if base in bookmarks:
            # XXX if base URL points to an alias, resolve to an existing URL
            base = bookmarks[base][0]

        if path and path.strip('/'):
            alias += '-'.join(path.split('/'))
        alias, ok = show_prompt(
            'Please enter an alias (will override aliases with the same name)',
            default=alias)

        if not (alias and ok):
            return
        if not is_ftp(alias):
            # XXX alias must include the FTP scheme
            scheme, _ = splitscheme(base)
            alias = scheme + alias
        if urlparse(alias).path:
            show_alert('Aliases should not include path information')
            return

        bookmarks[alias] = (base, path)
예제 #12
0
 def __call__(self):
     fileName = as_human_readable(self.get_chosen_files()[0])
     gpg = GPG()
     default_recipient = self._get_default_recipient()
     recipient, ok = show_prompt('Recipient', default=default_recipient)
     with open(fileName, 'rb') as f:
         status = gpg.encrypt_file(f,
                                   recipients=[recipient],
                                   output=fileName + '.gpg')
     show_alert('Status: ' + status.status)
예제 #13
0
 def __call__(self):
     #
     # Get the directory path.
     #
     selected_files = self.pane.get_selected_files()
     if len(selected_files) >= 1 or (len(selected_files) == 0
                                     and self.get_chosen_files()):
         if len(selected_files) == 0 and self.get_chosen_files():
             selected_files.append(self.get_chosen_files()[0])
         dirName = as_human_readable(selected_files[0])
         if os.path.isfile(dirName):
             #
             # It's a file, not a directory. Get the directory
             # name for this file's parent directory.
             #
             dirName = os.path.dirname(dirName)
         #
         # Set the directory obtained as a project directory.
         #
         with open(PROJECTDIR, "w") as f:
             f.write(dirName)
         #
         # Add to the list of projects. Get a name
         # from the user.
         #
         projName, checked = show_prompt("Name this Project:")
         projEntry = projName + "|" + dirName
         writeappend = 'w'
         if os.path.isfile(PROJECTSLIST):
             writeappend = 'a'
         with open(PROJECTSLIST, writeappend) as f:
             f.write(projEntry + "\n")
         #
         # Create the launch script file and open in the
         # editor.
         #
         scriptFile = dirName + "/.startproject"
         with open(scriptFile, 'w') as f:
             f.write("#!/bin/sh\n\n")
         os.chmod(scriptFile, stat.S_IEXEC | stat.S_IRUSR | stat.S_IWUSR)
         if (_THIRDPARTY_PLUGINS_DIR +
                 "/OpenWithEditor") in _get_thirdparty_plugins():
             self.pane.run_command("my_open_with_editor",
                                   args={'url': as_url(scriptFile)})
         else:
             self.pane.run_command("open_with_editor",
                                   args={'url': as_url(scriptFile)})
     else:
         #
         # Technically, this will never be reached. Just here
         # for completeness.
         #
         show_alert("No directory selected")
예제 #14
0
 def __call__(self, url=None):
     text, ok = show_prompt('Enter Filter e.g. *.txt',
                            default="*.txt",
                            selection_start=2)
     if url is None:
         url = self.pane.get_file_under_cursor() or self.pane.get_path()
     if not is_dir(url):
         url = dirname(url)
     Flat.filtertext = text
     new_url = Flat.scheme + splitscheme(url)[1]
     # Not working yet
     if ok and text:
         self.pane.set_path(new_url + '?' + text)
예제 #15
0
    def _get_default_recipient(self):
        config = load_json(CONFIG_FILE_NAME, default={})

        if config and "default_recipient" in config:
            return config["default_recipient"]

        else:
            default_recipient, ok = show_prompt(
                'First run - please enter default recipient')
            config['default_recipient'] = default_recipient
            save_json(CONFIG_FILE_NAME, config)

            return default_recipient
예제 #16
0
def set_tortoisesvnproc_install_path():
    new_tortoisesvnproc_filepath, ok = show_prompt(
        'Enter full path to TortoiseSVNProc.exe program here',
        default=get_current_tortoisesvnproc_install_path(),
        selection_start=0,
        selection_end=None)

    if not ok:
        return False

    if not exists(as_url(new_tortoisesvnproc_filepath)):
        show_alert('Path to TortoiseSVNProc given is invalid')
        return False

    _TORTOISEPROCPATH = new_tortoisesvnproc_filepath
    save_json(_TORTOISEPROCCONFIGFILE, {'path': new_tortoisesvnproc_filepath})
    return True
예제 #17
0
def set_sublime_install_path():
    new_sublime_filepath, ok = show_prompt(
        'Enter full path to Sublime Text program here',
        default=get_current_sublime_install_path(),
        selection_start=0,
        selection_end=None)

    if not ok:
        return False

    if not exists(as_url(new_sublime_filepath)):
        show_alert('Path to Sublime Text given is invalid')
        return False

    _SUBLIMETEXTPATH = new_sublime_filepath
    save_json(_SUBLIMETEXTCONFIGFILE, {'path': new_sublime_filepath})
    return True
예제 #18
0
 def __call__(self):
     global DBDATA
     show_status_message('NotePad (A)ppend or Over(w)rite')
     npdata = load_json('CopyToNotePad.json')
     if npdata is None:
         npdata = dict()
         npdata['number'] = 3
         npdata['save'] = 'a'
     npappend, result = show_prompt(
         "The 'a'ppend or 'w'rite:")
     if not npappend:
         npappend = 'a'
     npappend = npappend.lower()
     if (npappend != 'a') and (npappend != 'w'):
         npappend = 'a'
     npdata['save'] = npappend
     save_json('CopyToNotePad.json', npdata)
     clear_status_message()
예제 #19
0
 def __call__(self):
     # get_chosen_files() returns the files selected by the user
     # (="red" files). If no files are selected, the file under the cursor
     # is returned:
     chosen_files = self.get_chosen_files()
     if chosen_files:  # equivalent to `len(chosen_files) > 0`
         ndnam, okay = show_prompt("Directory Name?")
         # Check ndnam as well because it could be empty
         if ndnam and okay:
             numf = 0
             newdirp = join(self.pane.get_path(), ndnam)
             if not self.isADir(newdirp):
                 # Create directory and its parent directories:
                 makedirs(newdirp)
             for filep in chosen_files:
                 move(filep, join(newdirp, basename(filep)))
                 numf += 1
             show_alert('%d items were moved!' % numf)
     else:
         show_alert("No files or directories selected")
예제 #20
0
 def setSizeDivisor(self, value_default):
     _accept = ('1000', '1024')
     value_cfg = str(int(self.cfgCurrent['SizeDivisor']))
     prompt_msg      = "Please enter the file size divisor ('1000' or '1024') to display file size\nin a decimal (1k=1000=10³) or binary (1k=1024=2¹⁰) format" + '\n'\
         + "or leave the field empty to restore the default ("+str(value_default) +'):'
     selection_start = 2
     value_new = ''
     while value_new not in _accept:
         value_new, ok = show_prompt(prompt_msg, value_cfg, selection_start)
         value_cfg = value_new  # preserve user input on multiple edits
         if not ok:
             show_status_message("StatusBarExtended: setup canceled")
             return
         if value_new.strip(' ') == '':
             self.cfgCurrent['SizeDivisor'] = value_default
             return
         elif value_new not in _accept:
             show_alert("You entered\n" + value_new +'\n'\
                 + "but the only acceptable values are:\n1000\n1024")
     self.cfgCurrent['SizeDivisor'] = float(value_new)
예제 #21
0
 def __call__(self):
     global DBDATA
     show_status_message('NotePad Number')
     npdata = load_json('CopyToNotePad.json')
     if npdata is None:
         npdata = dict()
         npdata['number'] = 3
         npdata['save'] = 'a'
     npnum, result = show_prompt(
         "The NotePad Number to use:")
     if not npnum:
         npnum = 3
     else:
         npnum = int(npnum)
     if npnum < 1:
         npnum = 1
     elif npnum > 9:
         npnum = 9
     npdata['number'] = npnum
     save_json('CopyToNotePad.json', npdata)
     clear_status_message()
예제 #22
0
    def __call__(self):
        cfg = SingletonConfig()
        self.cfgCurrent, exit_status = cfg.loadConfig()
        if self.cfgCurrent is None:
            return

        prompt_msg = "Please enter any combination of the the &u&n&d&e&r&l&i&n&e&d numbers/letters" +'\n'\
            + "to configure the corresponding option(s)" +'\n'\
            + "# \tOption \t\tDescription" +'\n'\
            + "&0. \t&a&l&l\t\t"       + "Configure all the options"                    +'\n'\
            + "&1. \t&Enabled\t\t"     + "Enable/Disable this plugin"                   +'\n'\
            + "&2. \tSize&Divisor\t"   + "File size format: decimal or binary"          +'\n'\
            + "&3. \tMax&Glob\t\t"     + "Skip folders with as many items"              +'\n'\
            + "&4. \tSymbol&Pane\t"    + "Left/Right pane symbol"                       +'\n'\
            + "&5. \tSymbol&HiddenF\t" + "Hidden files Shown/Hidden symbol"             +'\n'\
            + "&6. \tHideD&otfile\t"   + "Treat .dotfiles as hidden files on Windows"   +'\n'\
            + "&7. \t&Justify\t\t"     + "Minimum width of the Folder/File/Size values" +'\n'\
            + '\n'
        value_new, ok = show_prompt(prompt_msg)
        if not ok:
            show_status_message("StatusBarExtended: setup canceled")
            return
        if any(x in value_new.casefold() for x in ('1', 'e', '0', 'all')):
            self.setEnabled(cfg.Default['Enabled'])
        if any(x in value_new.casefold() for x in ('2', 'd', '0', 'all')):
            self.setSizeDivisor(cfg.Default['SizeDivisor'])
        if any(x in value_new.casefold() for x in ('3', 'g', '0', 'all')):
            self.setMaxGlob(cfg.Default['MaxGlob'])
        if any(x in value_new.casefold() for x in ('4', 'p', '0', 'all')):
            self.setSymbolPane(cfg.Default['SymbolPane'])
        if any(x in value_new.casefold() for x in ('5', 'h', '0', 'all')):
            self.setSymbolHiddenF(cfg.Default['SymbolHiddenF'])
        if any(x in value_new.casefold() for x in ('6', 'o', '0', 'all')):
            self.setHideDotfile(cfg.Default['HideDotfile'])
        if any(x in value_new.casefold() for x in ('7', 'j', '0', 'all')):
            self.setJustify(cfg.Default['Justify'])
        cfg.saveConfig(self.cfgCurrent)
        run_application_command('view_configuration_status_bar_extended')
예제 #23
0
    def __call__(self, url=None):

        # This will get set if the automagically determined destination
        #  directory exists, and will always be the default name when prompted
        #  to manually choose a destination directory name.
        originalName = None

        if url is None:
            workingFile = self.pane.get_file_under_cursor()
            if workingFile:
                url = workingFile

        fileName = basename(url)

        if _CHECK_EXTENSION:

            try:
                extension = fileName[fileName.rindex('.'):]
            except ValueError:
                show_alert("Failed to determine extension, aborting!")
                return

            if not extension in _SUPPORTED_EXTENSIONS:
                show_alert("Unsupported extension, aborting!")
                return

            newDirName = fileName[0:fileName.rindex('.')]

        else:

            try:
                newDirName = fileName[0:fileName.rindex('.')]

            except ValueError:

                message = "Archive has no extension.\n"
                message += "Click 'YES' to enter a name "
                message += "for the destination directory.\n"
                message += "Click 'NO' to use the archive name.\n"
                message += "Click 'ABORT' to abort extraction."

                choice = show_alert(message,
                                    buttons=YES | NO | ABORT,
                                    default_button=ABORT)

                if choice == YES:
                    newDirName, ok = show_prompt("Destination directory:",
                                                 default=fileName)
                    if not (newDirName and ok):
                        return

                elif choice == NO:
                    newDirName = fileName

                else:
                    return

        oppositePane = _get_opposite_pane(self.pane)
        oppositePaneUrl = oppositePane.get_path()
        oppositePaneScheme, _ = splitscheme(resolve(oppositePaneUrl))
        if oppositePaneScheme != 'file://':
            show_alert("Can't extract to %s, aborting!" % oppositeScheme)
            return

        newDirUrl = join(oppositePaneUrl, newDirName)

        while exists(newDirUrl):
            if not originalName:
                originalName = newDirName
            message = newDirName + " already exists!\nEnter a different name?"
            choice = show_alert(message,
                                buttons=YES | ABORT,
                                default_button=ABORT)
            if choice == YES:
                newDirName, ok = show_prompt("Destination directory:",
                                             default=newDirName)
                if not (newDirName and ok):
                    continue
                else:
                    newDirUrl = join(oppositePaneUrl, newDirName)

        try:
            mkdir(newDirUrl)
        except (FileNotFoundError, NotImplementedError):
            message = "Failed to create directory '"
            message += newDirName + "', aborting!"
            show_alert(message)
            return

        archive = as_human_readable(url)
        destDir = as_human_readable(newDirUrl)

        submit_task(ExtractArchive(archive, destDir))

        return
예제 #24
0
    def loadConfig(cls):
        """Check StatusBarExtended.json for consistency/completeness, restore defaults on fail"""
        msg_t = cls.msgTimeout
        if hasattr(cls, 'locked_update'):
            show_status_message(
                "StatusBarExtended: waiting for the config files to be updated, try again later..."
            )
            return None, 'UpdateInProgress'
        cls.locked_update = True  # ensure globally unique 'loadConfig' run so that e.g. we don't ask a user multiple times to delete corrupted configs

        cfgCurrent = load_json(
            'StatusBarExtended.json'
        )  # within one fman session, it is guaranteed that multiple calls to load_json(...) with the same JSON name always return the same object, so save {} when deleting the files to force reload
        if type(cfgCurrent) not in (type(dict()), type(None)):
            # delete config files, fman's save_json can't replace types
            config_files = ['StatusBarExtended.json']
            config_files.append('StatusBarExtended (' + PLATFORM + ').json')
            user_settings_url = join(as_url(DATA_DIRECTORY), 'Plugins', 'User',
                                     'Settings')
            user_input_allow = ''
            prompt_msg_full = ''
            corrupt_config = []
            for f in config_files:
                f_url = join(user_settings_url, f)
                f_path = as_path(f_url)

                if not exists(f_url):
                    continue

                excerpt = str(load_json(f_path))[:100]
                prompt_msg = f_path \
                    + "\n  that begins with:"\
                    + "\n  " + excerpt
                corrupt_config.append({})
                corrupt_config[-1]['url'] = f_url
                corrupt_config[-1]['path'] = f_path
                corrupt_config[-1]['prompt_msg'] = prompt_msg

            corrupt_count = len(corrupt_config)
            if corrupt_count:  # delete corrupt config files with the user's permission
                prompt_msg_full += "Please enter 'y' or 'yes' or '1' (without the quotes) to delete " + str(corrupt_count) + " corrupt plugin config file" \
                    + ("\n" if corrupt_count==1 else "s\n") \
                    + "with incompatible data type " + str(type(cfgCurrent)) + '\n'\
                    + "(all settings will be reset to their defaults)\n"
                for corrupt_file_dict in corrupt_config:
                    prompt_msg_full += '\n' + corrupt_file_dict[
                        'prompt_msg'] + '\n'
                user_input_allow, ok = show_prompt(prompt_msg_full,
                                                   default=user_input_allow)
                if ok and user_input_allow in ('y', 'yes', '1'):
                    _reset = False
                    for corrupt_file_dict in corrupt_config:
                        f_url = corrupt_file_dict['url']
                        f_path = corrupt_file_dict['path']
                        try:
                            move_to_trash(f_url)
                        except Exception as e:
                            show_status_message(
                                "StatusBarExtended: failed to move to trash — "
                                + f_path + " — with exception " + repr(e),
                                msg_t)
                            pass

                        if not exists(f_url):
                            show_status_message(
                                "StatusBarExtended: moved to trash — " +
                                f_path, msg_t)
                            _reset = True
                        else:
                            show_status_message(
                                "StatusBarExtended: failed to move to trash, deleting — "
                                + f_path, msg_t)
                            try:
                                delete(f_url)
                            except Exception as e:
                                show_status_message(
                                    "StatusBarExtended: failed to delete — " +
                                    f_path + " — with exception " + repr(e),
                                    msg_t)
                                pass

                            if not exists(f_url):
                                show_status_message(
                                    "StatusBarExtended: deleted — " + f_path,
                                    msg_t)
                                _reset = True
                            else:
                                show_alert(
                                    "StatusBarExtended: failed to move to trash or delete — "
                                    + f_path + "\nPlease, delete it manually")
                                del cls.locked_update
                                return None, 'ConfigDeleteFail'
                    if _reset == True:  # can save_json only when both files are deleted, otherwise there is a format mismatch ValueError
                        cls.saveConfig({})
                        cfgCurrent = load_json('StatusBarExtended.json')
                else:  # user canceled or didn't enter y/1
                    show_status_message(
                        "StatusBarExtended: you canceled the deletion of the corrupted config files",
                        msg_t)
                    del cls.locked_update
                    return None, 'Canceled'
            else:  # can't find the config files
                show_alert("StatusBarExtended: can't find the corrupt config files:\n" \
                    + str(config_files) + "\n @ " + as_path(user_settings_url) \
                    + "\nMaybe their default location changed, please, delete them manually")
                del cls.locked_update
                return None, 'CorruptConfigNotFound'

        reload = False
        if (cfgCurrent is None) or (
                cfgCurrent == {}
        ):  # empty file or empty dictionary (corrupt configs deleted and replaced with {}), save defaults to the config file
            cfgCurrent = dict()
            for key in cls.Default:
                cfgCurrent[key] = cls.Default[key]
            reload = True

        if type(cfgCurrent) is dict:
            for key in cls.Default:  # Fill in missing default values (e.g. in older installs)
                if key not in cfgCurrent:
                    cfgCurrent[key] = cls.Default[key]
                    reload = True
        if reload:
            cls.saveConfig(cfgCurrent)
            cfgCurrent = load_json('StatusBarExtended.json')

        if type(cfgCurrent) is dict:  # check for still missing keys
            missing_keys = []
            for key in cls.Default:
                if key in cfgCurrent:
                    continue
                missing_keys.append(key)
            if len(missing_keys):
                show_status_message(
                    "StatusBarExtended: config files are missing some required keys:"
                    + str(missing_keys) + " Maybe try to reload?", msg_t)
                del cls.locked_update
                return None, 'MissingKeys'
            else:
                del cls.locked_update
                return cfgCurrent, 'Success'
        else:
            show_status_message(
                "StatusBarExtended: couldn't fix config files, maybe try to reload?",
                msg_t)
            del cls.locked_update
            return None, 'UnknownError'
예제 #25
0
 def setJustify(self, value_default):
     value_cfg_in = self.cfgCurrent['Justify']
     value_cfg = " ".join(str(val) for val in value_cfg_in.values())
     val_def_fmt = " ".join(str(val) for val in value_default.values())
     max_val = 11
     prompt_msg      = "Please enter three natural numbers, each <= "+str(max_val)+", separated by space, to set the minimum width of" +'\n'\
         + "the folder, file, and size indicators respectively." +'\n'\
         + "e.g. with a min width=2, 1 in 1 and 21 will align, but not in 321:" +'\n'\
         + " 1" +'\n'\
         + "21" +'\n'\
         + "321" +'\n'\
         + "or enter '0' to restore an individual default" +'\n'\
         + "or leave the field empty to restore all the defaults ("+val_def_fmt+"):"
     selection_start = 0
     selection_end = 1
     value_new = ''
     value_new_list = []
     _len = len(value_new_list)
     len_def = len(value_default)
     above_max = False
     while (not all([isNat0(v) for v in value_new_list])) \
         or above_max \
         or _len != len_def:
         value_new, ok = show_prompt(prompt_msg, value_cfg, selection_start,
                                     selection_end)
         value_cfg = value_new  # preserve user input on multiple edits
         if not ok:
             show_status_message("StatusBarExtended: setup canceled")
             return
         if value_new.strip(' ') == '':
             self.cfgCurrent['Justify'] = value_default
             return
         value_new_nosp = ' '.join(
             value_new.split())  # replace multiple spaces with 1
         value_new_list = value_new_nosp.split(' ')  # split by space
         _len = len(value_new_list)
         if not all([isInt(v) for v in value_new_list]):
             show_alert("You entered\n" + value_new +'\n'\
                 + "I parsed it as " + str(value_new_list) + " with " + str(_len) + " element" + ("" if _len==1 else "s") +'\n'\
                 + "but I couldn't parse all elements as integers")
         elif not all([isNat0(v) for v in value_new_list]):
             show_alert("You entered\n" + value_new +'\n'\
                 + "I parsed it as " + str(value_new_list) + " with " + str(_len) + " element" + ("" if _len==1 else "s") +'\n'\
                 + "but couldn't parse all elements as non-negative integers 0,1,2,3–∞")
         elif True in [int(v) > max_val for v in value_new_list]:
             above_max = True
             show_alert("You entered\n" + value_new +'\n'\
                 + "I parsed it as " + str(value_new_list) + " with " + str(_len) + " element" + ("" if _len==1 else "s") +'\n'\
                 + "but the maximum value of each number is " + str(max_val))
             continue
         elif all([int(v) <= max_val for v in value_new_list]):
             above_max = False
         if _len != len_def:
             show_alert("You entered\n" + value_new +'\n'\
                 + "I parsed it as " + str(value_new_list) + " with " + str(_len) + " element" + ("" if _len==1 else "s") +'\n'\
                 + "but was expecting " + str(len_def) + " elements")
     for i, key in enumerate(value_default):
         if value_new_list[i] == '0':
             self.cfgCurrent['Justify'][key] = value_default[key]
         else:
             self.cfgCurrent['Justify'][key] = int(value_new_list[i])