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

Archive Download this file

Branches

Tags

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