# HG changeset patch # User Rudolph Gutzerhagen # Date 1591972739 14400 # Fri Jun 12 10:38:59 2020 -0400 # Node ID cc4d0c36a5a864f8833913e1865a2d8b8fd920e6 # Parent c26b3ea688eef1da1f5d246ae76816003c7c30b9 UI enhancements: Multiple changes/modifications for: python 2 changes on default branch: o tooltip on recent files menu for full-path information. - aids in distinguishing entries with same short name. o tooltip on editor notebook tab for full-path information. - aids in distinguishing entries with same short name. o stock-config files handling on program load to preserve any user changes to config files (move to archive directory) and allow copy in of freshest version of shipped stock config files. o enhance stock-config menu to handle access to archive directories. - also extended to the 'radio/import from stock config' menu. o fix win32 file dialog start-directory issue in platform.py to support the open of correct or selected directory when requested. Additional note on stock configuration files: In order to prevent overwriting user-copy stock config files with shipped versions, CHIRP skips the copy of those files which already exist in the user-copy directory, leaving any new data in shipped versions in the shipped stock config directory. This function examines each pair (shipped vs user copy) and if different, moves the user-copy stock config to an archive directory, thereby permitting CHIRP to copy the newer shipped version to the user-copy directory. This new archive retains any user-modified data. diff --git a/chirp/platform.py b/chirp/platform.py --- a/chirp/platform.py +++ b/chirp/platform.py @@ -390,7 +390,18 @@ typestrs = None try: - fname, _, _ = win32gui.GetOpenFileNameW(Filter=typestrs) + if not start_dir: + fname, _, _ = win32gui.GetOpenFileNameW(Filter=typestrs) + else: + try: + # set environment variable with start-directory + os.environ['opentodir'] = start_dir + except Exception, e: + LOG.error("Failed to set environment opentodir: %s" % e) + return None + fname, _, _ = win32gui.GetOpenFileNameW( + Filter=typestrs, + InitialDir=os.environ['opentodir']) except Exception, e: LOG.error("Failed to get filename: %s" % e) return None diff --git a/chirp/ui/editorset.py b/chirp/ui/editorset.py --- a/chirp/ui/editorset.py +++ b/chirp/ui/editorset.py @@ -168,6 +168,7 @@ self.modified = (tempname is not None) if tempname: self.filename = tempname + self.tooltip_filename = None self.update_tab() def make_label(self): @@ -199,6 +200,10 @@ text = fn self.text_label.set_text(self.radio.get_name() + ": " + text) + if self.filename != self.tooltip_filename: + self.text_label.set_tooltip_text(self.filename) + self.tooltip_filename = self.filename + def save(self, fname=None): if not fname: diff --git a/chirp/ui/mainapp.py b/chirp/ui/mainapp.py --- a/chirp/ui/mainapp.py +++ b/chirp/ui/mainapp.py @@ -26,6 +26,7 @@ import gtk import gobject import sys +import hashlib from chirp.ui import inputdialog, common from chirp import platform, directory, util @@ -314,7 +315,7 @@ except: return - def do_open(self, fname=None, tempname=None): + def do_open(self, fname=None, tempname=None, start_dir=None): if not fname: types = [(_("All files") + " (*.*)", "*"), (_("CHIRP Radio Images") + " (*.img)", "*.img"), @@ -327,7 +328,10 @@ (_("VX6 Commander Files") + " (*.vx6)", "*.vx6"), (_("VX7 Commander Files") + " (*.vx7)", "*.vx7"), ] - fname = platform.get_platform().gui_open_file(types=types) + if not start_dir: + fname = platform.get_platform().gui_open_file(types=types) + else: + fname = platform.get_platform().gui_open_file(start_dir=start_dir, types=types) if not fname: return @@ -537,15 +541,25 @@ self.menu_ag.remove_action(old_action) file_basename = os.path.basename(fname).replace("_", "__") - action = gtk.Action( - action_name, "_%i. %s" % (i + 1, file_basename), - _("Open recent file {name}").format(name=fname), "") + widget_name = action_name + widget_label = "_%i. %s" % (i + 1, file_basename) + widget_tip = _("Open recent file") + (" {name}").format(name=fname) + action = gtk.Action(action_name, widget_label, widget_tip, "") + action.connect("activate", lambda a, f: self.do_open(f), fname) mid = self.menu_uim.new_merge_id() self.menu_uim.add_ui(mid, path, action_name, action_name, gtk.UI_MANAGER_MENUITEM, False) self.menu_ag.add_action(action) + + widget_uim_path = path + "/" + widget_name + try: + widget_item = self.menu_uim.get_widget(widget_uim_path) + widget_item.set_tooltip_text(widget_tip) + except: + pass + i += 1 def record_recent_file(self, filename): @@ -567,10 +581,12 @@ basepath = platform.get_platform().find_resource("stock_configs") files = glob(os.path.join(basepath, "*.csv")) + dir_timestamped = datetime.now().strftime('%Y%m%d-%H%M%S') for fn in files: if os.path.exists(os.path.join(stock_dir, os.path.basename(fn))): - LOG.info("Skipping existing stock config") - continue + if not self.age_stock_config_file(basepath, stock_dir, os.path.basename(fn), dir_timestamped): + LOG.info("Skipping existing stock config %s" % (fn)) + continue try: shutil.copy(fn, stock_dir) LOG.debug("Copying %s -> %s" % (fn, stock_dir)) @@ -627,6 +643,107 @@ _do_import_action(config) _do_open_action(config) + # set import menuitems for stock config directories + self._do_import_action_on_archive_directories(stock_files_directory=stock_dir) + # set open menuitems for stock config directories + self._do_open_action_on_archive_directories(stock_files_directory=stock_dir) + + + def _do_open_action_on_archive_directories(self, stock_files_directory): + + def add_menu_item(subdir, index_ref): + name = os.path.splitext(os.path.basename(subdir))[0] + action_name = "openstock-dir-%i" % index_ref + path = "/MenuBar/file/openstock" + action = gtk.Action(action_name, + _("Archive: ") + name, + _("Open stock configuration directory") + (" {name}").format(name=name), + "gtk-directory") + action.connect("activate", lambda a, c: self.do_open(start_dir=subdir),"") + mid = self.menu_uim.new_merge_id() + mid = self.menu_uim.add_ui(mid, path, + action_name, action_name, + gtk.UI_MANAGER_MENUITEM, False) + self.menu_ag.add_action(action) + + new_index = 0 + subdirs = glob(os.path.join(stock_files_directory, "*")) + for subdir in subdirs: + if os.path.isdir(subdir): + new_index += 1 + add_menu_item(subdir, new_index) + + + def _do_import_action_on_archive_directories(self, stock_files_directory): + + def add_menu_item(subdir, index_ref): + name = os.path.splitext(os.path.basename(subdir))[0] + action_name = "stock-dir-%i" % index_ref + path = "/MenuBar/radio/stock" + action = gtk.Action(action_name, + _("Archive: ") + name, + _("Import stock configuration directory") + (" {name}").format(name=name), + "gtk-directory") + action.connect("activate", lambda a, c: self.do_import(start_dir=subdir),"") + mid = self.menu_uim.new_merge_id() + mid = self.menu_uim.add_ui(mid, path, + action_name, action_name, + gtk.UI_MANAGER_MENUITEM, False) + self.menu_ag.add_action(action) + + new_index = 0 + subdirs = glob(os.path.join(stock_files_directory, "*")) + for subdir in subdirs: + if os.path.isdir(subdir): + new_index += 1 + add_menu_item(subdir, new_index) + + + def age_stock_config_file(self, basepath, stock_dir, stock_filename, dir_timestamped): + + READ_ONLY_BINARY='rb' + + #determine if the stock config file already exists + # if not, we are done, indicate we handled it. + filename1 = os.path.join(basepath, stock_filename) + filename2 = os.path.join(stock_dir, stock_filename) + if not os.path.exists(filename2): + return True + + # calculate and compare checksums + checksum1 = '' + checksum2 = '' + + with open(filename1,READ_ONLY_BINARY) as f: + f_contents=f.read() + checksum1 = hashlib.md5(f_contents).hexdigest() + + with open(filename2,READ_ONLY_BINARY) as f: + f_contents=f.read() + checksum2 = hashlib.md5(f_contents).hexdigest() + + # if the files are the same, we are done + # indicate we did not handle it + if checksum1 == checksum2: + return False + + # now check to see if the ageing directory exists + # if not, then create it + ageing_directory = os.path.join(stock_dir, dir_timestamped) + if not os.path.isdir(ageing_directory): + try: + os.mkdir(ageing_directory) + except Exception, e: + return False + + # now try to move the stock config file to ageing + try: + shutil.move(os.path.join(stock_dir, stock_filename), ageing_directory) + except Exception, e: + return False + return True + + def _confirm_experimental(self, rclass): sql_key = "warn_experimental_%s" % directory.radio_class_id(rclass) if CONF.is_defined(sql_key, "state") and \ @@ -834,7 +951,7 @@ return True - def do_import(self): + def do_import(self, start_dir=None): types = [(_("All files") + " (*.*)", "*"), (_("CHIRP Files") + " (*.chirp)", "*.chirp"), (_("CHIRP Radio Images") + " (*.img)", "*.img"), @@ -848,7 +965,10 @@ (_("VX5 Commander Files") + " (*.vx5)", "*.vx5"), (_("VX6 Commander Files") + " (*.vx6)", "*.vx6"), (_("VX7 Commander Files") + " (*.vx7)", "*.vx7")] - filen = platform.get_platform().gui_open_file(types=types) + if not start_dir: + filen = platform.get_platform().gui_open_file(types=types) + else: + filen = platform.get_platform().gui_open_file(start_dir=start_dir, types=types) if not filen: return @@ -2073,7 +2193,6 @@ event.new_window_state == gtk.gdk.WINDOW_STATE_MAXIMIZED, "state") self.connect("window-state-event", state_change) - d = CONF.get("last_dir", "state") if d and os.path.isdir(d): platform.get_platform().set_last_dir(d)