# # # patch "NEWS" # from [650c6f31c4d3d4348cec5b8d5e15bf56a7164d31] # to [6892374b7a0ec727e85b24288786b9ec45d8ac30] # # patch "res/forms/commit_revision.ui" # from [470e5fbd3fe58caff240d35dac1ce475dd64dc61] # to [5c31cbc645279d4894459c84b71ac1a2fa5a6589] # # patch "res/forms/file_diff.ui" # from [80ef8d2f2e9421f0fb8a026b00159e6e6e15df61] # to [6608573b8c72a21523ea07b110636abda6a8c815] # # patch "res/forms/preferences.ui" # from [0d11100970f4e76902c87bc53331a28b5f0cd993] # to [893f76e7dddd80eb90ab81b393a7d879019806aa] # # patch "res/forms/revision_diff.ui" # from [b4c9054581ea1f9e5cd89d2556fb30c858f3bd37] # to [462da831393f381934149847d2bb24b126b38fe3] # # patch "src/model/Ancestors.cpp" # from [40b168cdabbc4fbab891933ff9bd174bc434e4c6] # to [ff279efc1a19d894af6db12104e7462b12cce760] # # patch "src/model/AutomateCommand.cpp" # from [008019a4c691a5f977d2a6cb8b8cf347b652a63b] # to [1c819d32c321a539d0d59e100a632d17fdba16ca] # # patch "src/model/Branches.cpp" # from [d0a1ee6db5af0f1a7cdf864d070868a6d87d4532] # to [6a240a83de4e8cf7aaf21467ba9b4205e586e487] # # patch "src/model/Certs.cpp" # from [8b011be58c9224fb726884374bd3d4941a8c7e36] # to [285dd2004b058cc9a7082ca34d0cd21fdc8d9765] # # patch "src/model/ContentDiff.cpp" # from [74852b2ba64c28376d51ada0f718228ba4612d5f] # to [fba40b0f3894f67596c8ea99293ab78212535b53] # # patch "src/model/ContentDiff.h" # from [e89ba83d45953a4cfc745a4c7ad475e9733c37ff] # to [38035d0dec91320d0fa33e58ffc5238a11109852] # # patch "src/model/GetAttributes.cpp" # from [ed421d842b53dc2400e2c1adaae282c53e34c250] # to [bd51d4f673480a28c18b78bf4290a4a5e0897589] # # patch "src/model/GetContentChanged.cpp" # from [d0d630e76c1b8c7fee93d511fbfd1fd2eecb4bc7] # to [784b1572425d9af21934de417f6cbc65db3419f3] # # patch "src/model/GetDatabaseVariables.cpp" # from [daa9eb7f0b0a9bb2f684182b2c4a499814c1c028] # to [b9ad21e33c9994caf29bdadcb74103c4bb3973de] # # patch "src/model/GetFile.cpp" # from [3fffb3be172e0e28451322370f80dbf80408f132] # to [735189cbe8ffb4e7cffd1a40e07fef37a0068ea4] # # patch "src/model/GetFile.h" # from [f185acd189190ef2d6d519a8f5adbfd727a3093d] # to [73a82bb540ba6ca45cd9c6fcdc4f165a6ded958b] # # patch "src/model/GetRevision.cpp" # from [95014c1468ada6b5e60ed31407eec63f1a50975b] # to [a59701ac07d146aab3ebb5f021c2ee92bb6bb085] # # patch "src/model/Inventory.cpp" # from [4affff131bf9b3bbf87b6d2c38112754af3dfba9] # to [7056e6451d09ab9dbb5793d2b9529a5fddff9420] # # patch "src/model/Keys.cpp" # from [19dfda529004934172d6ceb4511eae24e30af3f6] # to [11aae606f14aaa15d64c9f68f651bf3630733dd6] # # patch "src/model/Manifest.cpp" # from [235f3ddeb0e0514934b9df6f619e87ceb9d6f387] # to [362e3fe7a8399c28ab69235376d5955421b2cd66] # # patch "src/model/Select.cpp" # from [ca967f456759195df4c53ec6922c0a8967273c35] # to [a82a300f0787e9ae330c0c8ca96083da99d82476] # # patch "src/model/Tags.cpp" # from [80d62375cd40c37849eacf93a0bcb61d3983175b] # to [31c3f32539c54fb7bc7d676d18ad0581356f844c] # # patch "src/model/Toposort.cpp" # from [d081059ec2bc33341fc9b6fc75292072cb29a3fe] # to [c5f0896d43e6e9763571e201281ee49db94ad22e] # # patch "src/monotone/FileExporter.cpp" # from [d8841a939d1676436370923e9b88c9dbe0b64a9c] # to [8d23af2f0072da14f8c79d2d71b3db6701693729] # # patch "src/monotone/MonotoneThread.cpp" # from [e3f258605fd1557753b903f13d1515b6e98d976b] # to [b5fd6caedca9f2cbc910e172ffbee6c0e3d2d3f0] # # patch "src/monotone/MonotoneThread.h" # from [b00320bbdc44039934898127d9093016adec9f16] # to [dd3abaa03d5a86ab9141e3da15c0e62766e016aa] # # patch "src/monotone/MonotoneUtil.cpp" # from [2679a231bcdd46393acc15364976563938c05325] # to [c18789f0f92cceb714cca6b981e50ab2437ea9ff] # # patch "src/monotone/MonotoneUtil.h" # from [7173990600fd78e4e75a5bc0111d2c29e2a06ed7] # to [2859545454f0987d0bb5141a06a1f34667a9dc51] # # patch "src/monotone/WorkspaceCommitter.cpp" # from [d2a5ce169d18495c84ed56ecf92732b2c6646812] # to [3efc567b8ef22e48f152f8b728a3b168ea0f3d0d] # # patch "src/view/TreeView.cpp" # from [dcf826574e06d65e22db5a586caeb918673c00d0] # to [bf98bd50c56ec9d93411832cd4e3c5d5f08184b8] # # patch "src/view/TreeView.h" # from [7b872f64c09770fed7e6cf2dd321b6a798a05f90] # to [651169fd52a772a8c03d883a3d75c35f52cb9524] # # patch "src/view/WorkspaceWindow.cpp" # from [9ae0a5ea1f3095ecc8824221b1d1523956942144] # to [ade47c546f628f21ee7457e923e57d4a910370c4] # # patch "src/view/WorkspaceWindow.h" # from [3c181a5026143baf9e58dc86a97cf4ee5469356b] # to [a463caf3a560c81ada20f1cd3eae8677644018b8] # # patch "src/view/dialogs/FileDiff.cpp" # from [699fafa30f746354fb9218fc18bc135e01e21e02] # to [f9e9afd79cc644e82dc4987ca845ae2e37dcc473] # # patch "src/view/dialogs/FileDiff.h" # from [14c54d84f6d8f41b74cb461a378d942ec0db690f] # to [752ff0409384d3e4220754274ea638604e4a278d] # # patch "src/view/dialogs/GenerateKeypair.cpp" # from [6ed24f6d1bcee46cd89605ecbe0c3c5a6b17475c] # to [17c560ed391524a64ae4f86642641999a1eb99d7] # # patch "src/view/dialogs/Preferences.cpp" # from [1e3bc13f16b3a04e84f5fc8ce2add567a35db359] # to [211cc5978c8f41c19d41165fe4e56951043989cf] # # patch "src/view/dialogs/RevisionDiff.cpp" # from [c03a05117e11c00a098e0fd55b09385dc7cb205c] # to [24e17c91450da9413c36beea817d744f313c2341] # # patch "src/view/dialogs/RevisionDiff.h" # from [e536991efd9001e62a385f86c7130ea26b2cbee7] # to [bce21301eb5596f48b36be6f32947ec6b5cfea71] # # patch "src/view/dialogs/RevisionManifest.cpp" # from [6316625d53d8349258ae940ddc1f5c8d5dc641ec] # to [f329b383c8b9aced2fe93ecda59f704f53f17427] # ============================================================ --- NEWS 650c6f31c4d3d4348cec5b8d5e15bf56a7164d31 +++ NEWS 6892374b7a0ec727e85b24288786b9ec45d8ac30 @@ -1,3 +1,14 @@ +????-??-?? (0.9) + - new: possibility to select the file encoding of a file in the diff dialog; + the preference can be saved to the special file attribute + "guitone:file-encoding" which is read out next time you diff the file. + Note that you need a checked-out workspace for this; if you diff a + not-checked-out file, the attribute must already be present in the manifest + of the base revision and an encoding change cannot be saved there. + - bugfix: if you change the selection of items in the workspace view the + attribute view should now get properly updated properly under all + circumstances + 2008-05-25 (0.8) - starting from this version (0.8) guitone is distributed under the terms of the GNU General Public License Version 3 ============================================================ --- res/forms/commit_revision.ui 470e5fbd3fe58caff240d35dac1ce475dd64dc61 +++ res/forms/commit_revision.ui 5c31cbc645279d4894459c84b71ac1a2fa5a6589 @@ -6,7 +6,7 @@ 0 0 417 - 509 + 513 @@ -54,11 +54,46 @@ - - - Double-click on a patch to open the diff dialog. - - + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 11 + + + + Double-click on a patch to open the diff dialog. + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + ============================================================ --- res/forms/file_diff.ui 80ef8d2f2e9421f0fb8a026b00159e6e6e15df61 +++ res/forms/file_diff.ui 6608573b8c72a21523ea07b110636abda6a8c815 @@ -13,119 +13,118 @@ File differences of "%1" - :/icons/guitone.png + + :/icons/guitone.png:/icons/guitone.png - + - + - - - - - false - - - false - - - - - - - + + + false + + + false + + - - - Show + + + + + + + + Display Options + + + + + + + + + Qt::Horizontal + + + QSizePolicy::MinimumExpanding + + + + 20 + 20 + + + + + + + + only left (%1) + + + + + + + only right (%2) + + + + + + + both + + + true + + + + + + + + + + + + Qt::Horizontal - - - - - - - first (%1) - - - - - - - second (%2) - - - - - - - both - - - true - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - + + + 40 + 20 + + + + + + + + Prev + - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Prev - - - - - - - Next - - - - - - - Close - - - true - - - - + + + Next + + + + + + Close + + + true + + + @@ -134,7 +133,7 @@ TreeView QTreeView -
TreeView.h
+
TreeView.h
DiffStatusView ============================================================ --- res/forms/preferences.ui 0d11100970f4e76902c87bc53331a28b5f0cd993 +++ res/forms/preferences.ui 893f76e7dddd80eb90ab81b393a7d879019806aa @@ -38,7 +38,7 @@ QTabWidget::Rounded - 0 + 2 false @@ -317,7 +317,7 @@ - + ask before opening executable files and files with @@ -328,7 +328,7 @@ the following extensions (comma-separate - + @@ -351,7 +351,7 @@ the following extensions (comma-separate - + Qt::Vertical @@ -364,6 +364,16 @@ the following extensions (comma-separate + + + + save encoding preference as file attribute + + + true + + +
============================================================ --- res/forms/revision_diff.ui b4c9054581ea1f9e5cd89d2556fb30c858f3bd37 +++ res/forms/revision_diff.ui 462da831393f381934149847d2bb24b126b38fe3 @@ -5,58 +5,92 @@ 0 0 - 528 - 431 + 539 + 500 - :/icons/guitone.png + + :/icons/guitone.png:/icons/guitone.png - + - + + + false + + + + + - + + + Qt::Horizontal + + + + 40 + 20 + + + - - - - - false - - - Show detailed differences - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Close - - - true - - - - + + + + 11 + true + + + + Double-click on a file node to display the differences in context. + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Close + + + true + + + + + ============================================================ --- src/model/Ancestors.cpp 40b168cdabbc4fbab891933ff9bd174bc434e4c6 +++ src/model/Ancestors.cpp ff279efc1a19d894af6db12104e7462b12cce760 @@ -50,13 +50,13 @@ void Ancestors::processTaskResult(const { if (task.getReturnCode() == 2) { - emit invalidAncestor(task.getOutputUtf8()); + emit invalidAncestor(task.getDecodedOutput()); } if (task.getReturnCode() != 0) { C(QString("Command returned with a non-zero return code (%1)") - .arg(task.getOutputUtf8())); + .arg(task.getDecodedOutput())); return; } @@ -65,7 +65,7 @@ void Ancestors::processTaskResult(const selRevisions->clear(); delete selRevisions; selRevisions = new RevisionList( - task.getOutputUtf8().split('\n', QString::SkipEmptyParts) + task.getDecodedOutput().split('\n', QString::SkipEmptyParts) ); // reset the view reset(); ============================================================ --- src/model/AutomateCommand.cpp 008019a4c691a5f977d2a6cb8b8cf347b652a63b +++ src/model/AutomateCommand.cpp 1c819d32c321a539d0d59e100a632d17fdba16ca @@ -112,7 +112,7 @@ void AutomateCommandHelper::taskAborted( .arg(task.getCommandNumber()) .arg(QString::fromUtf8(task.getEncodedInput())) .arg(task.getReturnCode()) - .arg(task.getOutputUtf8()) + .arg(task.getDecodedOutput()) ); } ============================================================ --- src/model/Branches.cpp d0a1ee6db5af0f1a7cdf864d070868a6d87d4532 +++ src/model/Branches.cpp 6a240a83de4e8cf7aaf21467ba9b4205e586e487 @@ -45,7 +45,7 @@ void Branches::processTaskResult(const M if (task.getReturnCode() != 0) { C(QString("Command returned with a non-zero return code (%1)") - .arg(task.getOutputUtf8())); + .arg(task.getDecodedOutput())); return; } @@ -73,11 +73,11 @@ void Branches::processTaskResult(const M branches.appendRow(root); builder = new TreeBuilder(root, this); - builder->addList(task.getOutputUtf8()); + builder->addList(task.getDecodedOutput()); } else { - QStringList branchList = task.getOutputUtf8() + QStringList branchList = task.getDecodedOutput() .split("\n", QString::SkipEmptyParts); foreach (QString branch, branchList) { ============================================================ --- src/model/Certs.cpp 8b011be58c9224fb726884374bd3d4941a8c7e36 +++ src/model/Certs.cpp 285dd2004b058cc9a7082ca34d0cd21fdc8d9765 @@ -44,11 +44,11 @@ void Certs::processTaskResult(const Mono if (task.getReturnCode() != 0) { C(QString("Command returned with a non-zero return code (%1)") - .arg(task.getOutputUtf8())); + .arg(task.getDecodedOutput())); return; } - BasicIOParser parser(task.getOutputUtf8()); + BasicIOParser parser(task.getDecodedOutput()); I(parser.parse()); StanzaList list = parser.getStanzas(); ============================================================ --- src/model/ContentDiff.cpp 74852b2ba64c28376d51ada0f718228ba4612d5f +++ src/model/ContentDiff.cpp fba40b0f3894f67596c8ea99293ab78212535b53 @@ -24,10 +24,8 @@ ContentDiff::ContentDiff(QObject * paren #include ContentDiff::ContentDiff(QObject * parent) - : QAbstractItemModel(parent), AutomateCommand(0) -{ - diffParser = 0; -} + : QAbstractItemModel(parent), AutomateCommand(0), diffParser(0) +{} ContentDiff::~ContentDiff() { @@ -37,16 +35,16 @@ void ContentDiff::cleanup() void ContentDiff::cleanup() { if (diffParser) delete diffParser; + diffParser = 0; qDeleteAll(lines); lines.clear(); reset(); } void ContentDiff::readDatabaseDiff(const DatabaseFile & db, const QString & fileName, - const QString & base, const QString & target) + const QString & base, const QString & target, + const QString & encoding) { - cleanup(); - QStringList cmd; cmd << "content_diff"; @@ -59,14 +57,19 @@ void ContentDiff::readDatabaseDiff(const opts << "r" << base << "r" << target; MonotoneTask task(cmd, opts); + + if (!encoding.isEmpty()) + { + task.setOutputEncoding(encoding); + } + AutomateCommand::enqueueDatabaseTask(db, task); } void ContentDiff::readWorkspaceDiff(const WorkspacePath & ws, const QString & fileName, - const QString & base, const QString & target) + const QString & base, const QString & target, + const QString & encoding) { - cleanup(); - QStringList cmd; cmd << "content_diff"; @@ -87,20 +90,52 @@ void ContentDiff::readWorkspaceDiff(cons } MonotoneTask task(cmd, opts); + + if (!encoding.isEmpty()) + { + task.setOutputEncoding(encoding); + } + AutomateCommand::enqueueWorkspaceTask(ws, task); } +void ContentDiff::switchOutputEncoding(const QString & codec) +{ + if (!lastTask.isFinished()) + { + D("task not yet finished, skip encoding switch"); + return; + } + + cleanup(); + + loadOutputIntoModel(lastTask.getDecodedOutput(codec)); + + emit encodingChanged(codec); +} + void ContentDiff::processTaskResult(const MonotoneTask & task) { if (task.getReturnCode() != 0) { C(QString("Command returned with a non-zero return code (%1)") - .arg(task.getOutputUtf8())); + .arg(task.getDecodedOutput())); return; } - diffParser = new DiffParser(task.getOutputUtf8()); + cleanup(); + lastTask = task; + loadOutputIntoModel(task.getDecodedOutput()); + + emit diffRead(); +} + +void ContentDiff::loadOutputIntoModel(const QString & output) +{ + I(!diffParser); + diffParser = new DiffParser(output); + // flatten the data for the current view FileDiffs fileDiffs = diffParser->getAllDiffs(); @@ -174,8 +209,6 @@ void ContentDiff::processTaskResult(cons } reset(); - - emit diffRead(); } int ContentDiff::columnCount(const QModelIndex & parent) const ============================================================ --- src/model/ContentDiff.h e89ba83d45953a4cfc745a4c7ad475e9733c37ff +++ src/model/ContentDiff.h 38035d0dec91320d0fa33e58ffc5238a11109852 @@ -104,18 +104,22 @@ public slots: inline FileDiffs getAllDiffs() { return diffParser->getAllDiffs(); } public slots: - void readDatabaseDiff(const DatabaseFile &, const QString &, const QString &, const QString &); - void readWorkspaceDiff(const WorkspacePath &, const QString &, const QString &, const QString &); + void readDatabaseDiff(const DatabaseFile &, const QString &, const QString &, const QString &, const QString & encoding = QString()); + void readWorkspaceDiff(const WorkspacePath &, const QString &, const QString &, const QString &, const QString & encoding = QString()); + void switchOutputEncoding(const QString &); signals: void diffRead(); + void encodingChanged(const QString &); private: void processTaskResult(const MonotoneTask &); + void loadOutputIntoModel(const QString &); void cleanup(); DiffParser * diffParser; ListLines lines; + MonotoneTask lastTask; }; #endif ============================================================ --- src/model/GetAttributes.cpp ed421d842b53dc2400e2c1adaae282c53e34c250 +++ src/model/GetAttributes.cpp bd51d4f673480a28c18b78bf4290a4a5e0897589 @@ -67,11 +67,11 @@ void GetAttributes::processTaskResult(co if (task.getReturnCode() != 0) { C(QString("Command returned with a non-zero return code (%1)") - .arg(task.getOutputUtf8())); + .arg(task.getDecodedOutput())); return; } - BasicIOParser parser(task.getOutputUtf8()); + BasicIOParser parser(task.getDecodedOutput()); I(parser.parse()); StanzaList list = parser.getStanzas(); ============================================================ --- src/model/GetContentChanged.cpp d0d630e76c1b8c7fee93d511fbfd1fd2eecb4bc7 +++ src/model/GetContentChanged.cpp 784b1572425d9af21934de417f6cbc65db3419f3 @@ -105,12 +105,12 @@ void GetContentChanged::processTaskResul if (task.getReturnCode() != 0) { C(QString("Command returned with a non-zero return code (%1)") - .arg(task.getOutputUtf8())); + .arg(task.getDecodedOutput())); return; } QString current = task.getArguments().at(0); - QString output = task.getOutputUtf8(); + QString output = task.getDecodedOutput(); if (current == "parents") { ============================================================ --- src/model/GetDatabaseVariables.cpp daa9eb7f0b0a9bb2f684182b2c4a499814c1c028 +++ src/model/GetDatabaseVariables.cpp b9ad21e33c9994caf29bdadcb74103c4bb3973de @@ -40,7 +40,7 @@ void GetDatabaseVariables::processTaskRe void GetDatabaseVariables::processTaskResult(const MonotoneTask & task) { - QString output = task.getOutputUtf8(); + QString output = task.getOutput(); if (task.getReturnCode() != 0) { if (output.indexOf("No variables found") != -1) @@ -51,7 +51,7 @@ void GetDatabaseVariables::processTaskRe } C(QString("Command returned with a non-zero return code (%1)") - .arg(task.getOutputUtf8())); + .arg(task.getDecodedOutput())); return; } ============================================================ --- src/model/GetFile.cpp 3fffb3be172e0e28451322370f80dbf80408f132 +++ src/model/GetFile.cpp 735189cbe8ffb4e7cffd1a40e07fef37a0068ea4 @@ -23,13 +23,14 @@ #include #include -GetFile::GetFile(QObject * parent, const DatabaseFile & db) - : QAbstractItemModel(parent), AutomateCommand(0), databaseFile(db) +GetFile::GetFile(QObject * parent) + : QAbstractItemModel(parent), AutomateCommand(0) {} GetFile::~GetFile() {} -void GetFile::readFileByName(const QString & fileName, const QString & rev) +void GetFile::readFileByName(const DatabaseFile & db, const QString & fileName, + const QString & rev, const QString & encoding) { QStringList cmd; cmd << "get_file_of" << fileName; @@ -37,23 +38,30 @@ void GetFile::readFileByName(const QStri QStringList opts; opts << "r" << rev; - readFile(cmd, opts); + readFile(db, cmd, opts, encoding); } -void GetFile::readFileById(const QString & fileID) +void GetFile::readFileById(const DatabaseFile & db, const QString & fileID, + const QString & encoding) { QStringList cmd; cmd << "get_file" << fileID; - readFile(cmd, QStringList()); + readFile(db, cmd, QStringList(), encoding); } -void GetFile::readFile(QStringList cmd, QStringList opts) +void GetFile::readFile(const DatabaseFile & db, const QStringList & cmd, + const QStringList & opts, const QString & encoding) { - // reset attached views reset(); MonotoneTask task(cmd, opts); - AutomateCommand::enqueueDatabaseTask(databaseFile, task); + + if (!encoding.isEmpty()) + { + task.setOutputEncoding(encoding); + } + + AutomateCommand::enqueueDatabaseTask(db, task); } void GetFile::processTaskResult(const MonotoneTask & task) @@ -61,15 +69,31 @@ void GetFile::processTaskResult(const Mo if (task.getReturnCode() != 0) { C(QString("Command returned with a non-zero return code (%1)") - .arg(task.getOutputUtf8())); + .arg(task.getDecodedOutput())); return; } + lastTask = task; - // FIXME: if the file contents is misinterpreted as utf-8, we have a - // problem here. The only thing we could do here is to let the user - // select the charset of the file and re-read it afterwards - QString output = task.getOutputUtf8(); + loadOutputIntoModel(task.getDecodedOutput()); + emit fileRead(); +} + +void GetFile::switchOutputEncoding(const QString & codec) +{ + if (!lastTask.isFinished()) + { + D("task not yet finished, skip encoding switch"); + return; + } + + loadOutputIntoModel(lastTask.getDecodedOutput(codec)); + + emit encodingChanged(codec); +} + +void GetFile::loadOutputIntoModel(const QString & output) +{ QRegExp rx("\\0x00"); if (rx.indexIn(output) != -1) { @@ -78,6 +102,9 @@ void GetFile::processTaskResult(const Mo return; } + Content oldContents = fileContents; + fileContents.clear(); + QStringList lines(output.split(QRegExp("\\n|\\r\\n"))); for (int i=0, s=lines.size(); ihunks.size(); ihunks.at(i); @@ -277,7 +302,7 @@ QVariant GetFile::data(const QModelIndex case 1: return QVariant(line->rightLineCount == 0 ? "-" : QString::number(line->rightLineCount)); case 2: return QVariant(line->content); - default: I(false); + default: return QVariant(); } } else ============================================================ --- src/model/GetFile.h f185acd189190ef2d6d519a8f5adbfd727a3093d +++ src/model/GetFile.h 73a82bb540ba6ca45cd9c6fcdc4f165a6ded958b @@ -43,7 +43,7 @@ public: { Q_OBJECT public: - GetFile(QObject *, const DatabaseFile &); + GetFile(QObject *); virtual ~GetFile(); // needed Qt Model methods @@ -58,19 +58,22 @@ public slots: QModelIndex getPrevGroup(const QModelIndex &, bool recurse = false); public slots: - void readFileById(const QString &); - void readFileByName(const QString &, const QString &); + void readFileById(const DatabaseFile &, const QString &, const QString & encoding = QString()); + void readFileByName(const DatabaseFile &, const QString &, const QString &, const QString & encoding = QString()); + void switchOutputEncoding(const QString &); void applyDiff(Diff * diff); signals: void fileRead(); + void encodingChanged(const QString &); private: - void readFile(QStringList, QStringList); + void readFile(const DatabaseFile &, const QStringList &, const QStringList &, const QString & encoding = QString()); void processTaskResult(const MonotoneTask &); + void loadOutputIntoModel(const QString &); Content fileContents; - DatabaseFile databaseFile; + MonotoneTask lastTask; }; #endif ============================================================ --- src/model/GetRevision.cpp 95014c1468ada6b5e60ed31407eec63f1a50975b +++ src/model/GetRevision.cpp a59701ac07d146aab3ebb5f021c2ee92bb6bb085 @@ -57,11 +57,11 @@ void GetRevision::processTaskResult(cons { if (task.getReturnCode() != 0) { - emit readFailed(MonotoneUtil::stripMtnPrefix(task.getOutputUtf8())); + emit readFailed(MonotoneUtil::stripMtnPrefix(task.getDecodedOutput())); return; } - BasicIOParser parser(task.getOutputUtf8()); + BasicIOParser parser(task.getDecodedOutput()); I(parser.parse()); StanzaList list = parser.getStanzas(); ============================================================ --- src/model/Inventory.cpp 4affff131bf9b3bbf87b6d2c38112754af3dfba9 +++ src/model/Inventory.cpp 7056e6451d09ab9dbb5793d2b9529a5fddff9420 @@ -95,7 +95,7 @@ void Inventory::processTaskResult(const } } - QString output = task.getOutputUtf8(); + QString output = task.getDecodedOutput(); // internal errors if (task.getReturnCode() == 1) ============================================================ --- src/model/Keys.cpp 19dfda529004934172d6ceb4511eae24e30af3f6 +++ src/model/Keys.cpp 11aae606f14aaa15d64c9f68f651bf3630733dd6 @@ -39,14 +39,14 @@ void Keys::processTaskResult(const Monot if (task.getReturnCode() != 0) { C(QString("Command returned with a non-zero return code (%1)") - .arg(task.getOutputUtf8())); + .arg(task.getDecodedOutput())); return; } reset(); keys.clear(); - BasicIOParser parser(task.getOutputUtf8()); + BasicIOParser parser(task.getDecodedOutput()); I(parser.parse()); StanzaList list = parser.getStanzas(); ============================================================ --- src/model/Manifest.cpp 235f3ddeb0e0514934b9df6f619e87ceb9d6f387 +++ src/model/Manifest.cpp 362e3fe7a8399c28ab69235376d5955421b2cd66 @@ -45,11 +45,11 @@ void Manifest::processTaskResult(const M if (task.getReturnCode() != 0) { C(QString("Command returned with a non-zero return code (%1)") - .arg(task.getOutputUtf8())); + .arg(task.getDecodedOutput())); return; } - BasicIOParser parser(task.getOutputUtf8()); + BasicIOParser parser(task.getDecodedOutput()); I(parser.parse()); StanzaList list = parser.getStanzas(); ============================================================ --- src/model/Select.cpp ca967f456759195df4c53ec6922c0a8967273c35 +++ src/model/Select.cpp a82a300f0787e9ae330c0c8ca96083da99d82476 @@ -48,20 +48,20 @@ void Select::processTaskResult(const Mon // is always fired if a invalid selector syntax has been given if (task.getReturnCode() == 2) { - emit invalidSelection(task.getOutputUtf8()); + emit invalidSelection(task.getDecodedOutput()); return; } if (task.getReturnCode() != 0) { C(QString("Command returned with a non-zero return code (%1)") - .arg(task.getOutputUtf8())); + .arg(task.getDecodedOutput())); return; } delete selRevisions; selRevisions = new RevisionList( - task.getOutputUtf8().split('\n', QString::SkipEmptyParts) + task.getDecodedOutput().split('\n', QString::SkipEmptyParts) ); // reset the view reset(); ============================================================ --- src/model/Tags.cpp 80d62375cd40c37849eacf93a0bcb61d3983175b +++ src/model/Tags.cpp 31c3f32539c54fb7bc7d676d18ad0581356f844c @@ -49,13 +49,13 @@ void Tags::processTaskResult(const Monot if (task.getReturnCode() != 0) { C(QString("Command returned with a non-zero return code (%1)") - .arg(task.getOutputUtf8())); + .arg(task.getDecodedOutput())); return; } tags->clear(); - BasicIOParser parser(task.getOutputUtf8()); + BasicIOParser parser(task.getDecodedOutput()); I(parser.parse()); StanzaList list = parser.getStanzas(); ============================================================ --- src/model/Toposort.cpp d081059ec2bc33341fc9b6fc75292072cb29a3fe +++ src/model/Toposort.cpp c5f0896d43e6e9763571e201281ee49db94ad22e @@ -120,11 +120,11 @@ void Toposort::processTaskResult(const M if (task.getReturnCode() != 0) { C(QString("Command returned with a non-zero return code (%1)") - .arg(task.getOutputUtf8())); + .arg(task.getDecodedOutput())); return; } - QStringList sortedRevs(task.getOutputUtf8().split("\n")); + QStringList sortedRevs(task.getDecodedOutput().split("\n")); for (int i=0, j=sortedRevs.size(); iinsert(sortedRevs.at(i), i); ============================================================ --- src/monotone/FileExporter.cpp d8841a939d1676436370923e9b88c9dbe0b64a9c +++ src/monotone/FileExporter.cpp 8d23af2f0072da14f8c79d2d71b3db6701693729 @@ -93,7 +93,7 @@ bool FileExporter::exportFile(const File if (out.getReturnCode() > 0) { C(QString("get_file_of '%1' (rev: %2) failed: %3") - .arg(entry.path).arg(revision).arg(out.getOutputUtf8())); + .arg(entry.path).arg(revision).arg(out.getDecodedOutput())); return false; } ============================================================ --- src/monotone/MonotoneThread.cpp e3f258605fd1557753b903f13d1515b6e98d976b +++ src/monotone/MonotoneThread.cpp b5fd6caedca9f2cbc910e172ffbee6c0e3d2d3f0 @@ -21,6 +21,7 @@ #include "BasicIOParser.h" #include +#include MonotoneTask::MonotoneTask() { @@ -37,6 +38,7 @@ MonotoneTask::MonotoneTask(const Monoton options = other.options; output = other.output; abortTask = other.abortTask; + outputEncoding = other.outputEncoding; } MonotoneTask::MonotoneTask(const QStringList & args) @@ -66,6 +68,7 @@ void MonotoneTask::init(const ByteArrayL returnCode = -1; finished = false; abortTask = false; + outputEncoding = "UTF-8"; static bool initialized = false; if (!initialized) @@ -121,6 +124,23 @@ QByteArray MonotoneTask::getEncodedInput return commandLine; } +QString MonotoneTask::getDecodedOutput(const QString & enc) const +{ + QString encoding = enc; + if (encoding.isEmpty()) + { + encoding = outputEncoding; + } + I(!encoding.isEmpty()); + + // QTextCodec implements codecForName as factory and caches multiple + // requests, so we don't need to do that ourselves + QTextCodec * codec = QTextCodec::codecForName(encoding.toLatin1()); + I(codec); + return codec->toUnicode(output); +} + + const int MonotoneThread::StdioBufferSize = 50 * 1024 * 1024; MonotoneThread::MonotoneThread( ============================================================ --- src/monotone/MonotoneThread.h b00320bbdc44039934898127d9093016adec9f16 +++ src/monotone/MonotoneThread.h dd3abaa03d5a86ab9141e3da15c0e62766e016aa @@ -53,6 +53,9 @@ public: //! marks that the task has been finished void setFinished() { finished = true; } + //! set the output encoding + void setOutputEncoding(const QString & e) { outputEncoding = e; } + //! returns the command input stdio-encoded QByteArray getEncodedInput() const; @@ -65,8 +68,8 @@ public: //! returns the raw monotone output QByteArray getOutput() const { return output; } - //! returns the monotone output as utf-8 encoded string - QString getOutputUtf8() const { return QString::fromUtf8(output); } + //! converts the raw byte output into a unicode encoded QString + QString getDecodedOutput(const QString & enc = QString()) const; //! returns the command's return code int getReturnCode() const { return returnCode; } @@ -99,6 +102,7 @@ private: ByteArrayList arguments; ByteArrayList options; QByteArray output; + QString outputEncoding; }; class MonotoneThread : public QThread ============================================================ --- src/monotone/MonotoneUtil.cpp 2679a231bcdd46393acc15364976563938c05325 +++ src/monotone/MonotoneUtil.cpp c18789f0f92cceb714cca6b981e50ab2437ea9ff @@ -87,7 +87,7 @@ QString MonotoneUtil::getBaseWorkspaceRe MonotoneTask out = runSynchronousWorkspaceTask(workspace, in); if (!out.isFinished()) F("task aborted"); - QString data = out.getOutputUtf8(); + QString data = out.getDecodedOutput(); if (out.getReturnCode() > 0) { C(QString("Could not execute get_base_revision_id: %1").arg(data)); @@ -108,7 +108,7 @@ QString MonotoneUtil::getOption(const Wo MonotoneTask out = runSynchronousWorkspaceTask(workspace, in); if (!out.isFinished()) F("task aborted"); - QString data = out.getOutputUtf8(); + QString data = out.getDecodedOutput(); if (out.getReturnCode() > 0) { C(QString("Couldn't retrieve option %1: %2").arg(opt).arg(data)); @@ -169,7 +169,7 @@ QStringList MonotoneUtil::resolveSelecto MonotoneTask out = runSynchronousDatabaseTask(db, in); if (!out.isFinished()) F("task aborted"); - QString data = out.getOutputUtf8(); + QString data = out.getDecodedOutput(); if (out.getReturnCode() > 0) { C(QString("Couldn't resolve selector %1: %2").arg(selector).arg(data)); @@ -186,7 +186,7 @@ RevisionCerts MonotoneUtil::getRevisionC MonotoneTask out = runSynchronousDatabaseTask(db, in); if (!out.isFinished()) F("task aborted"); - QString data = out.getOutputUtf8(); + QString data = out.getDecodedOutput(); if (out.getReturnCode() > 0) { C(QString("Couldn't query revision certs for %1: %2").arg(revision).arg(data)); @@ -231,7 +231,7 @@ FileEntryList MonotoneUtil::getRevisionM MonotoneTask out = runSynchronousDatabaseTask(db, in); if (!out.isFinished()) F("task aborted"); - QString data = out.getOutputUtf8(); + QString data = out.getDecodedOutput(); if (out.getReturnCode() > 0) { C(QString("Couldn't query manifest entries for %1: %2").arg(revision).arg(data)); @@ -294,7 +294,7 @@ QStringList MonotoneUtil::getPrivateKeyL MonotoneTask out = runSynchronousDatabaseTask(db, in); if (!out.isFinished()) F("task aborted"); - QString data = out.getOutputUtf8(); + QString data = out.getDecodedOutput(); if (out.getReturnCode() > 0) { C(QString("Couldn't query keys: %1").arg(data)); @@ -338,7 +338,7 @@ QString MonotoneUtil::getFileId(const Da MonotoneTask out = runSynchronousDatabaseTask(db, in); if (!out.isFinished()) F("task aborted"); - QString data = out.getOutputUtf8(); + QString data = out.getDecodedOutput(); if (out.getReturnCode() > 0) { C(QString("Couldn't identify path: %1").arg(data)); @@ -349,6 +349,56 @@ QString MonotoneUtil::getFileId(const Da return data; } +bool MonotoneUtil::getAttribute(const WorkspacePath & ws, const QString & path, + const QString & attrname, QPair & attrval) +{ + MonotoneTask in(QStringList() << "get_attributes" << path); + MonotoneTask out = runSynchronousWorkspaceTask(ws, in); + if (!out.isFinished()) F("task aborted"); + + QString data = out.getDecodedOutput(); + if (out.getReturnCode() > 0) + { + C(QString("Couldn't run get_attributes: %1").arg(data)); + return false; + } + + BasicIOParser parser(data); + I(parser.parse()); + StanzaList stanzas = parser.getStanzas(); + + foreach (Stanza st, stanzas) + { + if (st.at(0).sym != "attr") continue; + if (st.at(0).vals.at(0) == attrname) + { + attrval.first = st.at(0).vals.at(1); + attrval.second = st.at(1).vals.at(0); + return true; + } + } + + return false; +} + +bool MonotoneUtil::getAttribute(const DatabaseFile & db, const QString & revision, + const QString & path, const QString & attrname, QString & attrval) +{ + FileEntryList entryList = getRevisionManifest(db, revision); + + foreach (FileEntry en, entryList) + { + if (en.path != path) continue; + if (en.attrs.contains(attrname)) + { + attrval = en.attrs.value(attrname); + return true; + } + } + + return false; +} + /*! Strips mtn: warning: and alike from error strings coming from mtn and unwraps them, if this fails we add the original string unwrapped ============================================================ --- src/monotone/MonotoneUtil.h 7173990600fd78e4e75a5bc0111d2c29e2a06ed7 +++ src/monotone/MonotoneUtil.h 2859545454f0987d0bb5141a06a1f34667a9dc51 @@ -40,6 +40,8 @@ public: static FileEntryList getRevisionManifest(const DatabaseFile &, const QString &); static QStringList getPrivateKeyList(const DatabaseFile &); static QString getFileId(const DatabaseFile &, const QString &); + static bool getAttribute(const WorkspacePath &, const QString &, const QString &, QPair &); + static bool getAttribute(const DatabaseFile &, const QString &, const QString &, const QString &, QString &); // FIXME: decide what to do with that static QString stripMtnPrefix(const QString &); ============================================================ --- src/monotone/WorkspaceCommitter.cpp d2a5ce169d18495c84ed56ecf92732b2c6646812 +++ src/monotone/WorkspaceCommitter.cpp 3efc567b8ef22e48f152f8b728a3b168ea0f3d0d @@ -84,7 +84,7 @@ bool WorkspaceCommitter::run(const QStri ); if (!out.isFinished()) F("task aborted"); - QString data = out.getOutputUtf8(); + QString data = out.getDecodedOutput(); if (out.getReturnCode() > 0) { C(QString("Couldn't commit revision: %1").arg(data)); @@ -214,7 +214,7 @@ bool WorkspaceCommitter::putFile(const Q MonotoneTask out = MonotoneUtil::runSynchronousWorkspaceTask(workspacePath, in); if (!out.isFinished()) F("task aborted"); - QString data = out.getOutputUtf8(); + QString data = out.getDecodedOutput(); if (out.getReturnCode() > 0) { C(QString("Cannot execute put_file: %1").arg(data)); @@ -248,7 +248,7 @@ bool WorkspaceCommitter::putCert(const Q ); if (!out.isFinished()) F("task aborted"); - QString data = out.getOutputUtf8(); + QString data = out.getDecodedOutput(); if (out.getReturnCode() > 0) { C(QString("Couldn't attach cert %1: %2").arg(key).arg(data)); ============================================================ --- src/view/TreeView.cpp dcf826574e06d65e22db5a586caeb918673c00d0 +++ src/view/TreeView.cpp bf98bd50c56ec9d93411832cd4e3c5d5f08184b8 @@ -21,8 +21,12 @@ #include "vocab.h" #include +#include -TreeView::TreeView(QWidget * parent) : QTreeView(parent) {} +TreeView::TreeView(QWidget * parent) : QTreeView(parent) +{ + init(); +} TreeView::TreeView(QWidget * parent, const QString & objName) : QTreeView(parent) { @@ -43,7 +47,26 @@ void TreeView::setModel(QAbstractItemMod void TreeView::setModel(QAbstractItemModel * model) { + QItemSelectionModel * oldSelectionModel = selectionModel(); + if (oldSelectionModel != 0) + { + disconnect( + oldSelectionModel, SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), + this, SLOT(itemSelectionChanged(const QItemSelection &, const QItemSelection &)) + ); + } + + // each time a new model is set, a new selection model is internally + // created by Qt, so we need to re-connect to this new model to get + // notified about selection changes QTreeView::setModel(model); + QItemSelectionModel * newSelectionModel = selectionModel(); + + connect( + newSelectionModel, SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), + this, SLOT(itemSelectionChanged(const QItemSelection &, const QItemSelection &)) + ); + Settings::restoreHeaderViewState(header(), QString(objectName()).append("_header")); stateLoaded = true; } @@ -104,3 +127,24 @@ void TreeView::contextMenuEvent(QContext emit contextMenuRequested(currentSelection, mapToGlobal(pos)); } +void TreeView::itemSelectionChanged(const QItemSelection & selected, const QItemSelection & deselected) +{ + // we're not interested in these + Q_UNUSED(deselected); + + QModelIndexList selectedIndexes = selected.indexes(); + + for (int i=0; i 0) + { + selectedIndexes.removeAt(i); + continue; + } + i++; + } + + emit selectedRows(selectedIndexes); +} + ============================================================ --- src/view/TreeView.h 7b872f64c09770fed7e6cf2dd321b6a798a05f90 +++ src/view/TreeView.h 651169fd52a772a8c03d883a3d75c35f52cb9524 @@ -37,11 +37,15 @@ signals: signals: void contextMenuRequested(const QModelIndexList &, const QPoint &); + void selectedRows(const QModelIndexList &); private: void saveHeaderViewState(); bool stateLoaded; void contextMenuEvent(QContextMenuEvent *); + +private slots: + void itemSelectionChanged(const QItemSelection &, const QItemSelection &); }; #endif ============================================================ --- src/view/WorkspaceWindow.cpp 9ae0a5ea1f3095ecc8824221b1d1523956942144 +++ src/view/WorkspaceWindow.cpp ade47c546f628f21ee7457e923e57d4a910370c4 @@ -114,12 +114,12 @@ void WorkspaceWindow::setup() attrView->setAlternatingRowColors(true); connect( - treeView, SIGNAL(clicked(const QModelIndex &)), - this, SLOT(readAttributes(const QModelIndex &)) + treeView, SIGNAL(selectedRows(const QModelIndexList &)), + this, SLOT(maybeReadAttributes(const QModelIndexList &)) ); connect( - listView, SIGNAL(clicked(const QModelIndex &)), - this, SLOT(readAttributes(const QModelIndex &)) + listView, SIGNAL(selectedRows(const QModelIndexList &)), + this, SLOT(maybeReadAttributes(const QModelIndexList &)) ); connect( @@ -301,10 +301,17 @@ void WorkspaceWindow::openFile(const QSt Platform::openFile(workspacePath + "/" + filePath); } -void WorkspaceWindow::readAttributes(const QModelIndex & index) +void WorkspaceWindow::maybeReadAttributes(const QModelIndexList & indexes) { + if (indexes.size() != 1) + { + D("multiple or no items selected"); + attrModel->revert(); + return; + } + QModelIndex sourceIndex = static_cast - (index.model())->mapToSource(index); + (indexes.at(0).model())->mapToSource(indexes.at(0)); ModelItem * item = static_cast (sourceIndex.internalPointer()); InventoryItem * invitem = dynamic_cast @@ -312,14 +319,14 @@ void WorkspaceWindow::readAttributes(con if (!invitem) { - D("item is not inventory item"); + D("selected item is not inventory item"); attrModel->revert(); return; } if (invitem->isOldNode() || !invitem->isTracked()) { - D("item is old node or not tracked"); + D("selected item is old node or not tracked"); attrModel->revert(); return; } ============================================================ --- src/view/WorkspaceWindow.h 3c181a5026143baf9e58dc86a97cf4ee5469356b +++ src/view/WorkspaceWindow.h a463caf3a560c81ada20f1cd3eae8677644018b8 @@ -63,7 +63,7 @@ private slots: InventoryWatcher * invWatcher; private slots: - void readAttributes(const QModelIndex &); + void maybeReadAttributes(const QModelIndexList &); void invalidWorkspaceFormat(const QString &); void openFile(const QString &); }; ============================================================ --- src/view/dialogs/FileDiff.cpp 699fafa30f746354fb9218fc18bc135e01e21e02 +++ src/view/dialogs/FileDiff.cpp f9e9afd79cc644e82dc4987ca845ae2e37dcc473 @@ -23,6 +23,7 @@ #include #include #include +#include FileDiff::FileDiff(QWidget * parent) : Dialog(parent), diffModel(0), fileModel(0), fileProxyModel(0) @@ -64,27 +65,8 @@ FileDiff::FileDiff(QWidget * parent) scrollToPrev, SIGNAL(clicked()), this, SLOT(getPrevGroup()) ); -} -void FileDiff::forDatabase(const DatabaseFile & db, const QString & file, - const QString & base, const QString & target) -{ - fileName = file; - loaded = false; - - QString title = windowTitle(); - setWindowTitle(title.arg(fileName)); - - firstRevision->setText( - firstRevision->text().arg(base.left(12).append("...")) - ); - - secondRevision->setText( - secondRevision->text().arg(target.left(12).append("...")) - ); - - fileModel = new GetFile(this, db); - + fileModel = new GetFile(this); fileProxyModel = new GetFileProxyModel(this); fileProxyModel->setSourceModel(fileModel); @@ -103,19 +85,81 @@ void FileDiff::forDatabase(const Databas this, SLOT(applyDiff()) ); - fileModel->readFileByName(fileName, base); - diffModel->readDatabaseDiff(db, fileName, base, target); + ByteArrayList codecNamesAndAliases = QTextCodec::availableCodecs(); + QSet codecNameSet; + + // eliminate aliases + foreach (QByteArray codec, codecNamesAndAliases) + { + codecNameSet.insert(QTextCodec::codecForName(codec)->name()); + } + + ByteArrayList codecNames = codecNameSet.toList(); + qSort(codecNames); + + foreach (QByteArray codec, codecNames) + { + //! FIXME: better ordering, i.e. popular ones on top + textEncoding->addItem(QString(codec)); + } + + connect( + textEncoding, SIGNAL(currentIndexChanged(const QString &)), + this, SLOT(switchOutputEncoding(const QString &)) + ); } +void FileDiff::forDatabase(const DatabaseFile & db, const QString & file, + const QString & base, const QString & target) +{ + fileName = file; + loaded = false; + workspace = QString(); + + QString encoding = "UTF-8"; + if (!MonotoneUtil::getAttribute(db, base, file, "guitone:file-encoding", + encoding)) + { + L(QString("could not query file encoding of %1, using default").arg(file)); + } + textEncoding->setCurrentIndex(textEncoding->findText(encoding)); + + setWindowTitle(tr("File differences of \"%1\"").arg(fileName)); + + firstRevision->setText( + tr("only left (%1)").arg(base.left(12).append("...")) + ); + + secondRevision->setText( + tr("only right (%1)").arg(target.left(12).append("...")) + ); + + fileModel->readFileByName(db, fileName, base, encoding); + diffModel->readDatabaseDiff(db, fileName, base, target, encoding); +} + void FileDiff::forWorkspace(const WorkspacePath & ws, const QString & file, const QString & base, const QString & target) { fileName = file; loaded = false; + workspace = ws; - QString title = windowTitle(); - setWindowTitle(title.arg(fileName)); + QString encoding = "UTF-8"; + QPair attrval; + if (!MonotoneUtil::getAttribute(ws, file, "guitone:file-encoding", attrval) || + attrval.second == "dropped") + { + L(QString("could not query file encoding of %1, using default").arg(file)); + } + else + { + encoding = attrval.first; + } + textEncoding->setCurrentIndex(textEncoding->findText(encoding)); + setWindowTitle(tr("File differences of \"%1\"").arg(fileName)); + QString left = tr("workspace parent"); QString right = tr("workspace revision"); @@ -129,46 +173,27 @@ void FileDiff::forWorkspace(const Worksp right = target.left(12).append("..."); } - firstRevision->setText(left); - secondRevision->setText(right); + firstRevision->setText(tr("only left (%1)").arg(left)); + secondRevision->setText(tr("only right (%1)").arg(right)); - fileModel = new GetFile(this, MonotoneUtil::getDatabaseFile(ws)); - - fileProxyModel = new GetFileProxyModel(this); - fileProxyModel->setSourceModel(fileModel); - - diffModel = new ContentDiff(this); - - diffView->setModel(fileProxyModel); - diffStatusView->setModel(fileProxyModel); - - connect( - fileModel, SIGNAL(fileRead()), - this, SLOT(applyDiff()) - ); - - connect( - diffModel, SIGNAL(diffRead()), - this, SLOT(applyDiff()) - ); - + DatabaseFile db = MonotoneUtil::getDatabaseFile(ws); if (base.isEmpty()) { QString workspaceParent = MonotoneUtil::getBaseWorkspaceRevision(ws); - fileModel->readFileByName(fileName, workspaceParent); - diffModel->readWorkspaceDiff(ws, fileName, workspaceParent, target); + fileModel->readFileByName(db, fileName, workspaceParent, encoding); + diffModel->readWorkspaceDiff(ws, fileName, workspaceParent, target, encoding); return; } - fileModel->readFileByName(fileName, base); - diffModel->readWorkspaceDiff(ws, fileName, base, target); + fileModel->readFileByName(db, fileName, base, encoding); + diffModel->readWorkspaceDiff(ws, fileName, base, target, encoding); } FileDiff::~FileDiff() { - if (fileModel) delete fileModel; + if (fileModel) delete fileModel; if (fileProxyModel) delete fileProxyModel; - if (diffModel) delete diffModel; + if (diffModel) delete diffModel; } void FileDiff::applyDiff() @@ -279,3 +304,31 @@ void FileDiff::changeCurrentIndex(int po } } +void FileDiff::switchOutputEncoding(const QString & encoding) +{ + // if we're not yet fully loaded the models, don't do anything + if (!loaded) return; + + fileModel->switchOutputEncoding(encoding); + diffModel->switchOutputEncoding(encoding); + + // the model data are converted instantly, so we don't have + // to wait for signals this time... + applyDiff(); + + if (!workspace.isEmpty()) + { + MonotoneTask in(QStringList() << "set_attribute" + << fileName + << "guitone:file-encoding" + << encoding); + MonotoneTask out = MonotoneUtil::runSynchronousWorkspaceTask(workspace, in); + + if (out.getReturnCode() != 0) + { + W(QString("couldn't set guitone:file-encoding on %1: %2") + .arg(fileName).arg(out.getDecodedOutput())); + } + } +} + ============================================================ --- src/view/dialogs/FileDiff.h 14c54d84f6d8f41b74cb461a378d942ec0db690f +++ src/view/dialogs/FileDiff.h 752ff0409384d3e4220754274ea638604e4a278d @@ -42,6 +42,7 @@ private slots: void getPrevGroup(); void getNextGroup(); void changeCurrentIndex(int); + void switchOutputEncoding(const QString &); private: ContentDiff * diffModel; @@ -49,6 +50,7 @@ private: GetFileProxyModel * fileProxyModel; QModelIndex currentIndex; QString fileName; + WorkspacePath workspace; bool loaded; }; ============================================================ --- src/view/dialogs/GenerateKeypair.cpp 6ed24f6d1bcee46cd89605ecbe0c3c5a6b17475c +++ src/view/dialogs/GenerateKeypair.cpp 17c560ed391524a64ae4f86642641999a1eb99d7 @@ -72,7 +72,7 @@ void GenerateKeypair::accept() this, tr("Error creating keypair"), tr("There was an error creating the keypair:\n%1") - .arg(out.getOutputUtf8()), + .arg(out.getDecodedOutput()), QMessageBox::Ok, 0, 0 ); return; ============================================================ --- src/view/dialogs/Preferences.cpp 1e3bc13f16b3a04e84f5fc8ce2add567a35db359 +++ src/view/dialogs/Preferences.cpp 211cc5978c8f41c19d41165fe4e56951043989cf @@ -100,6 +100,11 @@ void Preferences::init() Qt::Checked : Qt::Unchecked ); + saveEncodingAsFileAttribute->setCheckState( + Settings::getBool("SaveEncodingAsFileAttribute", true) ? + Qt::Checked : Qt::Unchecked + ); + colorPickerAddedLines->setSelectedColor( QColor(Settings::getString("DiffColorAddedLines", "green")) ); @@ -180,6 +185,7 @@ void Preferences::accept() Settings::setLogLevel(level); Settings::setBool("CheckForUpdates", checkForUpdates->isChecked()); + Settings::setBool("SaveEncodingAsFileAttribute", saveEncodingAsFileAttribute->isChecked()); Settings::setBool("RelaxedVersionCheck", relaxedVersionCheck->isChecked()); Settings::setBool("AskFileOpen", askFileOpen->isChecked()); Settings::setString("AskFileOpenExt", askFileOpenExtensions->text()); ============================================================ --- src/view/dialogs/RevisionDiff.cpp c03a05117e11c00a098e0fd55b09385dc7cb205c +++ src/view/dialogs/RevisionDiff.cpp 24e17c91450da9413c36beea817d744f313c2341 @@ -31,24 +31,12 @@ RevisionDiff::RevisionDiff(QWidget * par diffView->setIndentation(10); connect( - diffView, SIGNAL(clicked(const QModelIndex &)), - this, SLOT(enableDisableFileDiff(const QModelIndex &)) - ); - - connect( diffView, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(triggerFileDiff(const QModelIndex &)) ); - connect( - showFileDiff, SIGNAL(clicked()), - this, SLOT(triggerFileDiff()) - ); - diffModel = new ContentDiff(this); diffView->setModel(diffModel); - - showFileDiff->setEnabled(false); } RevisionDiff::~RevisionDiff() @@ -108,33 +96,12 @@ void RevisionDiff::forWorkspace(const Wo diffModel->readWorkspaceDiff(ws, filePath, base, target); } -void RevisionDiff::enableDisableFileDiff(const QModelIndex & index) -{ - ListLine * line = static_cast(index.internalPointer()); - if (line->isFileLineAndDiffable()) - { - fileName = line->fileName; - showFileDiff->setEnabled(true); - } - else - { - showFileDiff->setEnabled(false); - } -} - void RevisionDiff::triggerFileDiff(const QModelIndex & index) { ListLine * line = static_cast(index.internalPointer()); if (line->isFileLineAndDiffable()) { - fileName = line->fileName; - triggerFileDiff(); + emit fileDiff(line->fileName, baseRevision, targetRevision); } } -void RevisionDiff::triggerFileDiff() -{ - I(!fileName.isEmpty()); - emit fileDiff(fileName, baseRevision, targetRevision); -} - ============================================================ --- src/view/dialogs/RevisionDiff.h e536991efd9001e62a385f86c7130ea26b2cbee7 +++ src/view/dialogs/RevisionDiff.h bce21301eb5596f48b36be6f32947ec6b5cfea71 @@ -38,14 +38,11 @@ private: private: ContentDiff * diffModel; - QString fileName; QString baseRevision; QString targetRevision; private slots: - void enableDisableFileDiff(const QModelIndex &); void triggerFileDiff(const QModelIndex &); - void triggerFileDiff(); }; #endif ============================================================ --- src/view/dialogs/RevisionManifest.cpp 6316625d53d8349258ae940ddc1f5c8d5dc641ec +++ src/view/dialogs/RevisionManifest.cpp f329b383c8b9aced2fe93ecda59f704f53f17427 @@ -86,7 +86,7 @@ void RevisionManifest::openFile(const QM if (out.getReturnCode() > 0) { - C(QString("Cannot execute get_file: %1").arg(out.getOutputUtf8())); + C(QString("Cannot execute get_file: %1").arg(out.getDecodedOutput())); return; }