monotone

monotone Mtn Source Tree

Root/ui.cc

1// -*- mode: C++; c-file-style: "gnu"; indent-tabs-mode: nil -*-
2// copyright (C) 2002, 2003 graydon hoare <graydon@pobox.com>
3// all rights reserved.
4// licensed to the public under the terms of the GNU GPL (>= 2)
5// see the file COPYING for details
6
7// this file contains a couple utilities to deal with the user
8// interface. the global user_interface object 'ui' owns clog, so no
9// writing to it directly!
10
11#include "config.h"
12#include "platform.hh"
13#include "sanity.hh"
14#include "ui.hh"
15#include "transforms.hh"
16#include "constants.hh"
17
18#include <iostream>
19#include <iomanip>
20#include <boost/lexical_cast.hpp>
21
22using namespace std;
23using boost::lexical_cast;
24struct user_interface ui;
25
26ticker::ticker(string const & tickname, std::string const & s, size_t mod,
27 bool kilocount) :
28 ticks(0),
29 mod(mod),
30 kilocount(kilocount),
31 name(tickname),
32 shortname(s)
33{
34 I(ui.tickers.find(tickname) == ui.tickers.end());
35 ui.tickers.insert(make_pair(tickname,this));
36}
37
38ticker::~ticker()
39{
40 I(ui.tickers.find(name) != ui.tickers.end());
41 if (ui.some_tick_is_dirty)
42 {
43 ui.write_ticks();
44 }
45 ui.tickers.erase(name);
46 ui.finish_ticking();
47}
48
49void
50ticker::operator++()
51{
52 I(ui.tickers.find(name) != ui.tickers.end());
53 ticks++;
54 ui.some_tick_is_dirty = true;
55 if (ticks % mod == 0)
56 ui.write_ticks();
57}
58
59void
60ticker::operator+=(size_t t)
61{
62 I(ui.tickers.find(name) != ui.tickers.end());
63 size_t old = ticks;
64
65 ticks += t;
66 if (t != 0)
67 {
68 ui.some_tick_is_dirty = true;
69 if (ticks % mod == 0 || (ticks / mod) > (old / mod))
70 ui.write_ticks();
71 }
72}
73
74
75tick_write_count::tick_write_count() : last_tick_len(0)
76{
77}
78
79tick_write_count::~tick_write_count()
80{
81}
82
83void tick_write_count::write_ticks()
84{
85 string tickline1, tickline2;
86 bool first_tick = true;
87
88 tickline1 = "monotone: ";
89 tickline2 = "\rmonotone:";
90
91 unsigned int width;
92 unsigned int minwidth = 7;
93 for (map<string,ticker *>::const_iterator i = ui.tickers.begin();
94 i != ui.tickers.end(); ++i)
95 {
96 width = 1 + length(utf8(i->second->name));
97 if (!first_tick)
98 {
99 tickline1 += " | ";
100 tickline2 += " |";
101 }
102 first_tick = false;
103 if (length(utf8(i->second->name)) < minwidth)
104 {
105 tickline1.append(minwidth - length(utf8(i->second->name)),' ');
106 width += minwidth - length(utf8(i->second->name));
107 }
108 tickline1 += i->second->name;
109
110 string count;
111 if (i->second->kilocount && i->second->ticks >= 10000)
112 { // automatic unit conversion is enabled
113 float div;
114 const char *message;
115 if (i->second->ticks >= 1048576) {
116 // ticks >=1MB, use Mb
117 div = 1048576;
118 // xgettext: mebibytes (2^20 bytes)
119 message = N_("%.1f M");
120 } else {
121 // ticks <1MB, use kb
122 div = 1024;
123 // xgettext: kibibytes (2^10 bytes)
124 message = N_("%.1f k");
125 }
126 // we reset the mod to the divider, to avoid spurious screen updates
127 i->second->mod = static_cast<int>(div / 10.0);
128 count = (F(message) % (i->second->ticks / div)).str();
129 }
130 else
131 {
132 // xgettext: bytes
133 count = (F("%d") % i->second->ticks).str();
134 }
135
136 if (length(utf8(count)) < width)
137 {
138 tickline2.append(width - length(utf8(count)),' ');
139 }
140 else if (length(utf8(count)) > width)
141 {
142 // FIXME: not quite right, because substr acts on bytes rather than
143 // characters; but there are always more bytes than characters, so
144 // at worst this will just chop off a little too much.
145 count = count.substr(length(utf8(count)) - width);
146 }
147 tickline2 += count;
148 }
149
150 if (!ui.tick_trailer.empty())
151 {
152 tickline2 += " ";
153 tickline2 += ui.tick_trailer;
154 }
155
156 size_t curr_sz = length(utf8(tickline2));
157 if (curr_sz < last_tick_len)
158 tickline2.append(last_tick_len - curr_sz, ' ');
159 last_tick_len = curr_sz;
160
161 unsigned int tw = terminal_width();
162 if(!ui.last_write_was_a_tick)
163 {
164 if (tw && length(utf8(tickline1)) > tw)
165 {
166 // FIXME: may chop off more than necessary (because we chop by
167 // bytes, not by characters)
168 tickline1.resize(tw);
169 }
170 clog << tickline1 << "\n";
171 }
172 if (tw && length(utf8(tickline2)) > tw)
173 {
174 // first character in tickline2 is "\r", which does not take up any
175 // width, so we add 1 to compensate.
176 // FIXME: may chop off more than necessary (because we chop by
177 // bytes, not by characters)
178 tickline2.resize(tw + 1);
179 }
180 clog << tickline2;
181 clog.flush();
182}
183
184void tick_write_count::clear_line()
185{
186 clog << endl;
187}
188
189
190tick_write_dot::tick_write_dot()
191{
192}
193
194tick_write_dot::~tick_write_dot()
195{
196}
197
198void tick_write_dot::write_ticks()
199{
200 static const string tickline_prefix = "monotone: ";
201 string tickline1, tickline2;
202 bool first_tick = true;
203
204 if (ui.last_write_was_a_tick)
205 {
206 tickline1 = "";
207 tickline2 = "";
208 }
209 else
210 {
211 tickline1 = "monotone: ticks: ";
212 tickline2 = "\n" + tickline_prefix;
213 chars_on_line = tickline_prefix.size();
214 }
215
216 for (map<string,ticker *>::const_iterator i = ui.tickers.begin();
217 i != ui.tickers.end(); ++i)
218 {
219 map<string,size_t>::const_iterator old = last_ticks.find(i->first);
220
221 if (!ui.last_write_was_a_tick)
222 {
223 if (!first_tick)
224 tickline1 += ", ";
225
226 tickline1 +=
227 i->second->shortname + "=\"" + i->second->name + "\""
228 + "/" + lexical_cast<string>(i->second->mod);
229 first_tick = false;
230 }
231
232 if (old == last_ticks.end()
233 || ((i->second->ticks / i->second->mod)
234 > (old->second / i->second->mod)))
235 {
236 chars_on_line += i->second->shortname.size();
237 if (chars_on_line > guess_terminal_width())
238 {
239 chars_on_line = tickline_prefix.size() + i->second->shortname.size();
240 tickline2 += "\n" + tickline_prefix;
241 }
242 tickline2 += i->second->shortname;
243
244 if (old == last_ticks.end())
245 last_ticks.insert(make_pair(i->first, i->second->ticks));
246 else
247 last_ticks[i->first] = i->second->ticks;
248 }
249 }
250
251 clog << tickline1 << tickline2;
252 clog.flush();
253}
254
255void tick_write_dot::clear_line()
256{
257 clog << endl;
258}
259
260
261user_interface::user_interface() :
262 last_write_was_a_tick(false),
263 t_writer(0)
264{
265#ifndef WIN32
266 clog.sync_with_stdio(false);
267#endif
268 clog.unsetf(ios_base::unitbuf);
269 if (have_smart_terminal())
270 set_tick_writer(new tick_write_count);
271 else
272 set_tick_writer(new tick_write_dot);
273}
274
275user_interface::~user_interface()
276{
277 delete t_writer;
278}
279
280void
281user_interface::finish_ticking()
282{
283 if (tickers.size() == 0 &&
284 last_write_was_a_tick)
285 {
286 tick_trailer = "";
287 t_writer->clear_line();
288 last_write_was_a_tick = false;
289 }
290}
291
292void
293user_interface::set_tick_trailer(string const & t)
294{
295 tick_trailer = t;
296}
297
298void
299user_interface::set_tick_writer(tick_writer * t)
300{
301 if (t_writer != 0)
302 delete t_writer;
303 t_writer = t;
304}
305
306void
307user_interface::write_ticks()
308{
309 t_writer->write_ticks();
310 last_write_was_a_tick = true;
311 some_tick_is_dirty = false;
312}
313
314void
315user_interface::warn(string const & warning)
316{
317 if (issued_warnings.find(warning) == issued_warnings.end())
318 inform("warning: " + warning);
319 issued_warnings.insert(warning);
320}
321
322void
323user_interface::fatal(string const & fatal)
324{
325 inform(F("fatal: %s\n"
326 "this is almost certainly a bug in monotone.\n"
327 "please send this error message, the output of 'monotone --full-version',\n"
328 "and a description of what you were doing to %s.\n")
329 % fatal % PACKAGE_BUGREPORT);
330}
331
332
333static inline string
334sanitize(string const & line)
335{
336 // FIXME: you might want to adjust this if you're using a charset
337 // which has safe values in the sub-0x20 range. ASCII, UTF-8,
338 // and most ISO8859-x sets do not.
339 string tmp;
340 tmp.reserve(line.size());
341 for (size_t i = 0; i < line.size(); ++i)
342 {
343 if ((line[i] == '\n')
344 || (static_cast<unsigned char>(line[i]) >= static_cast<unsigned char>(0x20)
345 && line[i] != static_cast<char>(0x7F)))
346 tmp += line[i];
347 else
348 tmp += ' ';
349 }
350 return tmp;
351}
352
353void
354user_interface::ensure_clean_line()
355{
356 if (last_write_was_a_tick)
357 {
358 write_ticks();
359 t_writer->clear_line();
360 }
361 last_write_was_a_tick = false;
362}
363
364void
365user_interface::inform(string const & line)
366{
367 string prefixedLine;
368 prefix_lines_with(_("monotone: "), line, prefixedLine);
369 ensure_clean_line();
370 clog << sanitize(prefixedLine) << endl;
371 clog.flush();
372}
373
374unsigned int
375guess_terminal_width()
376{
377 unsigned int w = terminal_width();
378 if (!w)
379 w = constants::default_terminal_width;
380 return w;
381}
382
383const locale &
384get_user_locale()
385{
386 // this is awkward because if LC_CTYPE is set to something the
387 // runtime doesn't know about, it will fail. in that case,
388 // the default will have to do.
389 static bool init = false;
390 static locale user_locale;
391 if (!init)
392 {
393 init = true;
394 try
395 {
396 user_locale = locale("");
397 }
398 catch( ... )
399 {}
400 }
401 return user_locale;
402}

Archive Download this file

Branches

Tags

Quick Links:     www.monotone.ca    -     Downloads    -     Documentation    -     Wiki    -     Code Forge    -     Build Status