1 # Copyright (C) 2009, David Aguilar <davvid@gmail.com>
2 """Provides the main() routine and ColaApplicaiton"""
11 from PyQt4
import QtGui
12 from PyQt4
import QtCore
13 from PyQt4
.QtCore
import SIGNAL
15 print >> sys
.stderr
, 'Sorry, you do not seem to have PyQt4 installed.'
16 print >> sys
.stderr
, 'Please install it before using git-cola.'
17 print >> sys
.stderr
, 'e.g.: sudo apt-get install python-qt4'
25 from cola
import guicmds
26 from cola
import inotify
28 from cola
import qtcompat
29 from cola
import qtutils
30 from cola
import resources
31 from cola
import signals
32 from cola
import utils
33 from cola
import version
34 from cola
.controllers
.createtag
import create_tag
35 from cola
.classic
import cola_classic
36 from cola
.dag
import git_dag
37 from cola
.stash
import stash
38 from cola
.decorators
import memoize
39 from cola
.main
.view
import MainView
40 from cola
.main
.controller
import MainController
41 from cola
.widgets
import cfgactions
42 from cola
.widgets
import startup
45 def setup_environment():
46 # Spoof an X11 display for SSH
47 os
.environ
.setdefault('DISPLAY', ':0')
49 # Provide an SSH_ASKPASS fallback
50 if sys
.platform
== 'darwin':
51 os
.environ
.setdefault('SSH_ASKPASS',
52 resources
.share('bin', 'ssh-askpass-darwin'))
54 os
.environ
.setdefault('SSH_ASKPASS',
55 resources
.share('bin', 'ssh-askpass'))
57 # Setup the path so that git finds us when we run 'git cola'
58 path_entries
= os
.environ
.get('PATH').split(os
.pathsep
)
59 bindir
= os
.path
.dirname(os
.path
.abspath(__file__
))
60 path_entries
.insert(0, bindir
)
61 path
= os
.pathsep
.join(path_entries
)
62 os
.environ
['PATH'] = path
63 os
.putenv('PATH', path
)
68 return QtGui
.QApplication(list(argv
))
71 # style note: we use camelCase here since we're masquerading a Qt class
72 class ColaApplication(object):
73 """The main cola application
75 ColaApplication handles i18n of user-visible data
78 def __init__(self
, argv
, locale
=None, gui
=True):
79 """Initialize our QApplication for translation
84 # Add the default style dir so that we find our icons
85 icon_dir
= resources
.icon_dir()
86 qtcompat
.add_search_path(os
.path
.basename(icon_dir
), icon_dir
)
88 # monkey-patch Qt's translate() to use our translate()
90 self
._app
= instance(tuple(argv
))
91 self
._app
.setWindowIcon(qtutils
.git_icon())
92 self
._translate
_base
= QtGui
.QApplication
.translate
93 QtGui
.QApplication
.translate
= self
.translate
95 self
._app
= QtCore
.QCoreApplication(argv
)
96 self
._translate
_base
= QtCore
.QCoreApplication
.translate
97 QtCore
.QCoreApplication
.translate
= self
.translate
99 # Register model commands
102 # Make file descriptors binary for win32
103 utils
.set_binary(sys
.stdin
)
104 utils
.set_binary(sys
.stdout
)
105 utils
.set_binary(sys
.stderr
)
107 def translate(self
, domain
, txt
):
109 Translate strings with gettext
111 Supports @@noun/@@verb specifiers.
114 trtxt
= i18n
.gettext(txt
)
115 if trtxt
[-6:-4] == '@@': # handle @@verb / @@noun
119 def activeWindow(self
):
120 """Wrap activeWindow()"""
121 return self
._app
.activeWindow()
125 return self
._app
.exec_()
128 def parse_args(context
):
129 parser
= optparse
.OptionParser(usage
='%prog [options]')
131 # We also accept 'git cola version'
132 parser
.add_option('-v', '--version',
133 help='Show cola version',
138 # Specifies a git repository to open
139 parser
.add_option('-r', '--repo',
140 help='Specifies the path to a git repository.',
145 # Specifies that we should prompt for a repository at startup
146 parser
.add_option('--prompt',
147 help='Prompt for a repository before starting the main GUI.',
152 # Used on Windows for adding 'git' to the path
153 parser
.add_option('-g', '--git-path',
154 help='Specifies the path to the git binary',
159 if context
== 'git-dag':
160 parser
.add_option('-c', '--count',
161 help='Number of commits to display.',
166 return parser
.parse_args()
169 def process_args(opts
, args
):
170 if opts
.version
or (args
and args
[0] == 'version'):
171 # Accept 'git cola --version' or 'git cola version'
172 print 'cola version', version
.version()
176 # Adds git to the PATH. This is needed on Windows.
177 path_entries
= os
.environ
.get('PATH', '').split(os
.pathsep
)
178 path_entries
.insert(0, os
.path
.dirname(opts
.git
))
179 os
.environ
['PATH'] = os
.pathsep
.join(path_entries
)
181 # Bail out if --repo is not a directory
182 repo
= os
.path
.realpath(opts
.repo
)
183 if not os
.path
.isdir(repo
):
184 print >> sys
.stderr
, "fatal: '%s' is not a directory. Consider supplying -r <path>.\n" % repo
187 # We do everything relative to the repo root
194 """Parses the command-line arguments and starts git-cola
197 opts
, args
= parse_args(context
)
198 repo
= process_args(opts
, args
)
200 # Allow Ctrl-C to exit
201 signal
.signal(signal
.SIGINT
, signal
.SIG_DFL
)
204 app
= ColaApplication(sys
.argv
)
206 # Ensure that we're working in a valid git repository.
207 # If not, try to find one. When found, chdir there.
209 valid
= model
.set_worktree(repo
) and not opts
.prompt
211 startup_dlg
= startup
.StartupDialog(app
.activeWindow())
212 gitdir
= startup_dlg
.find_git_repo()
215 valid
= model
.set_worktree(gitdir
)
217 # Finally, go to the root of the git repo
218 os
.chdir(model
.git
.worktree())
220 # Prepare to launch a sub-command
221 builtins
= set(('cola',
230 if context
!= 'git-dag' and args
and args
[0] in builtins
:
234 if context
== 'git-cola' or context
== 'cola':
235 view
= MainView(model
, qtutils
.active_window())
236 ctl
= MainController(model
, view
)
237 elif context
== 'git-dag' or context
== 'dag':
238 ctl
= git_dag(model
, opts
=opts
, args
=args
)
240 elif context
== 'classic':
241 view
= cola_classic(update
=False)
242 # TODO: the calls to update_status() can be done asynchronously
243 # by hooking into the message_updated notification.
244 elif context
== 'stash':
245 model
.update_status()
247 elif context
== 'fetch':
248 model
.update_status()
249 view
= guicmds
.fetch().view
250 elif context
== 'pull':
251 model
.update_status()
252 view
= guicmds
.pull().view
253 elif context
== 'push':
254 model
.update_status()
255 view
= guicmds
.push().view
256 elif context
== 'tag':
257 view
= create_tag().view
259 # Install UI wrappers for command objects
260 cfgactions
.install_command_wrapper()
261 guicmds
.install_command_wrapper()
263 # Make sure that we start out on top
267 # Scan for the first time
268 task
= _start_update_thread(model
)
270 # Start the inotify thread
273 msg_timer
= QtCore
.QTimer()
274 msg_timer
.setSingleShot(True)
275 msg_timer
.connect(msg_timer
, SIGNAL('timeout()'), _send_msg
)
278 # Start the event loop
283 QtCore
.QThreadPool
.globalInstance().waitForDone()
285 pattern
= cola
.model().tmp_file_pattern()
286 for filename
in glob
.glob(pattern
):
293 def _start_update_thread(model
):
294 """Update the model in the background
296 git-cola should startup as quickly as possible.
299 class UpdateTask(QtCore
.QRunnable
):
301 model
.update_status(update_index
=True)
303 # Hold onto a reference to prevent PyQt from dereferencing
305 QtCore
.QThreadPool
.globalInstance().start(task
)
311 if git
.GIT_COLA_TRACE
== 'trace':
312 msg
= ('info: Trace enabled. '
313 'Many of commands reported with "trace" use git\'s stable '
314 '"plumbing" API and are not intended for typical '
315 'day-to-day use. Here be dragons')
316 cola
.notifier().broadcast(signals
.log_cmd
, 0, msg
)