main.view: Use the brushed metal style on OS X
[git-cola.git] / cola / main / view.py
blobfca749ab3aacd302d0e6630c9916e31725f39f7a
1 """This view provides the main git-cola user interface.
2 """
3 import os
5 from PyQt4 import QtCore
6 from PyQt4 import QtGui
7 from PyQt4.QtCore import Qt
8 from PyQt4.QtCore import SIGNAL
10 import cola
11 from cola import core
12 from cola import gitcmds
13 from cola import guicmds
14 from cola import merge
15 from cola import settings
16 from cola import signals
17 from cola import gitcfg
18 from cola import qtutils
19 from cola import qtcompat
20 from cola import qt
21 from cola import resources
22 from cola import stash
23 from cola import utils
24 from cola import version
25 from cola.bookmarks import manage_bookmarks
26 from cola.classic import cola_classic
27 from cola.classic import classic_widget
28 from cola.dag import git_dag
29 from cola.git import git
30 from cola.prefs import diff_font
31 from cola.prefs import PreferencesModel
32 from cola.prefs import preferences
33 from cola.qt import create_button
34 from cola.qt import create_dock
35 from cola.qt import create_menu
36 from cola.qtutils import add_action
37 from cola.qtutils import connect_action
38 from cola.qtutils import connect_action_bool
39 from cola.qtutils import connect_button
40 from cola.qtutils import emit
41 from cola.qtutils import log
42 from cola.qtutils import relay_signal
43 from cola.qtutils import tr
44 from cola.widgets import cfgactions
45 from cola.widgets import remote
46 from cola.widgets import standard
47 from cola.widgets.about import launch_about_dialog
48 from cola.widgets.about import show_shortcuts
49 from cola.widgets.archive import GitArchiveDialog
50 from cola.widgets.commitmsg import CommitMessageEditor
51 from cola.widgets.compare import compare_branches
52 from cola.widgets.createtag import create_tag
53 from cola.widgets.createbranch import create_new_branch
54 from cola.widgets.diff import DiffEditor
55 from cola.widgets.recent import browse_recent
56 from cola.widgets.status import StatusWidget
57 from cola.widgets.search import search
60 class MainView(standard.MainWindow):
61 def __init__(self, model, parent):
62 super(MainView, self).__init__(parent)
63 # Default size; this is thrown out when save/restore is used
64 self.resize(987, 610)
65 self.model = model
66 self.prefs_model = prefs_model = PreferencesModel()
68 # Internal field used by import/export_state().
69 # Change this whenever dockwidgets are removed.
70 self.widget_version = 1
72 # Keeps track of merge messages we've seen
73 self.merge_message_hash = ''
75 self.setAcceptDrops(True)
76 self.setAttribute(QtCore.Qt.WA_MacMetalStyle)
78 # Dockwidget options
79 qtcompat.set_common_dock_options(self)
81 self.classic_dockable = gitcfg.instance().get('cola.classicdockable')
83 if self.classic_dockable:
84 self.classicdockwidget = create_dock('Classic', self)
85 self.classicwidget = classic_widget(self)
86 self.classicdockwidget.setWidget(self.classicwidget)
88 # "Actions" widget
89 self.actionsdockwidget = create_dock('Action', self)
90 self.actionsdockwidgetcontents = qt.QFlowLayoutWidget(self)
91 layout = self.actionsdockwidgetcontents.layout()
92 self.stage_button = create_button(text='Stage', layout=layout)
93 self.unstage_button = create_button(text='Unstage', layout=layout)
94 self.rescan_button = create_button(text='Rescan', layout=layout)
95 self.fetch_button = create_button(text='Fetch...', layout=layout)
96 self.push_button = create_button(text='Push...', layout=layout)
97 self.pull_button = create_button(text='Pull...', layout=layout)
98 self.stash_button = create_button(text='Stash...', layout=layout)
99 layout.addStretch()
100 self.actionsdockwidget.setWidget(self.actionsdockwidgetcontents)
102 # "Repository Status" widget
103 self.statusdockwidget = create_dock('Status', self)
104 self.statusdockwidget.setWidget(StatusWidget(self))
106 # "Commit Message Editor" widget
107 self.position_label = QtGui.QLabel()
108 font = qtutils.default_monospace_font()
109 font.setPointSize(int(font.pointSize() * 0.8))
110 self.position_label.setFont(font)
111 self.commitdockwidget = create_dock('Commit', self)
112 titlebar = self.commitdockwidget.titleBarWidget()
113 titlebar.add_corner_widget(self.position_label)
115 self.commitmsgeditor = CommitMessageEditor(model, self)
116 relay_signal(self, self.commitmsgeditor, SIGNAL(signals.amend_mode))
117 relay_signal(self, self.commitmsgeditor, SIGNAL(signals.signoff))
118 relay_signal(self, self.commitmsgeditor,
119 SIGNAL(signals.load_previous_message))
120 self.commitdockwidget.setWidget(self.commitmsgeditor)
122 # "Command Output" widget
123 logwidget = qtutils.logger()
124 logwidget.setFont(diff_font())
125 self.logdockwidget = create_dock('Console', self)
126 self.logdockwidget.setWidget(logwidget)
128 # "Diff Viewer" widget
129 self.diffdockwidget = create_dock('Diff', self)
130 self.diff_editor = DiffEditor(self.diffdockwidget)
131 self.diffdockwidget.setWidget(self.diff_editor)
133 # All Actions
134 self.menu_unstage_all = add_action(self,
135 'Unstage All', emit(self, signals.unstage_all))
136 self.menu_unstage_all.setIcon(qtutils.icon('remove.svg'))
138 self.menu_unstage_selected = add_action(self,
139 'Unstage From Commit', emit(self, signals.unstage_selected))
140 self.menu_unstage_selected.setIcon(qtutils.icon('remove.svg'))
142 self.menu_show_diffstat = add_action(self,
143 'Diffstat', emit(self, signals.diffstat), 'Alt+D')
145 self.menu_stage_modified = add_action(self,
146 'Stage Changed Files To Commit',
147 emit(self, signals.stage_modified), 'Alt+A')
148 self.menu_stage_modified.setIcon(qtutils.icon('add.svg'))
150 self.menu_stage_untracked = add_action(self,
151 'Stage All Untracked', emit(self, signals.stage_untracked), 'Alt+U')
152 self.menu_stage_untracked.setIcon(qtutils.icon('add.svg'))
154 self.menu_export_patches = add_action(self,
155 'Export Patches...', guicmds.export_patches, 'Alt+E')
156 self.menu_preferences = add_action(self,
157 'Preferences', lambda: preferences(model=prefs_model),
158 QtGui.QKeySequence.Preferences, 'Ctrl+O')
160 self.menu_rescan = add_action(self,
161 'Rescan', emit(self, signals.rescan_and_refresh), 'Ctrl+R')
162 self.menu_rescan.setIcon(qtutils.reload_icon())
164 self.menu_browse_recent = add_action(self,
165 'Recently Modified Files...', browse_recent, 'Shift+Ctrl+E')
167 self.menu_cherry_pick = add_action(self,
168 'Cherry-Pick...', guicmds.cherry_pick, 'Ctrl+P')
170 self.menu_load_commitmsg = add_action(self,
171 'Load Commit Message...', guicmds.load_commitmsg)
173 self.menu_save_tarball = add_action(self,
174 'Save As Tarball/Zip...', self.save_archive)
176 self.menu_quit = add_action(self,
177 'Quit', self.close, 'Ctrl+Q')
178 self.menu_manage_bookmarks = add_action(self,
179 'Bookmarks...', manage_bookmarks)
180 self.menu_grep = add_action(self,
181 'Grep', guicmds.grep)
182 self.menu_merge_local = add_action(self,
183 'Merge...', merge.local_merge)
185 self.menu_merge_abort = add_action(self,
186 'Abort Merge...', merge.abort_merge)
188 self.menu_fetch = add_action(self,
189 'Fetch...', remote.fetch)
190 self.menu_push = add_action(self,
191 'Push...', remote.push)
192 self.menu_pull = add_action(self,
193 'Pull...', remote.pull)
195 self.menu_open_repo = add_action(self,
196 'Open...', guicmds.open_repo)
197 self.menu_open_repo.setIcon(qtutils.open_icon())
199 self.menu_stash = add_action(self,
200 'Stash...', stash.stash, 'Alt+Shift+S')
202 self.menu_clone_repo = add_action(self,
203 'Clone...', guicmds.clone_repo)
204 self.menu_clone_repo.setIcon(qtutils.git_icon())
206 self.menu_help_docs = add_action(self,
207 'Documentation', resources.show_html_docs,
208 QtGui.QKeySequence.HelpContents)
210 self.menu_help_shortcuts = add_action(self,
211 'Keyboard Shortcuts',
212 show_shortcuts,
213 QtCore.Qt.Key_Question)
215 self.menu_visualize_current = add_action(self,
216 'Visualize Current Branch...',
217 emit(self, signals.visualize_current))
218 self.menu_visualize_all = add_action(self,
219 'Visualize All Branches...',
220 emit(self, signals.visualize_all))
221 self.menu_search_commits = add_action(self,
222 'Search...', search)
223 self.menu_browse_branch = add_action(self,
224 'Browse Current Branch...', guicmds.browse_current)
225 self.menu_browse_other_branch = add_action(self,
226 'Browse Other Branch...', guicmds.browse_other)
227 self.menu_load_commitmsg_template = add_action(self,
228 'Get Commit Message Template',
229 emit(self, signals.load_commit_template))
230 self.menu_help_about = add_action(self,
231 'About', launch_about_dialog)
233 self.menu_branch_diff = add_action(self,
234 'SHA-1...', guicmds.diff_revision)
235 self.menu_diff_expression = add_action(self,
236 'Expression...', guicmds.diff_expression)
237 self.menu_branch_compare = add_action(self,
238 'Branches...', compare_branches)
240 self.menu_create_tag = add_action(self,
241 'Create Tag...', create_tag)
243 self.menu_create_branch = add_action(self,
244 'Create...', create_new_branch, 'Ctrl+B')
246 self.menu_delete_branch = add_action(self,
247 'Delete...', guicmds.branch_delete)
249 self.menu_checkout_branch = add_action(self,
250 'Checkout...', guicmds.checkout_branch, 'Alt+B')
251 self.menu_rebase_branch = add_action(self,
252 'Rebase...', guicmds.rebase)
253 self.menu_branch_review = add_action(self,
254 'Review...', guicmds.review_branch)
256 self.menu_classic = add_action(self,
257 'Cola Classic...', cola_classic)
258 self.menu_classic.setIcon(qtutils.git_icon())
260 self.menu_dag = add_action(self,
261 'DAG...', lambda: git_dag(self.model))
262 self.menu_dag.setIcon(qtutils.git_icon())
264 # Relayed actions
265 status_tree = self.statusdockwidget.widget().tree
266 self.addAction(status_tree.up)
267 self.addAction(status_tree.down)
268 self.addAction(status_tree.process_selection)
269 self.addAction(status_tree.launch_difftool)
271 # Create the application menu
272 self.menubar = QtGui.QMenuBar(self)
274 # File Menu
275 self.file_menu = create_menu('&File', self.menubar)
276 self.file_menu.addAction(self.menu_preferences)
277 self.file_menu.addSeparator()
278 self.file_menu.addAction(self.menu_open_repo)
279 self.file_menu.addAction(self.menu_clone_repo)
280 self.file_menu.addAction(self.menu_manage_bookmarks)
281 self.file_menu.addSeparator()
282 self.file_menu.addAction(self.menu_rescan)
283 self.file_menu.addSeparator()
284 self.file_menu.addAction(self.menu_browse_recent)
285 self.file_menu.addSeparator()
286 self.file_menu.addAction(self.menu_load_commitmsg)
287 self.file_menu.addAction(self.menu_load_commitmsg_template)
288 self.file_menu.addSeparator()
289 self.file_menu.addAction(self.menu_save_tarball)
290 self.file_menu.addAction(self.menu_quit)
291 # Add to menubar
292 self.menubar.addAction(self.file_menu.menuAction())
294 # Commit Menu
295 self.commit_menu = create_menu('Co&mmit', self.menubar)
296 self.commit_menu.setTitle(tr('Commit@@verb'))
297 self.commit_menu.addAction(self.menu_stage_modified)
298 self.commit_menu.addAction(self.menu_stage_untracked)
299 self.commit_menu.addSeparator()
300 self.commit_menu.addAction(self.menu_unstage_all)
301 self.commit_menu.addAction(self.menu_unstage_selected)
302 self.commit_menu.addSeparator()
303 self.commit_menu.addAction(self.menu_search_commits)
304 # Add to menubar
305 self.menubar.addAction(self.commit_menu.menuAction())
307 # Branch Menu
308 self.branch_menu = create_menu('B&ranch', self.menubar)
309 self.branch_menu.addAction(self.menu_branch_review)
310 self.branch_menu.addSeparator()
311 self.branch_menu.addAction(self.menu_create_branch)
312 self.branch_menu.addAction(self.menu_checkout_branch)
313 self.branch_menu.addAction(self.menu_rebase_branch)
314 self.branch_menu.addAction(self.menu_delete_branch)
315 self.branch_menu.addSeparator()
316 self.branch_menu.addAction(self.menu_browse_branch)
317 self.branch_menu.addAction(self.menu_browse_other_branch)
318 self.branch_menu.addSeparator()
319 self.branch_menu.addAction(self.menu_visualize_current)
320 self.branch_menu.addAction(self.menu_visualize_all)
321 # Add to menubar
322 self.menubar.addAction(self.branch_menu.menuAction())
324 # Actions menu
325 self.actions_menu = create_menu('Act&ions', self.menubar)
326 self.actions_menu.addAction(self.menu_merge_local)
327 self.actions_menu.addAction(self.menu_stash)
328 self.actions_menu.addSeparator()
329 self.actions_menu.addAction(self.menu_fetch)
330 self.actions_menu.addAction(self.menu_push)
331 self.actions_menu.addAction(self.menu_pull)
332 self.actions_menu.addSeparator()
333 self.actions_menu.addAction(self.menu_create_tag)
334 self.actions_menu.addSeparator()
335 self.actions_menu.addAction(self.menu_export_patches)
336 self.actions_menu.addAction(self.menu_cherry_pick)
337 self.actions_menu.addSeparator()
338 self.actions_menu.addAction(self.menu_merge_abort)
339 self.actions_menu.addAction(self.menu_grep)
340 # Add to menubar
341 self.menubar.addAction(self.actions_menu.menuAction())
343 # Diff Menu
344 self.diff_menu = create_menu('&Diff', self.menubar)
345 self.diff_menu.addAction(self.menu_branch_diff)
346 self.diff_menu.addAction(self.menu_diff_expression)
347 self.diff_menu.addAction(self.menu_branch_compare)
348 self.diff_menu.addSeparator()
349 self.diff_menu.addAction(self.menu_show_diffstat)
350 # Add to menubar
351 self.menubar.addAction(self.diff_menu.menuAction())
353 # Tools Menu
354 self.tools_menu = create_menu('&Tools', self.menubar)
355 self.tools_menu.addAction(self.menu_classic)
356 self.tools_menu.addAction(self.menu_dag)
357 self.tools_menu.addSeparator()
358 if self.classic_dockable:
359 self.tools_menu.addAction(self.classicdockwidget.toggleViewAction())
361 self.setup_dockwidget_tools_menu()
362 self.menubar.addAction(self.tools_menu.menuAction())
364 # Help Menu
365 self.help_menu = create_menu('&Help', self.menubar)
366 self.help_menu.addAction(self.menu_help_docs)
367 self.help_menu.addAction(self.menu_help_shortcuts)
368 self.help_menu.addAction(self.menu_help_about)
369 # Add to menubar
370 self.menubar.addAction(self.help_menu.menuAction())
372 # Set main menu
373 self.setMenuBar(self.menubar)
375 # Arrange dock widgets
376 top = Qt.TopDockWidgetArea
377 bottom = Qt.BottomDockWidgetArea
379 self.addDockWidget(top, self.commitdockwidget)
380 if self.classic_dockable:
381 self.addDockWidget(top, self.classicdockwidget)
382 self.addDockWidget(top, self.statusdockwidget)
383 self.addDockWidget(top, self.actionsdockwidget)
384 self.addDockWidget(bottom, self.logdockwidget)
385 if self.classic_dockable:
386 self.tabifyDockWidget(self.classicdockwidget, self.commitdockwidget)
387 self.tabifyDockWidget(self.logdockwidget, self.diffdockwidget)
389 # Listen for model notifications
390 model.add_observer(model.message_updated, self._update_view)
392 prefs_model.add_observer(prefs_model.message_config_updated,
393 self._config_updated)
395 # Set a default value
396 self.show_cursor_position(1, 0)
398 # Add button callbacks
399 connect_button(self.rescan_button,
400 emit(self, signals.rescan_and_refresh))
401 connect_button(self.fetch_button, remote.fetch)
402 connect_button(self.push_button, remote.push)
403 connect_button(self.pull_button, remote.pull)
404 connect_button(self.stash_button, stash.stash)
406 connect_button(self.stage_button, self.stage)
407 connect_button(self.unstage_button, self.unstage)
409 self.connect(self.commitmsgeditor, SIGNAL('cursorPosition(int,int)'),
410 self.show_cursor_position)
411 self.connect(self, SIGNAL('update'), self._update_callback)
412 self.connect(self, SIGNAL('apply_state'), self.apply_state)
413 self.connect(self, SIGNAL('install_config_actions'),
414 self._install_config_actions)
416 # Install .git-config-defined actions
417 self._config_task = None
418 self.install_config_actions()
420 # Restore saved settings
421 self._gui_state_task = None
422 self._load_gui_state()
424 log(0, version.git_version_str() + '\ncola version ' + version.version())
426 # Qt overrides
427 def closeEvent(self, event):
428 """Save state in the settings manager."""
429 qtutils.save_state(self)
430 standard.MainWindow.closeEvent(self, event)
432 # Accessors
433 mode = property(lambda self: self.model.mode)
435 def _config_updated(self, source, config, value):
436 if config == 'cola.fontdiff':
437 font = QtGui.QFont()
438 if not font.fromString(value):
439 return
440 qtutils.logger().setFont(font)
441 self.diff_editor.setFont(font)
442 self.commitmsgeditor.setFont(font)
444 elif config == 'cola.tabwidth':
445 # variable-tab-width setting
446 self.diff_editor.set_tab_width(value)
448 def install_config_actions(self):
449 """Install .gitconfig-defined actions"""
450 self._config_task = self._start_config_actions_task()
452 def _start_config_actions_task(self):
453 """Do the expensive "get_config_actions()" call in the background"""
454 class ConfigActionsTask(QtCore.QRunnable):
455 def __init__(self, sender):
456 QtCore.QRunnable.__init__(self)
457 self._sender = sender
458 def run(self):
459 names = cfgactions.get_config_actions()
460 self._sender.emit(SIGNAL('install_config_actions'), names)
462 task = ConfigActionsTask(self)
463 QtCore.QThreadPool.globalInstance().start(task)
464 return task
466 def _install_config_actions(self, names):
467 """Install .gitconfig-defined actions"""
468 if not names:
469 return
470 menu = self.actions_menu
471 menu.addSeparator()
472 for name in names:
473 menu.addAction(name, emit(self, signals.run_config_action, name))
475 def _update_view(self):
476 self.emit(SIGNAL('update'))
478 def _update_callback(self):
479 """Update the title with the current branch and directory name."""
480 branch = self.model.currentbranch
481 curdir = core.decode(os.getcwd())
482 msg = 'Repository: %s\nBranch: %s' % (curdir, branch)
483 self.commitdockwidget.setToolTip(msg)
485 title = '%s: %s' % (self.model.project, branch)
486 if self.mode == self.model.mode_amend:
487 title += ' ** amending **'
488 self.setWindowTitle(title)
490 self.commitmsgeditor.set_mode(self.mode)
492 if not self.model.read_only() and self.mode != self.model.mode_amend:
493 # Check if there's a message file in .git/
494 merge_msg_path = gitcmds.merge_message_path()
495 if merge_msg_path is None:
496 return
497 merge_msg_hash = utils.checksum(core.decode(merge_msg_path))
498 if merge_msg_hash == self.merge_message_hash:
499 return
500 self.merge_message_hash = merge_msg_hash
501 cola.notifier().broadcast(signals.load_commit_message,
502 core.decode(merge_msg_path))
504 def apply_state(self, state):
505 """Imports data for save/restore"""
506 # 1 is the widget version; change when widgets are added/removed
507 standard.MainWindow.apply_state(self, state)
508 qtutils.apply_window_state(self, state, 1)
510 def export_state(self):
511 """Exports data for save/restore"""
512 state = standard.MainWindow.export_state(self)
513 return qtutils.export_window_state(self, state, self.widget_version)
515 def _load_gui_state(self):
516 """Restores the gui from the preferences file."""
517 self._gui_state_task = self._start_gui_state_loading_thread()
519 def _start_gui_state_loading_thread(self):
520 """Do expensive file reading and json decoding in the background"""
521 class LoadGUIStateTask(QtCore.QRunnable):
522 def __init__(self, sender):
523 QtCore.QRunnable.__init__(self)
524 self._sender = sender
525 def run(self):
526 state = settings.Settings().get_gui_state(self._sender)
527 self._sender.emit(SIGNAL('apply_state'), state)
529 task = LoadGUIStateTask(self)
530 QtCore.QThreadPool.globalInstance().start(task)
531 return task
533 def setup_dockwidget_tools_menu(self):
534 # Hotkeys for toggling the dock widgets
535 dockwidgets = (
536 (self.logdockwidget, 'Alt+0'),
537 (self.commitdockwidget, 'Alt+1'),
538 (self.statusdockwidget, 'Alt+2'),
539 (self.diffdockwidget, 'Alt+3'),
540 (self.actionsdockwidget, 'Alt+4'),
542 for dockwidget, shortcut in dockwidgets:
543 # Associate the action with the shortcut
544 action = dockwidget.toggleViewAction()
545 action.setShortcut(shortcut)
546 self.tools_menu.addAction(action)
547 def showdock(show, dockwidget=dockwidget):
548 if show:
549 dockwidget.raise_()
550 dockwidget.widget().setFocus(True)
551 else:
552 self.setFocus(True)
553 self.addAction(action)
554 connect_action_bool(action, showdock)
556 # Create a new shortcut Shift+<shortcut> that gives focus
557 action = QtGui.QAction(self)
558 action.setShortcut('Shift+' + shortcut)
559 def focusdock(dockwidget=dockwidget, showdock=showdock):
560 if dockwidget.toggleViewAction().isChecked():
561 showdock(True)
562 else:
563 dockwidget.toggleViewAction().trigger()
564 self.addAction(action)
565 connect_action(action, focusdock)
567 def save_archive(self):
568 ref = git.rev_parse('HEAD')
569 shortref = ref[:7]
570 GitArchiveDialog.save(ref, shortref, self)
572 def stage(self):
573 """Stage selected files, or all files if no selection exists."""
574 paths = cola.selection_model().unstaged
575 if not paths:
576 cola.notifier().broadcast(signals.stage_modified)
577 else:
578 cola.notifier().broadcast(signals.stage, paths)
580 def unstage(self):
581 """Unstage selected files, or all files if no selection exists."""
582 paths = cola.selection_model().staged
583 if not paths:
584 cola.notifier().broadcast(signals.unstage_all)
585 else:
586 cola.notifier().broadcast(signals.unstage, paths)
588 def dragEnterEvent(self, event):
589 """Accepts drops"""
590 standard.MainWindow.dragEnterEvent(self, event)
591 event.acceptProposedAction()
593 def dropEvent(self, event):
594 """Apply dropped patches with git-am"""
595 event.accept()
596 urls = event.mimeData().urls()
597 if not urls:
598 return
599 paths = map(lambda x: unicode(x.path()), urls)
600 patches = [p for p in paths if p.endswith('.patch')]
601 dirs = [p for p in paths if os.path.isdir(p)]
602 dirs.sort()
603 for d in dirs:
604 patches.extend(self._gather_patches(d))
605 # Broadcast the patches to apply
606 cola.notifier().broadcast(signals.apply_patches, patches)
608 def _gather_patches(self, path):
609 """Find patches in a subdirectory"""
610 patches = []
611 for root, subdirs, files in os.walk(path):
612 for name in [f for f in files if f.endswith('.patch')]:
613 patches.append(os.path.join(root, name))
614 return patches
616 def show_cursor_position(self, rows, cols):
617 display = '&nbsp;%02d:%02d&nbsp;' % (rows, cols)
618 if cols > 78:
619 display = ('<span style="color: white; '
620 ' background-color: red;"'
621 '>%s</span>' % display)
622 elif cols > 72:
623 display = ('<span style="color: black; '
624 ' background-color: orange;"'
625 '>%s</span>' % display)
626 elif cols > 64:
627 display = ('<span style="color: black; '
628 ' background-color: yellow;"'
629 '>%s</span>' % display)
630 else:
631 display = ('<span style="color: grey;">%s</span>' % display)
633 self.position_label.setText(display)