FIX issue #19 where multiple widget values led to bad replacements in the command
[qgit4/redivivus.git] / src / namespace_def.cpp
blob486d7e172cf1fff2cafbf22e093393a9eb4f897c
1 /*
2 Author: Marco Costalba (C) 2005-2007
4 Copyright: See COPYING file that comes with this distribution
7 Definitions of complex namespace constants
9 Complex constant objects are not folded in like integral types, so they
10 are declared 'extern' in namespace to avoid duplicating them as file scope
11 data in each file where QGit namespace is included.
14 #include <QDir>
15 #include <QHash>
16 #include <QPixmap>
17 #include <QProcess>
18 #include <QSettings>
19 #include <QSplitter>
20 #include <QTemporaryFile>
21 #include <QTextStream>
22 #include <QWidget>
23 #include <QTextCodec>
24 #include "common.h"
25 #include "git.h"
26 #include "annotate.h"
28 #ifdef Q_OS_WIN32 // ********* platform dependent code ******
30 const QString QGit::SCRIPT_EXT = ".bat";
32 static void adjustPath(QStringList& args, bool* winShell) {
34 To run an application/script under Windows you need
35 to wrap the command line in the shell interpreter.
36 You need this also to start native commands as 'dir'.
37 An exception is if application is 'git' in that case we
38 call with absolute path to be sure to find it.
40 if (args.first() == "git" || args.first().startsWith("git-")) {
42 if (!QGit::GIT_DIR.isEmpty()) // application built from sources
43 args.first().prepend(QGit::GIT_DIR + '/');
45 if (winShell)
46 *winShell = false;
48 } else if (winShell) {
49 args.prepend("/c");
50 args.prepend("cmd.exe");
51 *winShell = true;
55 #elif defined(Q_OS_MACX) // MacOS X specific code
57 #include <sys/types.h> // used by chmod()
58 #include <sys/stat.h> // used by chmod()
60 const QString QGit::SCRIPT_EXT = ".sh";
62 static void adjustPath(QStringList& args, bool*) {
64 Under MacOS X, git typically doesn't live in the PATH
65 So use GIT_DIR from the settings if available
67 Note: I (OC) think that this should be the default behaviour,
68 but I don't want to break other platforms, so I introduced
69 the MacOS X special case. Feel free to make this the default if
70 you do feel the same.
72 if (args.first() == "git" || args.first().startsWith("git-")) {
74 if (!QGit::GIT_DIR.isEmpty()) // application built from sources
75 args.first().prepend(QGit::GIT_DIR + '/');
80 #else
82 #include <sys/types.h> // used by chmod()
83 #include <sys/stat.h> // used by chmod()
85 const QString QGit::SCRIPT_EXT = ".sh";
87 static void adjustPath(QStringList&, bool*) {}
89 #endif // ********* end of platform dependent code ******
91 // definition of an optimized sha hash function
92 static inline uint hexVal(const uchar* ch) {
94 return (*ch < 64 ? *ch - 48 : *ch - 87);
97 uint qHash(const ShaString& s) { // fast path, called 6-7 times per revision
99 const uchar* ch = reinterpret_cast<const uchar*>(s.latin1());
100 return (hexVal(ch ) << 24)
101 + (hexVal(ch + 2) << 20)
102 + (hexVal(ch + 4) << 16)
103 + (hexVal(ch + 6) << 12)
104 + (hexVal(ch + 8) << 8)
105 + (hexVal(ch + 10) << 4)
106 + hexVal(ch + 12);
109 /* Value returned by this function should be used only as function argument,
110 * and not stored in a variable because 'ba' value is overwritten at each
111 * call so the returned ShaString could became stale very quickly
113 const ShaString QGit::toTempSha(const QString& sha) {
115 static QByteArray ba;
116 ba = sha.toLatin1();
117 return ShaString(sha.isEmpty() ? NULL : ba.constData());
120 const ShaString QGit::toPersistentSha(const QString& sha, QVector<QByteArray>& v) {
122 v.append(sha.toLatin1());
123 return ShaString(v.last().constData());
126 // minimum git version required
127 const QString QGit::GIT_VERSION = "1.5.5";
129 // colors
130 const QColor QGit::BROWN = QColor(150, 75, 0);
131 const QColor QGit::ORANGE = QColor(255, 160, 50);
132 const QColor QGit::DARK_ORANGE = QColor(216, 144, 0);
133 const QColor QGit::LIGHT_ORANGE = QColor(255, 221, 170);
134 const QColor QGit::LIGHT_BLUE = QColor(85, 255, 255);
135 const QColor QGit::PURPLE = QColor(221, 221, 255);
136 const QColor QGit::DARK_GREEN = QColor(0, 205, 0);
138 // initialized at startup according to system wide settings
139 QColor QGit::ODD_LINE_COL;
140 QColor QGit::EVEN_LINE_COL;
141 QString QGit::GIT_DIR;
144 Default QFont c'tor calls static method QApplication::font() that could
145 be still NOT initialized at this time, so set a dummy font family instead,
146 it will be properly changed later, at startup
148 QFont QGit::STD_FONT("Helvetica");
149 QFont QGit::TYPE_WRITER_FONT("Helvetica");
151 // patches drag and drop
152 const QString QGit::PATCHES_DIR = "/.qgit_patches_copy";
153 const QString QGit::PATCHES_NAME = "qgit_import";
155 // git index parameters
156 const QString QGit::ZERO_SHA = "0000000000000000000000000000000000000000";
157 const QString QGit::CUSTOM_SHA = "*** CUSTOM * CUSTOM * CUSTOM * CUSTOM **";
158 const QString QGit::ALL_MERGE_FILES = "ALL_MERGE_FILES";
160 const QByteArray QGit::ZERO_SHA_BA(QGit::ZERO_SHA.toLatin1());
161 const ShaString QGit::ZERO_SHA_RAW(QGit::ZERO_SHA_BA.constData());
163 // settings keys
164 const QString QGit::ORG_KEY = "qgit";
165 const QString QGit::APP_KEY = "qgit4";
166 const QString QGit::GIT_DIR_KEY = "msysgit_exec_dir";
167 const QString QGit::DCLICK_ACT_KEY = "double_click_action";
168 const QString QGit::EXT_DIFF_KEY = "external_diff_viewer";
169 const QString QGit::EXT_EDITOR_KEY = "external_editor";
170 const QString QGit::REC_REP_KEY = "recent_open_repos";
171 const QString QGit::STD_FNT_KEY = "standard_font";
172 const QString QGit::TYPWRT_FNT_KEY = "typewriter_font";
173 const QString QGit::FLAGS_KEY = "flags";
174 const QString QGit::PATCH_DIR_KEY = "Patch/last_dir";
175 const QString QGit::FMT_P_OPT_KEY = "Patch/args";
176 const QString QGit::AM_P_OPT_KEY = "Patch/args_2";
177 const QString QGit::EX_KEY = "Working_dir/exclude_file_path";
178 const QString QGit::EX_PER_DIR_KEY = "Working_dir/exclude_per_directory_file_name";
179 const QString QGit::CON_GEOM_KEY = "Console/geometry";
180 const QString QGit::CMT_GEOM_KEY = "Commit/geometry";
181 const QString QGit::MAIN_GEOM_KEY = "Top_window/geometry";
182 const QString QGit::REV_GEOM_KEY = "Rev_List_view/geometry";
183 const QString QGit::REV_COLS_KEY = "Rev_List_view/columns";
184 const QString QGit::FILE_COLS_KEY = "File_List_view/columns";
185 const QString QGit::CMT_TEMPL_KEY = "Commit/template_file_path";
186 const QString QGit::CMT_ARGS_KEY = "Commit/args";
187 const QString QGit::RANGE_FROM_KEY = "RangeSelect/from";
188 const QString QGit::RANGE_TO_KEY = "RangeSelect/to";
189 const QString QGit::RANGE_OPT_KEY = "RangeSelect/options";
190 const QString QGit::ACT_GEOM_KEY = "Custom_actions/geometry";
191 const QString QGit::ACT_LIST_KEY = "Custom_actions/list";
192 const QString QGit::ACT_GROUP_KEY = "Custom_action_list/";
193 const QString QGit::ACT_TEXT_KEY = "/commands";
194 const QString QGit::ACT_FLAGS_KEY = "/flags";
196 // settings default values
197 const QString QGit::CMT_TEMPL_DEF = ".git/commit-template";
198 const QString QGit::EX_DEF = ".git/info/exclude";
199 const QString QGit::EX_PER_DIR_DEF = ".gitignore";
200 const QString QGit::EXT_DIFF_DEF = "kompare";
201 const QString QGit::EXT_EDITOR_DEF = "emacs";
203 // cache file
204 const QString QGit::BAK_EXT = ".bak";
205 const QString QGit::C_DAT_FILE = "/qgit_cache.dat";
207 // misc
208 const QString QGit::QUOTE_CHAR = "$";
211 using namespace QGit;
213 // settings helpers
214 uint QGit::flags(SCRef flagsVariable) {
216 QSettings settings;
217 return settings.value(flagsVariable, FLAGS_DEF).toUInt();
220 bool QGit::testFlag(uint f, SCRef flagsVariable) {
222 return (flags(flagsVariable) & f);
225 void QGit::setFlag(uint f, bool b, SCRef flagsVariable) {
227 QSettings settings;
228 uint flags = settings.value(flagsVariable, FLAGS_DEF).toUInt();
229 flags = b ? flags | f : flags & ~f;
230 settings.setValue(flagsVariable, flags);
233 // tree view icons helpers
234 static QHash<QString, const QPixmap*> mimePixMap;
236 void QGit::initMimePix() {
238 if (!mimePixMap.empty()) // only once
239 return;
241 QPixmap* pm = new QPixmap(QString::fromUtf8(":/icons/resources/folder.svg"));
242 mimePixMap.insert("#folder_closed", pm);
243 pm = new QPixmap(QString::fromUtf8(":/icons/resources/folder-open.svg"));
244 mimePixMap.insert("#folder_open", pm);
245 pm = new QPixmap(QString::fromUtf8(":/icons/resources/snap-page.svg"));
246 mimePixMap.insert("#default", pm);
247 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/text-x-cmake.svg"));
248 mimePixMap.insert("CMakeLists.txt", pm);
249 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/text-dockerfile.svg"));
250 mimePixMap.insert("Dockerfile", pm);
251 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/text-csv.svg"));
252 mimePixMap.insert("csv", pm);
253 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/text-x-csrc.svg"));
254 mimePixMap.insert("c", pm);
255 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/text-x-c++src.svg"));
256 mimePixMap.insert("cpp", pm);
257 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/text-x-chdr.svg"));
258 mimePixMap.insert("h", pm);
259 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/text-x-c++hdr.svg"));
260 mimePixMap.insert("hpp", pm);
261 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/text-x-generic.svg"));
262 mimePixMap.insert("txt", pm);
263 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/text-rtf.svg"));
264 mimePixMap.insert("rtf", pm);
265 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/text-x-script.svg"));
266 mimePixMap.insert("sh", pm);
267 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/application-x-perl.svg"));
268 mimePixMap.insert("perl", pm);
269 pm = new QPixmap(*pm);
270 mimePixMap.insert("pl", pm);
271 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/application-x-python-bytecode.svg"));
272 mimePixMap.insert("py", pm);
273 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/application-x-java.svg"));
274 mimePixMap.insert("java", pm);
275 pm = new QPixmap(*pm);
276 mimePixMap.insert("jar", pm);
277 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/application-x-tar.svg"));
278 mimePixMap.insert("tar", pm);
279 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/application-x-ace.svg"));
280 mimePixMap.insert("gz", pm);
281 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/application-x-compressed-tar.svg"));
282 mimePixMap.insert("tgz", pm);
283 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/application-zip.svg"));
284 mimePixMap.insert("zip", pm);
285 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/application-x-bzip.svg"));
286 mimePixMap.insert("bz", pm);
287 pm = new QPixmap(*pm);
288 mimePixMap.insert("bz2", pm);
289 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/text-html.svg"));
290 mimePixMap.insert("html", pm);
291 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/dialog-xml-editor.svg"));
292 mimePixMap.insert("xml", pm);
293 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/image-bmp.svg"));
294 mimePixMap.insert("bmp", pm);
295 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/image-gif.svg"));
296 mimePixMap.insert("gif", pm);
297 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/image-jpeg.svg"));
298 mimePixMap.insert("jpg", pm);
299 pm = new QPixmap(*pm);
300 mimePixMap.insert("jpeg", pm);
301 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/image-png.svg"));
302 mimePixMap.insert("png", pm);
303 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/image-svg+xml-compressed.svg"));
304 mimePixMap.insert("svg", pm);
305 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/image-tiff.svg"));
306 mimePixMap.insert("tiff", pm);
307 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/image-x-ico.svg"));
308 mimePixMap.insert("ico", pm);
309 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/image-x-xcf.svg"));
310 mimePixMap.insert("xcf", pm);
311 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/image-x-generic.svg"));
312 mimePixMap.insert("pbm", pm);
313 pm = new QPixmap(*pm);
314 mimePixMap.insert("pgm", pm);
315 pm = new QPixmap(*pm);
316 mimePixMap.insert("ppm", pm);
317 pm = new QPixmap(*pm);
318 mimePixMap.insert("xbm", pm);
319 pm = new QPixmap(*pm);
320 mimePixMap.insert("xpm", pm);
321 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/application-json.svg"));
322 mimePixMap.insert("json", pm);
323 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/application-pdf.svg"));
324 mimePixMap.insert("pdf", pm);
325 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/application-x-javascript.svg"));
326 mimePixMap.insert("js", pm);
327 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/text-x-go.svg"));
328 mimePixMap.insert("go", pm);
329 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/text-x-markdown.svg"));
330 mimePixMap.insert("md", pm);
331 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/text-x-patch.svg"));
332 mimePixMap.insert("patch", pm);
333 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/text-x-qml.svg"));
334 mimePixMap.insert("qml", pm);
335 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/text-x-tex.svg"));
336 mimePixMap.insert("tex", pm);
337 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/application-x-ruby.svg"));
338 mimePixMap.insert("rb", pm);
339 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/text-rust.svg"));
340 mimePixMap.insert("rs", pm);
341 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/text-css.svg"));
342 mimePixMap.insert("css", pm);
343 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/text-x-csharp.svg"));
344 mimePixMap.insert("cs", pm);
345 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/text-x-r.svg"));
346 mimePixMap.insert("r", pm);
347 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/application-x-designer.svg"));
348 mimePixMap.insert("ui", pm);
349 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/project-development.svg"));
350 mimePixMap.insert("pro", pm);
351 pm = new QPixmap(*pm);
352 mimePixMap.insert("sln", pm);
353 pm = new QPixmap(*pm);
354 mimePixMap.insert("vcproj", pm);
355 pm = new QPixmap(*pm);
356 mimePixMap.insert("vcxproj", pm);
357 pm = new QPixmap(QString::fromUtf8(":/icons/resources/file/application-x-sharedlib.svg"));
358 mimePixMap.insert("Makefile", pm);
361 void QGit::freeMimePix() {
363 qDeleteAll(mimePixMap);
366 const QPixmap* QGit::mimePix(SCRef fileName) {
368 // Try to match full filename (e.g. CMakeLists.txt)
369 if (mimePixMap.contains(fileName))
370 return mimePixMap.value(fileName);
372 // Try to match extension
373 SCRef ext = fileName.section('.', -1, -1).toLower();
374 if (mimePixMap.contains(ext))
375 return mimePixMap.value(ext);
377 return mimePixMap.value("#default");
380 // geometry settings helers
381 void QGit::saveGeometrySetting(SCRef name, QWidget* w, splitVect* svPtr) {
383 QSettings settings;
384 if (w && w->isVisible())
385 settings.setValue(name + "_window", w->saveGeometry());
387 if (!svPtr)
388 return;
390 int cnt = 0;
391 FOREACH (splitVect, it, *svPtr) {
393 cnt++;
394 if ((*it)->sizes().contains(0))
395 continue;
397 QString nm(name + "_splitter_" + QString::number(cnt));
398 settings.setValue(nm, (*it)->saveState());
402 void QGit::restoreGeometrySetting(SCRef name, QWidget* w, splitVect* svPtr) {
404 QSettings settings;
405 QString nm;
406 if (w) {
407 nm = name + "_window";
408 QVariant v = settings.value(nm);
409 if (v.isValid())
410 w->restoreGeometry(v.toByteArray());
412 if (!svPtr)
413 return;
415 int cnt = 0;
416 FOREACH (splitVect, it, *svPtr) {
418 cnt++;
419 nm = name + "_splitter_" + QString::number(cnt);
420 QVariant v = settings.value(nm);
421 if (!v.isValid())
422 continue;
424 (*it)->restoreState(v.toByteArray());
428 // misc helpers
429 bool QGit::stripPartialParaghraps(const QByteArray& ba, QString* dst, QString* prev) {
431 QTextCodec* tc = QTextCodec::codecForLocale();
433 if (ba.endsWith('\n')) { // optimize common case
434 *dst = tc->toUnicode(ba);
436 // handle rare case of a '\0' inside content
437 while (dst->size() < ba.size() && ba.at(dst->size()) == '\0') {
438 QString s = tc->toUnicode(ba.mid(dst->size() + 1)); // sizes should match
439 dst->append(" ").append(s);
442 dst->truncate(dst->size() - 1); // strip trailing '\n'
443 if (!prev->isEmpty()) {
444 dst->prepend(*prev);
445 prev->clear();
447 return true;
449 QString src = tc->toUnicode(ba);
450 // handle rare case of a '\0' inside content
451 while (src.size() < ba.size() && ba.at(src.size()) == '\0') {
452 QString s = tc->toUnicode(ba.mid(src.size() + 1));
453 src.append(" ").append(s);
456 int idx = src.lastIndexOf('\n');
457 if (idx == -1) {
458 prev->append(src);
459 dst->clear();
460 return false;
462 *dst = src.left(idx).prepend(*prev); // strip trailing '\n'
463 *prev = src.mid(idx + 1); // src[idx] is '\n', skip it
464 return true;
467 bool QGit::writeToFile(SCRef fileName, SCRef data, bool setExecutable) {
469 QFile file(fileName);
470 if (!file.open(QIODevice::WriteOnly)) {
471 dbp("ERROR: unable to write file %1", fileName);
472 return false;
474 QString data2(data);
475 QTextStream stream(&file);
477 #ifdef Q_OS_WIN32
478 data2.replace("\r\n", "\n"); // change windows CRLF to linux
479 data2.replace("\n", "\r\n"); // then change all linux CRLF to windows
480 #endif
481 stream << data2;
482 file.close();
484 #ifndef Q_OS_WIN32
485 if (setExecutable)
486 chmod(fileName.toLatin1().constData(), 0755);
487 #endif
488 return true;
491 bool QGit::writeToFile(SCRef fileName, const QByteArray& data, bool setExecutable) {
493 QFile file(fileName);
494 if (!file.open(QIODevice::WriteOnly)) {
495 dbp("ERROR: unable to write file %1", fileName);
496 return false;
498 QDataStream stream(&file);
499 stream.writeRawData(data.constData(), data.size());
500 file.close();
502 #ifndef Q_OS_WIN32
503 if (setExecutable)
504 chmod(fileName.toLatin1().constData(), 0755);
505 #endif
506 return true;
509 bool QGit::readFromFile(SCRef fileName, QString& data) {
511 data = "";
512 QFile file(fileName);
513 if (!file.open(QIODevice::ReadOnly)) {
514 dbp("ERROR: unable to read file %1", fileName);
515 return false;
517 QTextStream stream(&file);
518 data = stream.readAll();
519 file.close();
520 return true;
523 bool QGit::startProcess(QProcess* proc, SCList args, SCRef buf, bool* winShell) {
525 if (!proc || args.isEmpty())
526 return false;
528 QStringList arguments(args);
529 adjustPath(arguments, winShell);
531 QString prog(arguments.first());
532 arguments.removeFirst();
533 if (!buf.isEmpty()) {
535 On Windows buffer size of QProcess's standard input
536 pipe is quite limited and a crash can occur in case
537 a big chunk of data is written to process stdin.
538 As a workaround we use a temporary file to store data.
539 Process stdin will be redirected to this file
541 QTemporaryFile* bufFile = new QTemporaryFile(proc);
542 bufFile->open();
543 QTextStream stream(bufFile);
544 stream << buf;
545 proc->setStandardInputFile(bufFile->fileName());
546 bufFile->close();
548 QStringList env = QProcess::systemEnvironment();
549 env << "GIT_TRACE=0"; // avoid choking on debug traces
550 env << "GIT_FLUSH=0"; // skip the fflush() in 'git log'
551 proc->setEnvironment(env);
553 proc->start(prog, arguments); // TODO test QIODevice::Unbuffered
554 return proc->waitForStarted();