Mercurial > traipse
comparison upmana/mercurial/ui.py @ 28:ff154cf3350c ornery-orc
Traipse 'OpenRPG' {100203-00}
Traipse is a distribution of OpenRPG that is designed to be easy to
setup and go. Traipse also makes it easy for developers to work on code
without fear of sacrifice. 'Ornery-Orc' continues the trend of 'Grumpy'
and adds fixes to the code. 'Ornery-Orc's main goal is to offer more
advanced features and enhance the productivity of the user.
Update Summary (Stable)
New Features:
New Bookmarks Feature
New 'boot' command to remote admin
New confirmation window for sent nodes
Miniatures Layer pop up box allows users to turn off Mini labels, from
FlexiRPG
New Zoom Mouse plugin added
New Images added to Plugin UI
Switching to Element Tree
New Map efficiency, from FlexiRPG
New Status Bar to Update Manager
New TrueDebug Class in orpg_log (See documentation for usage)
New Portable Mercurial
New Tip of the Day, from Core and community
New Reference Syntax added for custom PC sheets
New Child Reference for gametree
New Parent Reference for gametree
New Gametree Recursion method, mapping, context sensitivity, and
effeciency..
New Features node with bonus nodes and Node Referencing help added
New Dieroller structure from Core
New DieRoller portability for odd Dice
New 7th Sea die roller; ie [7k3] = [7d10.takeHighest(3).open(10)]
New 'Mythos' System die roller added
New vs. die roller method for WoD; ie [3v3] = [3d10.vs(3)]. Included for
Mythos roller also
New Warhammer FRPG Die Roller (Special thanks to Puu-san for the
support)
New EZ_Tree Reference system. Push a button, Traipse the tree, get a
reference (Beta!)
New Grids act more like Spreadsheets in Use mode, with Auto Calc
Fixes:
Fix to allow for portability to an OpenSUSE linux OS
Fix to mplay_client for Fedora and OpenSUSE
Fix to Text based Server
Fix to Remote Admin Commands
Fix to Pretty Print, from Core
Fix to Splitter Nodes not being created
Fix to massive amounts of images loading, from Core
Fix to Map from gametree not showing to all clients
Fix to gametree about menus
Fix to Password Manager check on startup
Fix to PC Sheets from tool nodes. They now use the tabber_panel
Fix to Whiteboard ID to prevent random line or text deleting.
Fixes to Server, Remote Server, and Server GUI
Fix to Update Manager; cleaner clode for saved repositories
Fixes made to Settings Panel and now reactive settings when Ok is
pressed
Fixes to Alternity roller's attack roll. Uses a simple Tuple instead of
a Splice
Fix to Use panel of Forms and Tabbers. Now longer enters design mode
Fix made Image Fetching. New fetching image and new failed image
Fix to whiteboard ID's to prevent non updated clients from ruining the
fix.
default_manifest.xml renamed to default_upmana.xml
author | sirebral |
---|---|
date | Wed, 03 Feb 2010 22:16:49 -0600 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
27:51428d30c59e | 28:ff154cf3350c |
---|---|
1 # ui.py - user interface bits for mercurial | |
2 # | |
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com> | |
4 # | |
5 # This software may be used and distributed according to the terms of the | |
6 # GNU General Public License version 2, incorporated herein by reference. | |
7 | |
8 from i18n import _ | |
9 import errno, getpass, os, socket, sys, tempfile, traceback | |
10 import config, util, error | |
11 | |
12 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, | |
13 '0': False, 'no': False, 'false': False, 'off': False} | |
14 | |
15 class ui(object): | |
16 def __init__(self, src=None): | |
17 self._buffers = [] | |
18 self.quiet = self.verbose = self.debugflag = self._traceback = False | |
19 self._reportuntrusted = True | |
20 self._ocfg = config.config() # overlay | |
21 self._tcfg = config.config() # trusted | |
22 self._ucfg = config.config() # untrusted | |
23 self._trustusers = set() | |
24 self._trustgroups = set() | |
25 | |
26 if src: | |
27 self._tcfg = src._tcfg.copy() | |
28 self._ucfg = src._ucfg.copy() | |
29 self._ocfg = src._ocfg.copy() | |
30 self._trustusers = src._trustusers.copy() | |
31 self._trustgroups = src._trustgroups.copy() | |
32 self.fixconfig() | |
33 else: | |
34 # we always trust global config files | |
35 for f in util.rcpath(): | |
36 self.readconfig(f, trust=True) | |
37 | |
38 def copy(self): | |
39 return self.__class__(self) | |
40 | |
41 def _is_trusted(self, fp, f): | |
42 st = util.fstat(fp) | |
43 if util.isowner(st): | |
44 return True | |
45 | |
46 tusers, tgroups = self._trustusers, self._trustgroups | |
47 if '*' in tusers or '*' in tgroups: | |
48 return True | |
49 | |
50 user = util.username(st.st_uid) | |
51 group = util.groupname(st.st_gid) | |
52 if user in tusers or group in tgroups or user == util.username(): | |
53 return True | |
54 | |
55 if self._reportuntrusted: | |
56 self.warn(_('Not trusting file %s from untrusted ' | |
57 'user %s, group %s\n') % (f, user, group)) | |
58 return False | |
59 | |
60 def readconfig(self, filename, root=None, trust=False, | |
61 sections=None, remap=None): | |
62 try: | |
63 fp = open(filename) | |
64 except IOError: | |
65 if not sections: # ignore unless we were looking for something | |
66 return | |
67 raise | |
68 | |
69 cfg = config.config() | |
70 trusted = sections or trust or self._is_trusted(fp, filename) | |
71 | |
72 try: | |
73 cfg.read(filename, fp, sections=sections, remap=remap) | |
74 except error.ConfigError, inst: | |
75 if trusted: | |
76 raise | |
77 self.warn(_("Ignored: %s\n") % str(inst)) | |
78 | |
79 if trusted: | |
80 self._tcfg.update(cfg) | |
81 self._tcfg.update(self._ocfg) | |
82 self._ucfg.update(cfg) | |
83 self._ucfg.update(self._ocfg) | |
84 | |
85 if root is None: | |
86 root = os.path.expanduser('~') | |
87 self.fixconfig(root=root) | |
88 | |
89 def fixconfig(self, root=None): | |
90 # translate paths relative to root (or home) into absolute paths | |
91 root = root or os.getcwd() | |
92 for c in self._tcfg, self._ucfg, self._ocfg: | |
93 for n, p in c.items('paths'): | |
94 if p and "://" not in p and not os.path.isabs(p): | |
95 c.set("paths", n, os.path.normpath(os.path.join(root, p))) | |
96 | |
97 # update ui options | |
98 self.debugflag = self.configbool('ui', 'debug') | |
99 self.verbose = self.debugflag or self.configbool('ui', 'verbose') | |
100 self.quiet = not self.debugflag and self.configbool('ui', 'quiet') | |
101 if self.verbose and self.quiet: | |
102 self.quiet = self.verbose = False | |
103 self._reportuntrusted = self.configbool("ui", "report_untrusted", True) | |
104 self._traceback = self.configbool('ui', 'traceback', False) | |
105 | |
106 # update trust information | |
107 self._trustusers.update(self.configlist('trusted', 'users')) | |
108 self._trustgroups.update(self.configlist('trusted', 'groups')) | |
109 | |
110 def setconfig(self, section, name, value): | |
111 for cfg in (self._ocfg, self._tcfg, self._ucfg): | |
112 cfg.set(section, name, value) | |
113 self.fixconfig() | |
114 | |
115 def _data(self, untrusted): | |
116 return untrusted and self._ucfg or self._tcfg | |
117 | |
118 def configsource(self, section, name, untrusted=False): | |
119 return self._data(untrusted).source(section, name) or 'none' | |
120 | |
121 def config(self, section, name, default=None, untrusted=False): | |
122 value = self._data(untrusted).get(section, name, default) | |
123 if self.debugflag and not untrusted and self._reportuntrusted: | |
124 uvalue = self._ucfg.get(section, name) | |
125 if uvalue is not None and uvalue != value: | |
126 self.debug(_("ignoring untrusted configuration option " | |
127 "%s.%s = %s\n") % (section, name, uvalue)) | |
128 return value | |
129 | |
130 def configbool(self, section, name, default=False, untrusted=False): | |
131 v = self.config(section, name, None, untrusted) | |
132 if v is None: | |
133 return default | |
134 if v.lower() not in _booleans: | |
135 raise error.ConfigError(_("%s.%s not a boolean ('%s')") | |
136 % (section, name, v)) | |
137 return _booleans[v.lower()] | |
138 | |
139 def configlist(self, section, name, default=None, untrusted=False): | |
140 """Return a list of comma/space separated strings""" | |
141 result = self.config(section, name, untrusted=untrusted) | |
142 if result is None: | |
143 result = default or [] | |
144 if isinstance(result, basestring): | |
145 result = result.replace(",", " ").split() | |
146 return result | |
147 | |
148 def has_section(self, section, untrusted=False): | |
149 '''tell whether section exists in config.''' | |
150 return section in self._data(untrusted) | |
151 | |
152 def configitems(self, section, untrusted=False): | |
153 items = self._data(untrusted).items(section) | |
154 if self.debugflag and not untrusted and self._reportuntrusted: | |
155 for k, v in self._ucfg.items(section): | |
156 if self._tcfg.get(section, k) != v: | |
157 self.debug(_("ignoring untrusted configuration option " | |
158 "%s.%s = %s\n") % (section, k, v)) | |
159 return items | |
160 | |
161 def walkconfig(self, untrusted=False): | |
162 cfg = self._data(untrusted) | |
163 for section in cfg.sections(): | |
164 for name, value in self.configitems(section, untrusted): | |
165 yield section, name, str(value).replace('\n', '\\n') | |
166 | |
167 def username(self): | |
168 """Return default username to be used in commits. | |
169 | |
170 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL | |
171 and stop searching if one of these is set. | |
172 If not found and ui.askusername is True, ask the user, else use | |
173 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname". | |
174 """ | |
175 user = os.environ.get("HGUSER") | |
176 if user is None: | |
177 user = self.config("ui", "username") | |
178 if user is None: | |
179 user = os.environ.get("EMAIL") | |
180 if user is None and self.configbool("ui", "askusername"): | |
181 user = self.prompt(_("enter a commit username:"), default=None) | |
182 if user is None: | |
183 try: | |
184 user = '%s@%s' % (util.getuser(), socket.getfqdn()) | |
185 self.warn(_("No username found, using '%s' instead\n") % user) | |
186 except KeyError: | |
187 pass | |
188 if not user: | |
189 raise util.Abort(_("Please specify a username.")) | |
190 if "\n" in user: | |
191 raise util.Abort(_("username %s contains a newline\n") % repr(user)) | |
192 return user | |
193 | |
194 def shortuser(self, user): | |
195 """Return a short representation of a user name or email address.""" | |
196 if not self.verbose: user = util.shortuser(user) | |
197 return user | |
198 | |
199 def _path(self, loc): | |
200 p = self.config('paths', loc) | |
201 if p and '%%' in p: | |
202 self.warn('(deprecated \'%%\' in path %s=%s from %s)\n' % | |
203 (loc, p, self.configsource('paths', loc))) | |
204 p = p.replace('%%', '%') | |
205 return p | |
206 | |
207 def expandpath(self, loc, default=None): | |
208 """Return repository location relative to cwd or from [paths]""" | |
209 if "://" in loc or os.path.isdir(os.path.join(loc, '.hg')): | |
210 return loc | |
211 | |
212 path = self._path(loc) | |
213 if not path and default is not None: | |
214 path = self._path(default) | |
215 return path or loc | |
216 | |
217 def pushbuffer(self): | |
218 self._buffers.append([]) | |
219 | |
220 def popbuffer(self): | |
221 return "".join(self._buffers.pop()) | |
222 | |
223 def write(self, *args): | |
224 if self._buffers: | |
225 self._buffers[-1].extend([str(a) for a in args]) | |
226 else: | |
227 for a in args: | |
228 sys.stdout.write(str(a)) | |
229 | |
230 def write_err(self, *args): | |
231 try: | |
232 if not sys.stdout.closed: sys.stdout.flush() | |
233 for a in args: | |
234 sys.stderr.write(str(a)) | |
235 # stderr may be buffered under win32 when redirected to files, | |
236 # including stdout. | |
237 if not sys.stderr.closed: sys.stderr.flush() | |
238 except IOError, inst: | |
239 if inst.errno != errno.EPIPE: | |
240 raise | |
241 | |
242 def flush(self): | |
243 try: sys.stdout.flush() | |
244 except: pass | |
245 try: sys.stderr.flush() | |
246 except: pass | |
247 | |
248 def interactive(self): | |
249 i = self.configbool("ui", "interactive", None) | |
250 if i is None: | |
251 return sys.stdin.isatty() | |
252 return i | |
253 | |
254 def _readline(self, prompt=''): | |
255 if sys.stdin.isatty(): | |
256 try: | |
257 # magically add command line editing support, where | |
258 # available | |
259 import readline | |
260 # force demandimport to really load the module | |
261 readline.read_history_file | |
262 # windows sometimes raises something other than ImportError | |
263 except Exception: | |
264 pass | |
265 line = raw_input(prompt) | |
266 # When stdin is in binary mode on Windows, it can cause | |
267 # raw_input() to emit an extra trailing carriage return | |
268 if os.linesep == '\r\n' and line and line[-1] == '\r': | |
269 line = line[:-1] | |
270 return line | |
271 | |
272 def prompt(self, msg, choices=None, default="y"): | |
273 """Prompt user with msg, read response, and ensure it matches | |
274 one of the provided choices. choices is a sequence of acceptable | |
275 responses with the format: ('&None', 'E&xec', 'Sym&link') | |
276 No sequence implies no response checking. Responses are case | |
277 insensitive. If ui is not interactive, the default is returned. | |
278 """ | |
279 if not self.interactive(): | |
280 self.write(msg, ' ', default, "\n") | |
281 return default | |
282 while True: | |
283 try: | |
284 r = self._readline(msg + ' ') | |
285 if not r: | |
286 return default | |
287 if not choices: | |
288 return r | |
289 resps = [s[s.index('&')+1].lower() for s in choices] | |
290 if r.lower() in resps: | |
291 return r.lower() | |
292 else: | |
293 self.write(_("unrecognized response\n")) | |
294 except EOFError: | |
295 raise util.Abort(_('response expected')) | |
296 | |
297 def getpass(self, prompt=None, default=None): | |
298 if not self.interactive(): return default | |
299 try: | |
300 return getpass.getpass(prompt or _('password: ')) | |
301 except EOFError: | |
302 raise util.Abort(_('response expected')) | |
303 def status(self, *msg): | |
304 if not self.quiet: self.write(*msg) | |
305 def warn(self, *msg): | |
306 self.write_err(*msg) | |
307 def note(self, *msg): | |
308 if self.verbose: self.write(*msg) | |
309 def debug(self, *msg): | |
310 if self.debugflag: self.write(*msg) | |
311 def edit(self, text, user): | |
312 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt", | |
313 text=True) | |
314 try: | |
315 f = os.fdopen(fd, "w") | |
316 f.write(text) | |
317 f.close() | |
318 | |
319 editor = self.geteditor() | |
320 | |
321 util.system("%s \"%s\"" % (editor, name), | |
322 environ={'HGUSER': user}, | |
323 onerr=util.Abort, errprefix=_("edit failed")) | |
324 | |
325 f = open(name) | |
326 t = f.read() | |
327 f.close() | |
328 finally: | |
329 os.unlink(name) | |
330 | |
331 return t | |
332 | |
333 def traceback(self): | |
334 '''print exception traceback if traceback printing enabled. | |
335 only to call in exception handler. returns true if traceback | |
336 printed.''' | |
337 if self._traceback: | |
338 traceback.print_exc() | |
339 return self._traceback | |
340 | |
341 def geteditor(self): | |
342 '''return editor to use''' | |
343 return (os.environ.get("HGEDITOR") or | |
344 self.config("ui", "editor") or | |
345 os.environ.get("VISUAL") or | |
346 os.environ.get("EDITOR", "vi")) |