Merge pull request #1391 from davvid/macos/hotkeys
[git-cola.git] / cola / icons.py
blobf7faf0a534e2a2c64e2008027c11a793ce101521
1 """The only file where icon filenames are mentioned"""
2 import os
4 from qtpy import QtGui
5 from qtpy import QtWidgets
7 from . import core
8 from . import decorators
9 from . import qtcompat
10 from . import resources
11 from .compat import ustr
12 from .i18n import N_
15 KNOWN_FILE_MIME_TYPES = [
16 ('text', 'file-code.svg'),
17 ('image', 'file-media.svg'),
18 ('octet', 'file-binary.svg'),
21 KNOWN_FILE_EXTENSIONS = {
22 '.bash': 'file-code.svg',
23 '.c': 'file-code.svg',
24 '.cpp': 'file-code.svg',
25 '.css': 'file-code.svg',
26 '.cxx': 'file-code.svg',
27 '.h': 'file-code.svg',
28 '.hpp': 'file-code.svg',
29 '.hs': 'file-code.svg',
30 '.html': 'file-code.svg',
31 '.java': 'file-code.svg',
32 '.js': 'file-code.svg',
33 '.ksh': 'file-code.svg',
34 '.lisp': 'file-code.svg',
35 '.perl': 'file-code.svg',
36 '.pl': 'file-code.svg',
37 '.py': 'file-code.svg',
38 '.rb': 'file-code.svg',
39 '.rs': 'file-code.svg',
40 '.sh': 'file-code.svg',
41 '.zsh': 'file-code.svg',
45 def install(themes):
46 for theme in themes:
47 icon_dir = resources.icon_dir(theme)
48 qtcompat.add_search_path('icons', icon_dir)
51 def icon_themes():
52 return (
53 (N_('Default'), 'default'),
54 (N_('Dark Theme'), 'dark'),
55 (N_('Light Theme'), 'light'),
59 def name_from_basename(basename):
60 """Prefix the basename with "icons:" so that git-cola's icons are found
62 "icons" is registered with the Qt resource system during install().
64 """
65 return 'icons:' + basename
68 @decorators.memoize
69 def from_name(name):
70 """Return a QIcon from an absolute filename or "icons:basename.svg" name"""
71 return QtGui.QIcon(name)
74 def icon(basename):
75 """Given a basename returns a QIcon from the corresponding cola icon"""
76 return from_name(name_from_basename(basename))
79 def from_theme(name, fallback=None):
80 """Grab an icon from the current theme with a fallback
82 Support older versions of Qt checking for fromTheme's availability.
84 """
85 if hasattr(QtGui.QIcon, 'fromTheme'):
86 base, _ = os.path.splitext(name)
87 if fallback:
88 qicon = QtGui.QIcon.fromTheme(base, icon(fallback))
89 else:
90 qicon = QtGui.QIcon.fromTheme(base)
91 if not qicon.isNull():
92 return qicon
93 return icon(fallback or name)
96 def basename_from_filename(filename):
97 """Returns an icon name based on the filename"""
98 mimetype = core.guess_mimetype(filename)
99 if mimetype is not None:
100 mimetype = mimetype.lower()
101 for filetype, icon_name in KNOWN_FILE_MIME_TYPES:
102 if filetype in mimetype:
103 return icon_name
104 extension = os.path.splitext(filename)[1]
105 return KNOWN_FILE_EXTENSIONS.get(extension.lower(), 'file-text.svg')
108 def from_filename(filename):
109 """Return a QIcon from a filename"""
110 basename = basename_from_filename(filename)
111 return from_name(name_from_basename(basename))
114 def mkicon(value, default=None):
115 """Create an icon from a string value"""
116 if value is None and default is not None:
117 value = default()
118 elif value and isinstance(value, (str, ustr)):
119 value = QtGui.QIcon(value)
120 return value
123 def from_style(key):
124 """Maintain a cache of standard icons and return cache entries."""
125 style = QtWidgets.QApplication.instance().style()
126 return style.standardIcon(key)
129 def status(filename, deleted, is_staged, untracked):
130 """Status icon for a file"""
131 if deleted:
132 icon_name = 'circle-slash-red.svg'
133 elif is_staged:
134 icon_name = 'staged.svg'
135 elif untracked:
136 icon_name = 'question-plain.svg'
137 else:
138 icon_name = basename_from_filename(filename)
139 return icon_name
142 # Icons creators and SVG file references
145 def three_bars():
146 """Three-bars icon"""
147 return icon('three-bars.svg')
150 def add():
151 """Add icon"""
152 return from_theme('list-add', fallback='plus.svg')
155 def alphabetical():
156 """Alphabetical icon"""
157 return from_theme('view-sort', fallback='a-z-order.svg')
160 def branch():
161 """Branch icon"""
162 return icon('git-branch.svg')
165 def check_name():
166 """Check mark icon name"""
167 return name_from_basename('check.svg')
170 def cherry_pick():
171 """Cherry-pick icon"""
172 return icon('git-commit.svg')
175 def circle_slash_red():
176 """A circle with a slash through it"""
177 return icon('circle-slash-red.svg')
180 def close():
181 """Close icon"""
182 return icon('x.svg')
185 def cola():
186 """Git Cola icon"""
187 return icon('git-cola.svg')
190 def commit():
191 """Commit icon"""
192 return icon('document-save-symbolic.svg')
195 def compare():
196 """Compare icon"""
197 return icon('git-compare.svg')
200 def configure():
201 """Configure icon"""
202 return icon('gear.svg')
205 def cut():
206 """Cut icon"""
207 return from_theme('edit-cut', fallback='edit-cut.svg')
210 def copy():
211 """Copy icon"""
212 return from_theme('edit-copy', fallback='edit-copy.svg')
215 def paste():
216 """Paste icon"""
217 return from_theme('edit-paste', fallback='edit-paste.svg')
220 def play():
221 """Play icon"""
222 return icon('play.svg')
225 def delete():
226 """Delete icon"""
227 return from_theme('edit-delete', fallback='trashcan.svg')
230 def default_app():
231 """Default app icon"""
232 return icon('telescope.svg')
235 def dot_name():
236 """Dot icon name"""
237 return name_from_basename('primitive-dot.svg')
240 def download():
241 """Download icon"""
242 return icon('file-download.svg')
245 def discard():
246 """Discard icon"""
247 return from_theme('delete', fallback='trashcan.svg')
250 # folder vs directory: directory is opaque, folder is just an outline
251 # directory is used for the File Browser, where more contrast with the file
252 # icons are needed.
255 def folder():
256 """Folder icon"""
257 return from_theme('folder', fallback='folder.svg')
260 def directory():
261 """Directory icon"""
262 return from_theme('folder', fallback='file-directory.svg')
265 def diff():
266 """Diff icon"""
267 return icon('diff.svg')
270 def edit():
271 """Edit icon"""
272 return from_theme('document-edit', fallback='pencil.svg')
275 def ellipsis():
276 """Ellipsis icon"""
277 return icon('ellipsis.svg')
280 def external():
281 """External link icon"""
282 return icon('link-external.svg')
285 def file_code():
286 """Code file icon"""
287 return icon('file-code.svg')
290 def file_text():
291 """Text file icon"""
292 return icon('file-text.svg')
295 def file_zip():
296 """Zip file / tarball icon"""
297 return icon('file-zip.svg')
300 def fold():
301 """Fold icon"""
302 return icon('fold.svg')
305 def merge():
306 """Merge icon"""
307 return icon('git-merge.svg')
310 def modified():
311 """Modified icon"""
312 return icon('modified.svg')
315 def modified_name():
316 """Modified icon name"""
317 return name_from_basename('modified.svg')
320 def move_down():
321 """Move down icon"""
322 return from_theme('go-next', fallback='arrow-down.svg')
325 def move_up():
326 """Move up icon"""
327 return from_theme('go-previous', fallback='arrow-up.svg')
330 def new():
331 """Add new/add-to-list icon"""
332 return from_theme('list-add', fallback='folder-new.svg')
335 def ok():
336 """Ok/accept icon"""
337 return from_theme('checkmark', fallback='check.svg')
340 def open_directory():
341 """Open directory icon"""
342 return from_theme('folder', fallback='folder.svg')
345 def up():
346 """Previous icon"""
347 return icon('arrow-up.svg')
350 def down():
351 """Go to next item icon"""
352 return icon('arrow-down.svg')
355 def partial_name():
356 """Partial icon name"""
357 return name_from_basename('partial.svg')
360 def pull():
361 """Pull icon"""
362 return icon('repo-pull.svg')
365 def push():
366 """Push icon"""
367 return icon('repo-push.svg')
370 def question():
371 """Question icon"""
372 return icon('question.svg')
375 def remove():
376 """Remove icon"""
377 return from_theme('list-remove', fallback='circle-slash.svg')
380 def repo():
381 """Repository icon"""
382 return icon('repo.svg')
385 def reverse_chronological():
386 """Reverse chronological icon"""
387 return icon('last-first-order.svg')
390 def save():
391 """Save icon"""
392 return from_theme('document-save', fallback='desktop-download.svg')
395 def search():
396 """Search icon"""
397 return from_theme('search', fallback='search.svg')
400 def select_all():
401 """Select all icon"""
402 return from_theme('edit-select-all', fallback='edit-select-all')
405 def staged():
406 """Staged icon"""
407 return icon('staged.svg')
410 def staged_name():
411 """Staged icon name"""
412 return name_from_basename('staged.svg')
415 def star():
416 """Star icon"""
417 return icon('star.svg')
420 def sync():
421 """Sync/update icon"""
422 return icon('sync.svg')
425 def tag():
426 """Tag icon"""
427 return icon('tag.svg')
430 def terminal():
431 """Terminal icon"""
432 return icon('terminal.svg')
435 def undo():
436 """Undo icon"""
437 return from_theme('edit-undo', fallback='edit-undo.svg')
440 def redo():
441 """Redo icon"""
442 return from_theme('edit-redo', fallback='edit-redo.svg')
445 def style_dialog_apply():
446 """Apply icon from the current style"""
447 return from_style(QtWidgets.QStyle.SP_DialogApplyButton)
450 def style_dialog_discard():
451 """Discard icon for the current style"""
452 return from_style(QtWidgets.QStyle.SP_DialogDiscardButton)
455 def style_dialog_reset():
456 """Reset icon for the current style"""
457 return from_style(QtWidgets.QStyle.SP_DialogResetButton)
460 def unfold():
461 """Expand/unfold icon"""
462 return icon('unfold.svg')
465 def visualize():
466 """An eye icon to represent visualization"""
467 return icon('eye.svg')
470 def upstream_name():
471 """Upstream branch icon name"""
472 return name_from_basename('upstream.svg')
475 def zoom_fit_best():
476 """Zoom-to-fit icon"""
477 return from_theme('zoom-fit-best', fallback='zoom-fit-best.svg')
480 def zoom_in():
481 """Zoom-in icon"""
482 return from_theme('zoom-in', fallback='zoom-in.svg')
485 def zoom_out():
486 """Zoom-out icon"""
487 return from_theme('zoom-out', fallback='zoom-out.svg')