Merge pull request #1299 from wojnilowicz/main
[git-cola.git] / cola / icons.py
blob220413a1d79de86154e6a5b8b92b022a7c5b1e52
1 """The only file where icon filenames are mentioned"""
2 from __future__ import absolute_import, division, print_function, unicode_literals
3 import os
5 from qtpy import QtGui
6 from qtpy import QtWidgets
8 from . import core
9 from . import decorators
10 from . import qtcompat
11 from . import resources
12 from .compat import ustr
13 from .i18n import N_
16 KNOWN_FILE_MIME_TYPES = [
17 ('text', 'file-code.svg'),
18 ('image', 'file-media.svg'),
19 ('octet', 'file-binary.svg'),
22 KNOWN_FILE_EXTENSIONS = {
23 '.bash': 'file-code.svg',
24 '.c': 'file-code.svg',
25 '.cpp': 'file-code.svg',
26 '.css': 'file-code.svg',
27 '.cxx': 'file-code.svg',
28 '.h': 'file-code.svg',
29 '.hpp': 'file-code.svg',
30 '.hs': 'file-code.svg',
31 '.html': 'file-code.svg',
32 '.java': 'file-code.svg',
33 '.js': 'file-code.svg',
34 '.ksh': 'file-code.svg',
35 '.lisp': 'file-code.svg',
36 '.perl': 'file-code.svg',
37 '.pl': 'file-code.svg',
38 '.py': 'file-code.svg',
39 '.rb': 'file-code.svg',
40 '.rs': 'file-code.svg',
41 '.sh': 'file-code.svg',
42 '.zsh': 'file-code.svg',
46 def install(themes):
47 for theme in themes:
48 icon_dir = resources.icon_dir(theme)
49 qtcompat.add_search_path('icons', icon_dir)
52 def icon_themes():
53 return (
54 (N_('Default'), 'default'),
55 (N_('Dark Theme'), 'dark'),
56 (N_('Light Theme'), 'light'),
60 def name_from_basename(basename):
61 """Prefix the basename with "icons:" so that git-cola's icons are found
63 "icons" is registered with Qt's resource system during install().
65 """
66 return 'icons:' + basename
69 @decorators.memoize
70 def from_name(name):
71 """Return a QIcon from an absolute filename or "icons:basename.svg" name"""
72 return QtGui.QIcon(name)
75 def icon(basename):
76 """Given a basename returns a QIcon from the corresponding cola icon"""
77 return from_name(name_from_basename(basename))
80 def from_theme(name, fallback=None):
81 """Grab an icon from the current theme with a fallback
83 Support older versions of Qt checking for fromTheme's availability.
85 """
86 if hasattr(QtGui.QIcon, 'fromTheme'):
87 base, _ = os.path.splitext(name)
88 if fallback:
89 qicon = QtGui.QIcon.fromTheme(base, icon(fallback))
90 else:
91 qicon = QtGui.QIcon.fromTheme(base)
92 if not qicon.isNull():
93 return qicon
94 return icon(fallback or name)
97 def basename_from_filename(filename):
98 """Returns an icon name based on the filename"""
99 mimetype = core.guess_mimetype(filename)
100 if mimetype is not None:
101 mimetype = mimetype.lower()
102 for filetype, icon_name in KNOWN_FILE_MIME_TYPES:
103 if filetype in mimetype:
104 return icon_name
105 extension = os.path.splitext(filename)[1]
106 return KNOWN_FILE_EXTENSIONS.get(extension.lower(), 'file-text.svg')
109 def from_filename(filename):
110 """Return a QIcon from a filename"""
111 basename = basename_from_filename(filename)
112 return from_name(name_from_basename(basename))
115 def mkicon(value, default=None):
116 """Create an icon from a string value"""
117 if value is None and default is not None:
118 value = default()
119 elif value and isinstance(value, (str, ustr)):
120 value = QtGui.QIcon(value)
121 return value
124 def from_style(key):
125 """Maintain a cache of standard icons and return cache entries."""
126 style = QtWidgets.QApplication.instance().style()
127 return style.standardIcon(key)
130 def status(filename, deleted, is_staged, untracked):
131 """Status icon for a file"""
132 if deleted:
133 icon_name = 'circle-slash-red.svg'
134 elif is_staged:
135 icon_name = 'staged.svg'
136 elif untracked:
137 icon_name = 'question-plain.svg'
138 else:
139 icon_name = basename_from_filename(filename)
140 return icon_name
143 # Icons creators and SVG file references
146 def three_bars():
147 """Three-bars icon"""
148 return icon('three-bars.svg')
151 def add():
152 """Add icon"""
153 return from_theme('list-add', fallback='plus.svg')
156 def alphabetical():
157 """Alphabetical icon"""
158 return from_theme('view-sort', fallback='a-z-order.svg')
161 def branch():
162 """Branch icon"""
163 return icon('git-branch.svg')
166 def check_name():
167 """Checkmark icon name"""
168 return name_from_basename('check.svg')
171 def cherry_pick():
172 """Cherry-pick icon"""
173 return icon('git-commit.svg')
176 def circle_slash_red():
177 """A circle with a slash through it"""
178 return icon('circle-slash-red.svg')
181 def close():
182 """Close icon"""
183 return icon('x.svg')
186 def cola():
187 """Git Cola icon"""
188 return icon('git-cola.svg')
191 def commit():
192 """Commit icon"""
193 return icon('document-save-symbolic.svg')
196 def compare():
197 """Compare icon"""
198 return icon('git-compare.svg')
201 def configure():
202 """Configure icon"""
203 return icon('gear.svg')
206 def cut():
207 """Cut icon"""
208 return from_theme('edit-cut', fallback='edit-cut.svg')
211 def copy():
212 """Copy icon"""
213 return from_theme('edit-copy', fallback='edit-copy.svg')
216 def paste():
217 """Paste icon"""
218 return from_theme('edit-paste', fallback='edit-paste.svg')
221 def play():
222 """Play icon"""
223 return icon('play.svg')
226 def delete():
227 """Delete icon"""
228 return from_theme('edit-delete', fallback='trashcan.svg')
231 def default_app():
232 """Default app icon"""
233 return icon('telescope.svg')
236 def dot_name():
237 """Dot icon name"""
238 return name_from_basename('primitive-dot.svg')
241 def download():
242 """Download icon"""
243 return icon('file-download.svg')
246 def discard():
247 """Discard icon"""
248 return from_theme('delete', fallback='trashcan.svg')
251 # folder vs directory: directory is opaque, folder is just an outline
252 # directory is used for the File Browser, where more contrast with the file
253 # icons are needed.
256 def folder():
257 """Folder icon"""
258 return from_theme('folder', fallback='folder.svg')
261 def directory():
262 """Directory icon"""
263 return from_theme('folder', fallback='file-directory.svg')
266 def diff():
267 """Diff icon"""
268 return icon('diff.svg')
271 def edit():
272 """Edit icon"""
273 return from_theme('document-edit', fallback='pencil.svg')
276 def ellipsis():
277 """Ellipsis icon"""
278 return icon('ellipsis.svg')
281 def external():
282 """External link icon"""
283 return icon('link-external.svg')
286 def file_code():
287 """Code file icon"""
288 return icon('file-code.svg')
291 def file_text():
292 """Text file icon"""
293 return icon('file-text.svg')
296 def file_zip():
297 """Zip file / tarball icon"""
298 return icon('file-zip.svg')
301 def fold():
302 """Fold icon"""
303 return icon('fold.svg')
306 def merge():
307 """Merge icon"""
308 return icon('git-merge.svg')
311 def modified():
312 """Modified icon"""
313 return icon('modified.svg')
316 def modified_name():
317 """Modified icon name"""
318 return name_from_basename('modified.svg')
321 def move_down():
322 """Move down icon"""
323 return from_theme('go-previous', fallback='arrow-down.svg')
326 def move_up():
327 """Move up icon"""
328 return from_theme('go-next', fallback='arrow-up.svg')
331 def new():
332 """Add new/add-to-list icon"""
333 return from_theme('list-add', fallback='folder-new.svg')
336 def ok():
337 """Ok/accept icon"""
338 return from_theme('checkmark', fallback='check.svg')
341 def open_directory():
342 """Open directory icon"""
343 return from_theme('folder', fallback='folder.svg')
346 def partial_name():
347 """Partial icon name"""
348 return name_from_basename('partial.svg')
351 def pull():
352 """Pull icon"""
353 return icon('repo-pull.svg')
356 def push():
357 """Push icon"""
358 return icon('repo-push.svg')
361 def question():
362 """Question icon"""
363 return icon('question.svg')
366 def remove():
367 """Remove icon"""
368 return from_theme('list-remove', fallback='circle-slash.svg')
371 def repo():
372 """Repository icon"""
373 return icon('repo.svg')
376 def reverse_chronological():
377 """Reverse chronological icon"""
378 return icon('last-first-order.svg')
381 def save():
382 """Save icon"""
383 return from_theme('document-save', fallback='desktop-download.svg')
386 def search():
387 """Search icon"""
388 return from_theme('search', fallback='search.svg')
391 def select_all():
392 """Select all icon"""
393 return from_theme('edit-select-all', fallback='edit-select-all')
396 def staged():
397 """Staged icon"""
398 return icon('staged.svg')
401 def staged_name():
402 """Staged icon name"""
403 return name_from_basename('staged.svg')
406 def star():
407 """Star icon"""
408 return icon('star.svg')
411 def sync():
412 """Sync/update icon"""
413 return icon('sync.svg')
416 def tag():
417 """Tag icon"""
418 return icon('tag.svg')
421 def terminal():
422 """Terminal icon"""
423 return icon('terminal.svg')
426 def undo():
427 """Undo icon"""
428 return from_theme('edit-undo', fallback='edit-undo.svg')
431 def redo():
432 """Redo icon"""
433 return from_theme('edit-redo', fallback='edit-redo.svg')
436 def style_dialog_apply():
437 """Apply icon from the current style"""
438 return from_style(QtWidgets.QStyle.SP_DialogApplyButton)
441 def style_dialog_discard():
442 """Discard icon for the current style"""
443 return from_style(QtWidgets.QStyle.SP_DialogDiscardButton)
446 def style_dialog_reset():
447 """Reset icon for the current style"""
448 return from_style(QtWidgets.QStyle.SP_DialogResetButton)
451 def unfold():
452 """Expand/unfold icon"""
453 return icon('unfold.svg')
456 def visualize():
457 """An eye icon to represent visualization"""
458 return icon('eye.svg')
461 def upstream_name():
462 """Upstream branch icon name"""
463 return name_from_basename('upstream.svg')
466 def zoom_fit_best():
467 """Zoom-to-fit icon"""
468 return from_theme('zoom-fit-best', fallback='zoom-fit-best.svg')
471 def zoom_in():
472 """Zoom-in icon"""
473 return from_theme('zoom-in', fallback='zoom-in.svg')
476 def zoom_out():
477 """Zoom-out icon"""
478 return from_theme('zoom-out', fallback='zoom-out.svg')