          sep.pack(fill=tk.X, pady=(5, 0), side=tk.TOP)
          logger.debug("Added frame seperator")
  
  
  class OptionsFrame(ttk.Frame):  # pylint:disable=too-many-ancestors
      """ Options Frame - Holds the Options for each command """
  
      def __init__(self, parent):
          logger.debug("Initializing %s", self.__class__.__name__)
          super().__init__(parent)
          self.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
  
          self.command = parent.command
  
          self.canvas = tk.Canvas(self, bd=0, highlightthickness=0)
          self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
  
          self.optsframe = ttk.Frame(self.canvas)
          self.group_frames = dict()
          self.optscanvas = self.canvas.create_window((0, 0),
                                                      window=self.optsframe,
                                                      anchor=tk.NW)
          self.chkbtns = self.checkbuttons_frame()
  
          self.build_frame()
          cli_opts = get_config().cli_opts
          cli_opts.set_context_option(self.command)
          logger.debug("Initialized %s", self.__class__.__name__)
  
      def checkbuttons_frame(self):
          """ Build and format frame for holding the check buttons """
          logger.debug("Add Options CheckButtons Frame")
          container = ttk.Frame(self.optsframe)
  
          lbl = ttk.Label(container, text="Options", width=16, anchor=tk.W)
          lbl.pack(padx=5, pady=5, side=tk.LEFT, anchor=tk.N)
  
          chkframe = ttk.Frame(container)
          chkleft = ttk.Frame(chkframe, name="leftFrame")
          chkright = ttk.Frame(chkframe, name="rightFrame")
  
          chkframe.pack(fill=tk.X, expand=True)
          chkleft.pack(padx=5, pady=5, fill=tk.X, expand=True, side=tk.LEFT, anchor=tk.N)
          chkright.pack(padx=5, pady=5, fill=tk.X, expand=True, side=tk.RIGHT, anchor=tk.N)
          logger.debug("Added Options CheckButtons Frame")
  
          return container, chkframe
  
      def build_frame(self):
          """ Build the options frame for this command """
          logger.debug("Add Options Frame")
          self.add_scrollbar()
          self.canvas.bind("<Configure>", self.resize_frame)
  
          cli_opts = get_config().cli_opts
          for option in cli_opts.gen_command_options(self.command):
              group = option["group"]
              frame = self.optsframe
              if group is not None:
                  group = group.lower()
                  if self.group_frames.get(group, None) is None:
                      group_frame = ttk.LabelFrame(self.optsframe, text=group.title())
                      group_frame.pack(side=tk.TOP, fill=tk.X, padx=5, pady=5)
                      self.group_frames[group] = group_frame
                  frame = self.group_frames[group]
  
              optioncontrol = OptionControl(self.command,
                                            option,
                                            frame,
                                            self.chkbtns[1])
              optioncontrol.build_full_control()
  
          if self.chkbtns[1].winfo_children():
              self.chkbtns[0].pack(side=tk.BOTTOM, fill=tk.X, expand=True)
          logger.debug("Added Options Frame")
  
      def add_scrollbar(self):
          """ Add a scrollbar to the options frame """
          logger.debug("Add Options Scrollbar")
          scrollbar = ttk.Scrollbar(self, command=self.canvas.yview)
          scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
          self.canvas.config(yscrollcommand=scrollbar.set)
          self.optsframe.bind("<Configure>", self.update_scrollbar)
          logger.debug("Added Options Scrollbar")
  
      def update_scrollbar(self, event):  # pylint:disable=unused-argument
          """ Update the options frame scrollbar """
          self.canvas.configure(scrollregion=self.canvas.bbox("all"))
  
      def resize_frame(self, event):
          """ Resize the options frame to fit the canvas """
          logger.debug("Resize Options Frame")
          canvas_width = event.width
          self.canvas.itemconfig(self.optscanvas, width=canvas_width)
          logger.debug("Resized Options Frame")
  
  
  class OptionControl():
      """ Build the correct control for the option parsed and place it on the
      frame """
  
      def __init__(self, command, option, option_frame, checkbuttons_frame):
          logger.debug("Initializing %s", self.__class__.__name__)
          self.command = command
          self.option = option
          self.option_frame = option_frame
          self.chkbtns = checkbuttons_frame
          logger.debug("Initialized %s", self.__class__.__name__)
  
      def build_full_control(self):
          """ Build the correct control type for the option passed through """
          logger.debug("Build option control")
          ctl = self.option["control"]
          ctltitle = self.option["control_title"]
          sysbrowser = self.option["filesystem_browser"]
          ctlhelp = self.format_help(ctltitle)
          dflt = self.option.get("default", "")
          if self.option.get("nargs", None) and isinstance(dflt, (list, tuple)):
              dflt = ' '.join(str(val) for val in dflt)
          if ctl == ttk.Checkbutton:
              dflt = self.option.get("default", False)
          choices = self.option["choices"] if ctl in(ttk.Combobox, ttk.Radiobutton) else None
          min_max = self.option["min_max"] if ctl == ttk.Scale else None
  
          ctlframe = self.build_one_control_frame()
  
          if ctl != ttk.Checkbutton:
              self.build_one_control_label(ctlframe, ctltitle)
  
          ctlvars = (ctl, ctltitle, dflt, ctlhelp)
          self.option["value"] = self.build_one_control(ctlframe,
                                                        ctlvars,
                                                        choices,
                                                        min_max,
                                                        sysbrowser)
          logger.debug("Built option control")
  
      def format_help(self, ctltitle):
          """ Format the help text for tooltips """
          logger.debug("Format control help: '%s'", ctltitle)
          ctlhelp = self.option.get("help", "")
          if ctlhelp.startswith("R|"):
              ctlhelp = ctlhelp[2:].replace("\nL|", "\n - ").replace("\n", "\n\n")
          else:
              ctlhelp = " ".join(ctlhelp.split())
          ctlhelp = ctlhelp.replace("%%", "%")
          ctlhelp = ". ".join(i.capitalize() for i in ctlhelp.split(". "))
          ctlhelp = ctltitle + " - " + ctlhelp
          logger.debug("Formatted control help: (title: '%s', help: '%s'", ctltitle, ctlhelp)
          return ctlhelp
  
      def build_one_control_frame(self):
          """ Build the frame to hold the control """
          logger.debug("Build control frame")
          frame = ttk.Frame(self.option_frame)
          frame.pack(fill=tk.X, expand=True)
          logger.debug("Built control frame")
          return frame
  
      @staticmethod
      def build_one_control_label(frame, control_title):
          """ Build and place the control label """
          logger.debug("Build control label: '%s'", control_title)
          lbl = ttk.Label(frame, text=control_title, width=16, anchor=tk.W)
          lbl.pack(padx=5, pady=5, side=tk.LEFT, anchor=tk.N)
          logger.debug("Built control label: '%s'", control_title)
  
      def build_one_control(self, frame, controlvars, choices, min_max, sysbrowser):
          """ Build and place the option controls """
          logger.debug("Build control: (controlvars: %s, choices: %s, min_max: %s, sysbrowser: %s",
                       controlvars, choices, min_max, sysbrowser)
          control, control_title, default, helptext = controlvars
          default = default if default is not None else ""
  
          var = tk.BooleanVar(frame) if control == ttk.Checkbutton else tk.StringVar(frame)
          var.set(default)
  
          if sysbrowser:
              self.add_browser_buttons(frame, sysbrowser, var)
  
          if control == ttk.Checkbutton:
              self.checkbutton_to_checkframe(control, control_title, var, helptext)
          elif control == ttk.Radiobutton:
              self.radio_control(frame, control_title, var, choices, helptext)
          elif control == ttk.Scale:
              self.slider_control(control, frame, var, min_max, helptext)
          else:
              self.control_to_optionsframe(control, frame, var, choices, helptext)
          logger.debug("Built control: '%s'", control_title)
          return var
  
      @staticmethod
      def radio_control(frame, control_title, var, choices, helptext):
          """ Create a group of radio buttons """
          logger.debug("Adding radio group: %s", control_title)
          radio_frame_left = ttk.Frame(frame)
          radio_frame_middle = ttk.Frame(frame)
          radio_frame_right = ttk.Frame(frame)
  
          radio_frame_left.pack(padx=5, pady=5, fill=tk.X, expand=True, side=tk.LEFT, anchor=tk.N)
          radio_frame_middle.pack(padx=5, pady=5, fill=tk.X, expand=True, side=tk.LEFT, anchor=tk.N)
          radio_frame_right.pack(padx=5, pady=5, fill=tk.X, expand=True, side=tk.RIGHT, anchor=tk.N)
  
          for idx, choice in enumerate(choices):
              pos = idx + 1
              if pos % 3 == 0:
                  radio_frame = radio_frame_right
              elif (pos + 1) % 3 == 0:
                  radio_frame = radio_frame_middle
              else:
                  radio_frame = radio_frame_left
  
              ctl = ttk.Radiobutton(radio_frame, text=choice.title(), value=choice, variable=var)
              ctl.pack(anchor=tk.W)
              Tooltip(ctl, text=helptext, wraplength=920)
          logger.debug("Added radio group: '%s'", control_title)
  
      def checkbutton_to_checkframe(self, control, control_title, var, helptext):
          """ Add checkbuttons to the checkbutton frame """
          logger.debug("Add control checkframe: '%s'", control_title)
          leftframe = self.chkbtns.children["leftFrame"]
          rightframe = self.chkbtns.children["rightFrame"]
          chkbtn_count = len({**leftframe.children, **rightframe.children})
  
          frame = leftframe if chkbtn_count % 2 == 0 else rightframe
  
          ctl = control(frame, variable=var, text=control_title)
          ctl.pack(side=tk.TOP, anchor=tk.W)
  
          Tooltip(ctl, text=helptext, wraplength=200)
          logger.debug("Added control checkframe: '%s'", control_title)
  
      def slider_control(self, control, frame, tk_var, min_max, helptext):
          """ A slider control with corresponding Entry box """
          logger.debug("Add slider control to Options Frame: %s", control)
          d_type = self.option.get("type", float)
          rnd = self.option.get("rounding", 2) if d_type == float else self.option.get("rounding", 1)
  
          tbox = ttk.Entry(frame, width=8, textvariable=tk_var, justify=tk.RIGHT)
          tbox.pack(padx=(0, 5), side=tk.RIGHT)
          ctl = control(
              frame,
              variable=tk_var,
              command=lambda val, var=tk_var, dt=d_type, rn=rnd, mm=min_max:
              set_slider_rounding(val, var, dt, rn, mm))
          ctl.pack(padx=5, pady=5, fill=tk.X, expand=True)
          rc_menu = ContextMenu(ctl)
          rc_menu.cm_bind()
          ctl["from_"] = min_max[0]
          ctl["to"] = min_max[1]
  
          Tooltip(ctl, text=helptext, wraplength=920)
          Tooltip(tbox, text=helptext, wraplength=920)
          logger.debug("Added slider control to Options Frame: %s", control)
  
      @staticmethod
      def control_to_optionsframe(control, frame, var, choices, helptext):
          """ Standard non-check buttons sit in the main options frame """
          logger.debug("Add control to Options Frame: %s", control)
          ctl = control(frame, textvariable=var)
          ctl.pack(padx=5, pady=5, fill=tk.X, expand=True)
          rc_menu = ContextMenu(ctl)
          rc_menu.cm_bind()
          if control == ttk.Combobox:
              logger.debug("Adding combo choices: %s", choices)
              ctl["values"] = [choice for choice in choices]
          Tooltip(ctl, text=helptext, wraplength=920)
          logger.debug("Added control to Options Frame: %s", control)
  
      def add_browser_buttons(self, frame, sysbrowser, filepath):
          """ Add correct file browser button for control """
          logger.debug("Adding browser buttons: (sysbrowser: '%s', filepath: '%s'",
                       sysbrowser, filepath)
          for browser in sysbrowser:
              img = get_images().icons[browser]
              action = getattr(self, "ask_" + browser)
              filetypes = self.option.get("filetypes", "default")
              fileopn = ttk.Button(frame,
                                   image=img,
                                   command=lambda cmd=action: cmd(filepath, filetypes))
              fileopn.pack(padx=(0, 5), side=tk.RIGHT)
              logger.debug("Added browser buttons: (action: %s, filetypes: %s",
                           action, filetypes)
  
      @staticmethod
      def ask_folder(filepath, filetypes=None):
          """ Pop-up to get path to a directory
              :param filepath: tkinter StringVar object
              that will store the path to a directory.
              :param filetypes: Unused argument to allow
              filetypes to be given in ask_load(). """
          dirname = FileHandler("dir", filetypes).retfile
          if dirname:
              logger.debug(dirname)
              filepath.set(dirname)
  
      @staticmethod
      def ask_load(filepath, filetypes):
          """ Pop-up to get path to a file """
          filename = FileHandler("filename", filetypes).retfile
          if filename:
              logger.debug(filename)
              filepath.set(filename)
  
      @staticmethod
      def ask_load_multi(filepath, filetypes):
          """ Pop-up to get path to a file """
          filenames = FileHandler("filename_multi", filetypes).retfile
          if filenames:
              final_names = " ".join("\"{}\"".format(fname) for fname in filenames)
              logger.debug(final_names)
              filepath.set(final_names)
  
      @staticmethod
      def ask_save(filepath, filetypes=None):
          """ Pop-up to get path to save a new file """
          filename = FileHandler("savefilename", filetypes).retfile
          if filename:
              logger.debug(filename)
              filepath.set(filename)
  
      @staticmethod
      def ask_nothing(filepath, filetypes=None):  # pylint:disable=unused-argument
          """ Method that does nothing, used for disabling open/save pop up """
          return
  
      def ask_context(self, filepath, filetypes):
          """ Method to pop the correct dialog depending on context """
          logger.debug("Getting context filebrowser")
          selected_action = self.option["action_option"].get()
          selected_variable = self.option["dest"]
          filename = FileHandler("context",
                                 filetypes,
                                 command=self.command,
                                 action=selected_action,
                                 variable=selected_variable).retfile
          if filename:
              logger.debug(filename)
              filepath.set(filename)
  
  
  class ActionFrame(ttk.Frame):  # pylint:disable=too-many-ancestors
      """Action Frame - Displays action controls for the command tab """
  
      def __init__(self, parent):
          logger.debug("Initializing %s: (command: '%s')", self.__class__.__name__, parent.command)
 
</s> remove     def control_to_optionsframe(self, choices):
 </s> add     def control_to_optionsframe(self, choices, sysbrowser): </s> remove     def add_info(self):
        """ Plugin information """
        info_frame = ttk.Frame(self.optsframe)
        info_frame.pack(fill=tk.X, expand=True)
        lbl = ttk.Label(info_frame, text="About:", width=20, anchor=tk.W)
        lbl.pack(padx=5, pady=5, side=tk.LEFT, anchor=tk.N)
        info = ttk.Label(info_frame, text=self.header_text)
        info.pack(padx=5, pady=5, fill=tk.X, expand=True)
        info.bind("<Configure>", adjust_wraplength)
 </s> add     def checkbuttons_frame(self, frame):
        """ Build and format frame for holding the check buttons
            if is_master then check buttons will be placed in a LabelFrame
            otherwise in a standard frame """
        logger.debug("Add Options CheckButtons Frame")
        chk_frame = ttk.Frame(frame, name="chkbuttons")
        holder = AutoFillContainer(chk_frame, self.radio_columns)
        logger.debug("Added Options CheckButtons Frame")
        return holder


class AutoFillContainer():
    """ A container object that autofills columns """
    def __init__(self, parent, columns):
        logger.debug("Initializing: %s: (parent: %s, columns: %s)", self.__class__.__name__,
                     parent, columns)
        self.parent = parent
        self.columns = columns
        self._items = 0
        self._idx = 0
        self.subframes = self.set_subframes()
        logger.debug("Initialized: %s: (items: %s)", self.__class__.__name__, self.items)

    @property
    def items(self):
        """ Returns the number if items held in this containter """
        return self._items

    @property
    def subframe(self):
        """ Returns the next subframe to be populated """
        frame = self.subframes[self._idx]
        next_idx = self._idx + 1 if self._idx + 1 != self.columns else 0
        logger.debug("current_idx: %s, next_idx: %s", self._idx, next_idx)
        self._idx = next_idx
        return frame

    @property
    def last_subframe(self):
        """ Returns the last column """
        return self.subframes[self.columns - 1]

    def set_subframes(self):
        """ Set a subrame for each requested column """
        subframes = []
        for idx in range(self.columns):
            if self.columns != 1:
                name = "{}_{}".format(self.parent.winfo_name(), idx)
                subframe = ttk.Frame(self.parent, name=name)
                subframe.pack(padx=5, pady=5, side=tk.LEFT, anchor=tk.N, expand=True, fill=tk.X)
                subframes.append(subframe)
                logger.debug("Added subframe: %s", name)
            else:
                subframes.append(self.parent)
                logger.debug("Using parent as subframe: %s", self.parent.winfo_name())
            self._items += 1
        return subframes </s> remove             group_frame = ttk.LabelFrame(self.optsframe, text=group.title())
            group_frame.pack(side=tk.TOP, fill=tk.X, padx=5, pady=5)
            self.group_frames[group] = group_frame
        return self.group_frames[group]
 </s> add             is_master = group == "_master"
            opts_frame = self.optsframe.subframe
            group_frame = ttk.LabelFrame(opts_frame,
                                         text="" if is_master else group.title(),
                                         name=group.lower())
            group_frame.pack(side=tk.TOP, fill=tk.X, padx=5, pady=5, anchor=tk.NW)

            self.group_frames[group] = dict(frame=group_frame,
                                            chkbtns=self.checkbuttons_frame(group_frame))
        group_frame = self.group_frames[group]
        return group_frame </s> remove     def set_control(self, option):
        """ Set the control and filesystem browser to use for each option """
        sysbrowser = None
        group = option.get("group", None)
        action = option.get("action", None)
        action_option = option.get("action_option", None)
        filetypes = option.get("filetypes", None)
        ctl = ttk.Entry
        if action in (cli.FullPaths,
                      cli.DirFullPaths,
                      cli.FileFullPaths,
                      cli.FilesFullPaths,
                      cli.DirOrFileFullPaths,
                      cli.SaveFileFullPaths,
                      cli.ContextFullPaths):
            sysbrowser, filetypes = self.set_sysbrowser(action,
                                                        filetypes,
                                                        action_option)
        elif option.get("min_max", None):
            ctl = ttk.Scale
        elif option.get("action", "") == cli.Radio:
            ctl = ttk.Radiobutton
        elif option.get("choices", "") != "":
            ctl = ttk.Combobox
        elif option.get("action", "") == "store_true":
            ctl = ttk.Checkbutton
        return ctl, sysbrowser, filetypes, action_option, group
 </s> add     @staticmethod
    def get_data_type(opt):
        """ Return a datatype for passing into control_helper.py to get the correct control """
        if opt.get("type", None) is not None and isinstance(opt["type"], type):
            retval = opt["type"]
        elif opt.get("action", "") in ("store_true", "store_false"):
            retval = bool
        else:
            retval = str
        return retval </s> remove         ctl = ttk.Frame(self.frame)
        frames = list()
        for _ in range(columns):
            frame = ttk.Frame(ctl)
            frame.pack(padx=5, pady=5, fill=tk.X, expand=True, side=tk.LEFT, anchor=tk.N)
            frames.append(frame)

 </s> add         ctl = ttk.LabelFrame(self.frame, text=self.title.replace("_", " ").title())
        radio_holder = AutoFillContainer(ctl, columns)
          sep.pack(fill=tk.X, pady=(5, 0), side=tk.TOP)
          logger.debug("Added frame seperator")
  
  

















































































































































































































































































































































  class ActionFrame(ttk.Frame):  # pylint:disable=too-many-ancestors
      """Action Frame - Displays action controls for the command tab """
  
      def __init__(self, parent):
          logger.debug("Initializing %s: (command: '%s')", self.__class__.__name__, parent.command)
keep keep keep keep replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace keep keep keep keep keep
 GUI Update


- Launch in fullscreen
- Expand Control Helper. Clean up config popup
- Standardize and overhaul GUI for CliOpts 
lib/gui/command.py