Merge pull request #1391 from davvid/macos/hotkeys
[git-cola.git] / cola / resources.py
blob2e1ce1ab7ddbeb315c66448ca021ad9fc7cd5204
1 """Functions for finding cola resources"""
2 import os
3 import sys
4 import webbrowser
6 from . import core
7 from . import compat
10 # Default git-cola icon theme
11 _default_icon_theme = 'light'
13 _resources = core.abspath(core.realpath(__file__))
14 _package = os.path.dirname(_resources)
16 if _package.endswith(os.path.join('site-packages', 'cola')):
17 # Unix release tree
18 # __file__ = '$prefix/lib/pythonX.Y/site-packages/cola/__file__.py'
19 # _package = '$prefix/lib/pythonX.Y/site-packages/cola'
20 _prefix = os.path.dirname(
21 os.path.dirname(os.path.dirname(os.path.dirname(_package)))
23 elif _package.endswith(os.path.join('pkgs', 'cola')):
24 # Windows release tree
25 # __file__ = $installdir/pkgs/cola
26 _prefix = os.path.dirname(os.path.dirname(_package))
27 else:
28 # this is the source tree
29 # __file__ = '$prefix/cola/__file__.py'
30 _prefix = os.path.dirname(_package)
33 def get_prefix():
34 """Return the installation prefix"""
35 return _prefix
38 def prefix(*args):
39 """Return a path relative to cola's installation prefix"""
40 return os.path.join(get_prefix(), *args)
43 def sibling_bindir(*args):
44 """Return a command sibling to sys.argv[0]"""
45 relative_bindir = os.path.dirname(sys.argv[0])
46 return os.path.join(relative_bindir, *args)
49 def command(name):
50 """Return a command from the bin/ directory"""
51 if compat.WIN32:
52 # On Windows we have to support being installed via the pynsist installation
53 # layout and the pip-installed layout. We also have check for .exe launchers
54 # and prefer them when present.
55 sibling = sibling_bindir(name)
56 scripts = prefix('Scripts', name)
57 bindir = prefix('bin', name)
58 # Check for "${name}.exe" on Windows.
59 exe = f'{name}.exe'
60 sibling_exe = sibling_bindir(exe)
61 scripts_exe = prefix('Scripts', exe)
62 bindir_exe = prefix('bin', exe)
63 if core.exists(sibling_exe):
64 result = sibling_exe
65 elif core.exists(sibling):
66 result = sibling
67 elif core.exists(bindir_exe):
68 result = bindir_exe
69 elif core.exists(scripts_exe):
70 result = scripts_exe
71 elif core.exists(scripts):
72 result = scripts
73 else:
74 result = bindir
75 else:
76 result = sibling_bindir(name)
77 if not core.exists(result):
78 result = prefix('bin', name)
80 return result
83 def doc(*args):
84 """Return a path relative to cola's /usr/share/doc/ directory"""
85 return share('doc', 'git-cola', *args)
88 def i18n(*args):
89 """Return a path relative to cola's i18n locale directory, e.g. cola/i18n"""
90 return package_data('i18n', *args)
93 def html_docs():
94 """Return the path to the cola html documentation."""
95 # html/index.html only exists after the install-docs target is run.
96 # Fallback to the source tree and lastly git-cola.rst.
97 paths_to_try = (('html', 'index.html'), ('_build', 'html', 'index.html'))
98 for paths in paths_to_try:
99 docdir = doc(*paths)
100 if core.exists(docdir):
101 return docdir
102 return doc('git-cola.rst')
105 def show_html_docs():
106 """Open the HTML documentation in a browser"""
107 url = html_docs()
108 webbrowser.open_new_tab('file://' + url)
111 def share(*args):
112 """Return a path relative to cola's /usr/share/ directory"""
113 return prefix('share', *args)
116 def package_data(*args):
117 """Return a path relative to cola's Python modules"""
118 return os.path.join(_package, *args)
121 def package_command(*args):
122 """Return a path relative to cola's private bin/ directory"""
123 return package_data('bin', *args)
126 def icon_dir(theme):
127 """Return the icons directory for the specified theme
129 This returns the ``icons`` directory inside the ``cola`` Python package.
130 When theme is defined then it will return a subdirectory of the icons/
131 directory, e.g. "dark" for the dark icon theme.
133 When theme is set to an absolute directory path, that directory will be
134 returned, which effectively makes git-cola use those icons.
136 if not theme or theme == _default_icon_theme:
137 icons = package_data('icons')
138 else:
139 theme_dir = package_data('icons', theme)
140 if os.path.isabs(theme) and os.path.isdir(theme):
141 icons = theme
142 elif os.path.isdir(theme_dir):
143 icons = theme_dir
144 else:
145 icons = package_data('icons')
147 return icons
150 def xdg_config_home(*args):
151 """Return the XDG_CONFIG_HOME configuration directory, eg. ~/.config"""
152 config = core.getenv(
153 'XDG_CONFIG_HOME', os.path.join(core.expanduser('~'), '.config')
155 return os.path.join(config, *args)
158 def xdg_data_home(*args):
159 """Return the XDG_DATA_HOME configuration directory, e.g. ~/.local/share"""
160 config = core.getenv(
161 'XDG_DATA_HOME', os.path.join(core.expanduser('~'), '.local', 'share')
163 return os.path.join(config, *args)
166 def xdg_data_dirs():
167 """Return the current set of XDG data directories
169 Returns the values from $XDG_DATA_DIRS when defined in the environment.
170 If $XDG_DATA_DIRS is either not set or empty, a value equal to
171 /usr/local/share:/usr/share is used.
173 paths = []
174 xdg_data_home_dir = xdg_data_home()
175 if os.path.isdir(xdg_data_home_dir):
176 paths.append(xdg_data_home_dir)
178 xdg_data_dirs_env = core.getenv('XDG_DATA_DIRS', '')
179 if not xdg_data_dirs_env:
180 xdg_data_dirs_env = '/usr/local/share:/usr/share'
181 paths.extend(path for path in xdg_data_dirs_env.split(':') if os.path.isdir(path))
182 return paths
185 def find_first(subpath, paths, validate=os.path.isfile):
186 """Return the first `subpath` found in the specified directory paths"""
187 if os.path.isabs(subpath):
188 return subpath
189 for path in paths:
190 candidate = os.path.join(path, subpath)
191 if validate(candidate):
192 return candidate
193 # Nothing was found so return None.
194 return None
197 def config_home(*args):
198 """Return git-cola's configuration directory, e.g. ~/.config/git-cola"""
199 return xdg_config_home('git-cola', *args)